diff -u --recursive --new-file v2.1.39/linux/Documentation/Configure.help linux/Documentation/Configure.help --- v2.1.39/linux/Documentation/Configure.help Tue May 13 22:40:59 1997 +++ linux/Documentation/Configure.help Sun May 18 16:38:34 1997 @@ -4650,6 +4650,40 @@ also controls the radio tuner on this card, however this is not yet supported in this software. +Gallant's Audio Excel DSP 16 support (SC-6000 and SC-6600) +CONFIG_AEDSP16 + Answer Y if you have a Gallant's Audio Excel DSP 16 card. This card + emulate an SBPro or a Microsoft Sound System card. + You must select one of the Sound Blaster or Microsoft Sound System + drivers before select this menu item. + Read the drivers/sound/lowlevel/README.aedsp16 file and the head of + drivers/sound/lowlevel/aedsp16.c to have more informations about + this driver and its configuration. + + If you are changing the card configuration, please, undefine all + the old Audio Excel parameters because leaving it defined while + selecting the alternate emulation, may screw up your .config file. + + !!!NOTE!!! + The driver supports Audio Excel DSP 16 but not the III version of + this card. Read drivers/sound/lowlevel/Readme.aedsp16 if you want + to know something more on how to use the III version with this sound + driver. + +SC-6600 based audio cards (new Audio Excel DSP 16) +CONFIG_SC6600 + The SC6600 is the new version of DSP mounted on the Audio Excel DSP 16 + cards. Check the FCC ID of your audio card and answer Y if you have an + SC6600 DSP. + +Audio Excel DSP 16 (MSS emulation) +CONFIG_AEDSP16_MSS + Answer Y if you want your audio card emulate Microsoft Sound System. + +Audio Excel DSP 16 (SBPro emulation) +CONFIG_AEDSP16_SBPRO + Answer Y if you want your audio card emulate Sound Blaster Pro. + Kernel profiling support CONFIG_PROFILE This is for kernel hackers who want to know how much time the kernel diff -u --recursive --new-file v2.1.39/linux/Documentation/devices.tex linux/Documentation/devices.tex --- v2.1.39/linux/Documentation/devices.tex Tue May 13 22:40:59 1997 +++ linux/Documentation/devices.tex Tue May 20 15:00:01 1997 @@ -47,7 +47,7 @@ % \title{{\bf Linux Allocated Devices}} \author{Maintained by H. Peter Anvin $<$hpa@zytor.com$>$} -\date{Last revised: May 1, 1997} +\date{Last revised: May 20, 1997} \maketitle % \noindent @@ -212,7 +212,8 @@ \major{81}{}{char }{Brooktree Bt848 frame grabbers} \major{82}{}{char }{WiNRADiO communications receiver card} \major{83}{}{char }{Teletext/videotext interfaces} -\major{84}{--119}{}{Unallocated} +\major{84}{}{char }{Ikon 1011[57] Versatec Greensheet Interface} +\major{85}{--119}{}{Unallocated} \major{120}{--127}{}{Local/experimental use} \major{128}{--239}{}{Unallocated} \major{240}{--254}{}{Local/experimental use} @@ -492,6 +493,7 @@ \minor{6}{/dev/sunmouse}{Sun mouse} \minor{7}{/dev/amigamouse1}{Second Amiga mouse} \minor{8}{/dev/smouse}{Simple serial mouse driver} + \minor{9}{/dev/pc110pad}{IBM PC-110 digitizer pad} \minor{128}{/dev/beep}{Fancy beep device} \minor{129}{/dev/modreq}{Kernel module load request} \minor{130}{/dev/watchdog}{Watchdog timer port} @@ -1418,7 +1420,13 @@ on {\url http://home.pages.de/~videotext/\/}. \begin{devicelist} -\major{84}{--119}{}{Unallocated} +\major{84}{}{char }{Ikon 1011[57] Versatec Greensheet Interface} + \minor{0}{/dev/ihcp0}{First Greensheet port} + \minor{1}{/dev/ihcp1}{Second Greensheet port} +\end{devicelist} + +\begin{devicelist} +\major{85}{--119}{}{Unallocated} \end{devicelist} \begin{devicelist} diff -u --recursive --new-file v2.1.39/linux/Documentation/devices.txt linux/Documentation/devices.txt --- v2.1.39/linux/Documentation/devices.txt Tue May 13 22:40:59 1997 +++ linux/Documentation/devices.txt Tue May 20 15:00:01 1997 @@ -1,7 +1,7 @@ LINUX ALLOCATED DEVICES Maintained by H. Peter Anvin - Last revised: May 1, 1997 + Last revised: May 20, 1997 This list is the successor to Rick Miller's Linux Device List, which he stopped maintaining when he got busy with other things in 1993. It @@ -273,6 +273,7 @@ 6 = /dev/sunmouse Sun mouse 7 = /dev/amigamouse1 Second Amiga mouse 8 = /dev/smouse Simple serial mouse driver + 9 = /dev/pc110pad IBM PC-110 digitizer pad 128 = /dev/beep Fancy beep device 129 = /dev/modreq Kernel module load request 130 = /dev/watchdog Watchdog timer port @@ -997,7 +998,11 @@ Devices for the driver contained in the VideoteXt package. More information on http://home.pages.de/~videotext/ - 84-119 UNALLOCATED + 84 char Ikon 1011[57] Versatec Greensheet Interface + 0 = /dev/ihcp0 First Greensheet port + 1 = /dev/ihcp1 Second Greensheet port + + 85-119 UNALLOCATED 120-127 LOCAL/EXPERIMENTAL USE diff -u --recursive --new-file v2.1.39/linux/MAINTAINERS linux/MAINTAINERS --- v2.1.39/linux/MAINTAINERS Tue May 13 22:41:00 1997 +++ linux/MAINTAINERS Sun May 18 17:10:37 1997 @@ -234,6 +234,13 @@ L: isdn4linux@hub-wue.franken.de S: Maintained +M68K: +P: Jes Sorensen +M: Jes.Sorensen@cern.ch +W: http://www.clark.net/pub/lawrencc/linux/index.html +L: linux-m68k@phil.uni-sb.de +S: Maintained + MODULE SUPPORT [GENERAL], KERNELD P: Bjorn Ekwall M: bj0rn@blox.se diff -u --recursive --new-file v2.1.39/linux/Makefile linux/Makefile --- v2.1.39/linux/Makefile Mon May 19 12:57:38 1997 +++ linux/Makefile Wed May 21 18:50:15 1997 @@ -1,8 +1,8 @@ VERSION = 2 PATCHLEVEL = 1 -SUBLEVEL = 39 +SUBLEVEL = 40 -ARCH := $(shell uname -m | sed s/i.86/i386/) +ARCH := $(shell uname -m | sed s/i.86/i386/ | sed s/sun4u/sparc64/) # # For SMP kernels, set this. We don't want to have this in the config file diff -u --recursive --new-file v2.1.39/linux/arch/alpha/kernel/ptrace.c linux/arch/alpha/kernel/ptrace.c --- v2.1.39/linux/arch/alpha/kernel/ptrace.c Tue May 13 22:41:00 1997 +++ linux/arch/alpha/kernel/ptrace.c Sun May 18 18:05:13 1997 @@ -161,7 +161,7 @@ repeat: pgdir = pgd_offset(vma->vm_mm, addr); if (pgd_none(*pgdir)) { - do_no_page(tsk, vma, addr, 0); + handle_mm_fault(tsk, vma, addr, 0); goto repeat; } if (pgd_bad(*pgdir)) { @@ -171,7 +171,7 @@ } pgmiddle = pmd_offset(pgdir, addr); if (pmd_none(*pgmiddle)) { - do_no_page(tsk, vma, addr, 0); + handle_mm_fault(tsk, vma, addr, 0); goto repeat; } if (pmd_bad(*pgmiddle)) { @@ -181,7 +181,7 @@ } pgtable = pte_offset(pgmiddle, addr); if (!pte_present(*pgtable)) { - do_no_page(tsk, vma, addr, 0); + handle_mm_fault(tsk, vma, addr, 0); goto repeat; } page = pte_page(*pgtable); @@ -212,7 +212,7 @@ repeat: pgdir = pgd_offset(vma->vm_mm, addr); if (!pgd_present(*pgdir)) { - do_no_page(tsk, vma, addr, 1); + handle_mm_fault(tsk, vma, addr, 1); goto repeat; } if (pgd_bad(*pgdir)) { @@ -222,7 +222,7 @@ } pgmiddle = pmd_offset(pgdir, addr); if (pmd_none(*pgmiddle)) { - do_no_page(tsk, vma, addr, 1); + handle_mm_fault(tsk, vma, addr, 1); goto repeat; } if (pmd_bad(*pgmiddle)) { @@ -232,12 +232,12 @@ } pgtable = pte_offset(pgmiddle, addr); if (!pte_present(*pgtable)) { - do_no_page(tsk, vma, addr, 1); + handle_mm_fault(tsk, vma, addr, 1); goto repeat; } page = pte_page(*pgtable); if (!pte_write(*pgtable)) { - do_wp_page(tsk, vma, addr, 1); + handle_mm_fault(tsk, vma, addr, 1); goto repeat; } /* this is a hack for non-kernel-mapped video buffers and similar */ diff -u --recursive --new-file v2.1.39/linux/arch/i386/kernel/i386_ksyms.c linux/arch/i386/kernel/i386_ksyms.c --- v2.1.39/linux/arch/i386/kernel/i386_ksyms.c Tue May 13 22:41:01 1997 +++ linux/arch/i386/kernel/i386_ksyms.c Sun May 18 17:10:37 1997 @@ -19,6 +19,11 @@ extern int dump_fpu(elf_fpregset_t *); extern void __lock_kernel(void); +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_HD) || defined(CONFIG_BLK_DEV_IDE_MODULE) || defined(CONFIG_BLK_DEV_HD_MODULE) +extern struct drive_info_struct drive_info; +EXPORT_SYMBOL(drive_info); +#endif + /* platform dependent support */ EXPORT_SYMBOL(EISA_bus); EXPORT_SYMBOL(MCA_bus); diff -u --recursive --new-file v2.1.39/linux/arch/i386/lib/locks.S linux/arch/i386/lib/locks.S --- v2.1.39/linux/arch/i386/lib/locks.S Tue May 13 22:41:01 1997 +++ linux/arch/i386/lib/locks.S Tue May 20 09:23:15 1997 @@ -14,6 +14,7 @@ lock btsl $0, SYMBOL_NAME(kernel_flag) jnc 3f + sti 2: btl %dl, SYMBOL_NAME(smp_invalidate_needed) jnc 0f @@ -27,6 +28,7 @@ 0: btl $0, SYMBOL_NAME(kernel_flag) jc 2b + cli jmp 1b 3: diff -u --recursive --new-file v2.1.39/linux/arch/m68k/Makefile linux/arch/m68k/Makefile --- v2.1.39/linux/arch/m68k/Makefile Tue May 13 22:41:01 1997 +++ linux/arch/m68k/Makefile Sun May 18 17:10:37 1997 @@ -26,7 +26,7 @@ LINKFLAGS = -T $(TOPDIR)/arch/m68k/vmlinux.lds -CFLAGS := $(CFLAGS) -pipe -fno-strength-reduce +CFLAGS += -pipe -fno-strength-reduce -ffixed-a2 ifdef CONFIG_OPTIMIZE_040 CFLAGS := $(CFLAGS) -m68040 diff -u --recursive --new-file v2.1.39/linux/arch/m68k/amiga/retz3fb.c linux/arch/m68k/amiga/retz3fb.c --- v2.1.39/linux/arch/m68k/amiga/retz3fb.c Tue May 13 22:41:02 1997 +++ linux/arch/m68k/amiga/retz3fb.c Sun May 18 17:10:37 1997 @@ -32,6 +32,7 @@ #include #include #include + #include #include #include diff -u --recursive --new-file v2.1.39/linux/arch/m68k/atari/ataints.c linux/arch/m68k/atari/ataints.c --- v2.1.39/linux/arch/m68k/atari/ataints.c Tue May 13 22:41:02 1997 +++ linux/arch/m68k/atari/ataints.c Sun May 18 17:10:37 1997 @@ -163,15 +163,19 @@ #define MFP_MK_BASE "0xfa13" -/* This must agree with head.S. */ -#define ORIG_DO "0x20" -#define FORMATVEC "0x2E" -#define SR "0x28" +/* This must agree with entry.S. */ +#define ORIG_DO "0x24" +#define FORMATVEC "0x32" +#define SR "0x2C" #define SAVE_ALL \ "clrl %%sp@-;" /* stk_adj */ \ "pea -1:w;" /* orig d0 = -1 */ \ "movel %%d0,%%sp@-;" /* d0 */ \ - "moveml %%d1-%%d5/%%a0-%%a1,%%sp@-" + "moveml %%d1-%%d5/%%a0-%%a2,%%sp@-" +#define GET_CURRENT(tmp) \ + "movel %%sp,"#tmp";" \ + "andw #-8192,"#tmp";" \ + "movel "#tmp",%%a2" #define BUILD_SLOW_IRQ(n) \ asmlinkage void IRQ_NAME(n); \ @@ -181,6 +185,7 @@ SYMBOL_NAME_STR(atari_slow_irq_) #n "_handler:\t" \ " addql #1,"SYMBOL_NAME_STR(local_irq_count)"\n" \ SAVE_ALL "\n" \ + GET_CURRENT(%%d0) "\n" \ " andb #~(1<<(" #n "&7))," /* mask this interrupt */ \ "("MFP_MK_BASE"+(((" #n "&8)^8)>>2)+((" #n "&16)<<3)):w\n" \ " bfextu %%sp@("SR"){#5,#3},%%d0\n" /* get old IPL from stack frame */ \ @@ -283,7 +288,8 @@ orw #0x700,%%sr /* disable all interrupts */ "SYMBOL_NAME_STR(atari_prio_irq_handler) ":\t addql #1,"SYMBOL_NAME_STR(local_irq_count)"\n" - SAVE_ALL " + SAVE_ALL "\n" + GET_CURRENT(%%d0) " /* get vector number from stack frame and convert to source */ bfextu %%sp@(" FORMATVEC "){#4,#10},%%d0 subw #(0x40-8),%%d0 diff -u --recursive --new-file v2.1.39/linux/arch/m68k/fpsp040/skeleton.S linux/arch/m68k/fpsp040/skeleton.S --- v2.1.39/linux/arch/m68k/fpsp040/skeleton.S Wed Apr 23 19:01:15 1997 +++ linux/arch/m68k/fpsp040/skeleton.S Sun May 18 17:10:37 1997 @@ -51,13 +51,23 @@ .include "fpsp.h" -LOFF_ORIG_D0 = 0x20 +/* + * This has to match entry.S + */ +LOFF_ORIG_D0 = 0x24 + +#define curptr a2 #define SAVE_ALL \ clrl %sp@-; /* stk_adj */ \ movel %d0,%sp@-; /* orig d0 */ \ movel %d0,%sp@-; /* d0 */ \ - moveml %d1-%d5/%a0-%a1,%sp@- + moveml %d1-%d5/%a0-%a1/%curptr,%sp@-; + +#define GET_CURRENT(tmp) \ + movel %sp,tmp; \ + andw &-8192,tmp; \ + movel tmp,%curptr; |xref b1238_fix @@ -81,6 +91,7 @@ movel %d0,%sp@(LOFF_ORIG_D0) | a -1 in the ORIG_D0 field | signifies that the stack frame | is NOT for syscall + GET_CURRENT(%d0) movel %sp,%sp@- | stack frame pointer argument bsrl SYMBOL_NAME(trap_c) addql #4,%sp @@ -175,6 +186,7 @@ movel %d0,%sp@(LOFF_ORIG_D0) | a -1 in the ORIG_D0 field | signifies that the stack frame | is NOT for syscall + GET_CURRENT(%d0) movel %sp,%sp@- | stack frame pointer argument bsrl SYMBOL_NAME(trap_c) addql #4,%sp @@ -204,6 +216,7 @@ movel %d0,%sp@(LOFF_ORIG_D0) | a -1 in the ORIG_D0 field | signifies that the stack frame | is NOT for syscall + GET_CURRENT(%d0) movel %sp,%sp@- | stack frame pointer argument bsrl SYMBOL_NAME(trap_c) addql #4,%sp @@ -233,6 +246,7 @@ movel %d0,%sp@(LOFF_ORIG_D0) | a -1 in the ORIG_D0 field | signifies that the stack frame | is NOT for syscall + GET_CURRENT(%d0) movel %sp,%sp@- | stack frame pointer argument bsrl SYMBOL_NAME(trap_c) addql #4,%sp @@ -258,6 +272,7 @@ movel %d0,%sp@(LOFF_ORIG_D0) | a -1 in the ORIG_D0 field | signifies that the stack frame | is NOT for syscall + GET_CURRENT(%d0) movel %sp,%sp@- | stack frame pointer argument bsrl SYMBOL_NAME(trap_c) addql #4,%sp @@ -283,6 +298,7 @@ movel %d0,%sp@(LOFF_ORIG_D0) | a -1 in the ORIG_D0 field | signifies that the stack frame | is NOT for syscall + GET_CURRENT(%d0) movel %sp,%sp@- | stack frame pointer argument bsrl SYMBOL_NAME(trap_c) addql #4,%sp @@ -314,6 +330,7 @@ movel %d0,%sp@(LOFF_ORIG_D0) | a -1 in the ORIG_D0 field | signifies that the stack frame | is NOT for syscall + GET_CURRENT(%d0) movel %sp,%sp@- | stack frame pointer argument bsrl SYMBOL_NAME(trap_c) addql #4,%sp @@ -338,6 +355,7 @@ movel %d0,%sp@(LOFF_ORIG_D0) | a -1 in the ORIG_D0 field | signifies that the stack frame | is NOT for syscall + GET_CURRENT(%d0) movel %sp,%sp@- | stack frame pointer argument bsrl SYMBOL_NAME(trap_c) addql #4,%sp @@ -363,6 +381,7 @@ movel %d0,%sp@(LOFF_ORIG_D0) | a -1 in the ORIG_D0 field | signifies that the stack frame | is NOT for syscall + GET_CURRENT(%d0) movel %sp,%sp@- | stack frame pointer argument bsrl SYMBOL_NAME(trap_c) addql #4,%sp @@ -419,6 +438,7 @@ SAVE_ALL moveq #-1,%d0 movel %d0,%sp@(LOFF_ORIG_D0) | indicate stack frame not for syscall + GET_CURRENT(%d0) bral SYMBOL_NAME(ret_from_exception) | deliver signals, reschedule etc.. @@ -497,7 +517,7 @@ movel %d0,-(%sp) movel %a1,-(%sp) movel %a0,-(%sp) - jsr copyin + jsr copyin addw #12,%sp movel (%sp)+,%d1 rts diff -u --recursive --new-file v2.1.39/linux/arch/m68k/ifpsp060/iskeleton.S linux/arch/m68k/ifpsp060/iskeleton.S --- v2.1.39/linux/arch/m68k/ifpsp060/iskeleton.S Wed Sep 25 00:47:39 1996 +++ linux/arch/m68k/ifpsp060/iskeleton.S Sun May 18 17:10:37 1997 @@ -36,13 +36,23 @@ #include -LOFF_ORIG_D0 = 0x20 +/* + * This has to match entry.S + */ +LOFF_ORIG_D0 = 0x24 + +#define curptr a2 #define SAVE_ALL \ clrl %sp@-; /* stk_adj */ \ movel %d0,%sp@-; /* orig d0 */ \ movel %d0,%sp@-; /* d0 */ \ - moveml %d1-%d5/%a0-%a1,%sp@- + moveml %d1-%d5/%a0-%a1/%curptr,%sp@-; + +#define GET_CURRENT(tmp) \ + movel %sp,tmp; \ + andw &-8192,tmp; \ + movel tmp,%curptr; |################################ | (1) EXAMPLE CALL-OUTS # @@ -85,6 +95,7 @@ SAVE_ALL moveq #-1,%d0 movel %d0,%sp@(LOFF_ORIG_D0) | indicate stack frame not for syscall + GET_CURRENT(%d0) bral SYMBOL_NAME(ret_from_exception) | deliver signals, reschedule etc.. | diff -u --recursive --new-file v2.1.39/linux/arch/m68k/kernel/entry.S linux/arch/m68k/kernel/entry.S --- v2.1.39/linux/arch/m68k/kernel/entry.S Wed Apr 23 19:01:15 1997 +++ linux/arch/m68k/kernel/entry.S Sun May 18 17:10:37 1997 @@ -33,12 +33,13 @@ * 10(sp) - d5 * 14(sp) - a0 * 18(sp) - a1 - * 1C(sp) - d0 - * 20(sp) - orig_d0 - * 24(sp) - stack adjustment - * 28(sp) - sr - * 2A(sp) - pc - * 2E(sp) - format & vector + * 1C(sp) - a2 + * 20(sp) - d0 + * 24(sp) - orig_d0 + * 28(sp) - stack adjustment + * 2C(sp) - sr + * 2E(sp) - pc + * 32(sp) - format & vector */ /* @@ -47,6 +48,11 @@ * number 0 in the 'current_set' list. */ +/* + * 97/05/14 Andreas: Register %a2 is now set to the current task throughout + * the whole kernel. + */ + #include #include #include @@ -57,6 +63,8 @@ .globl SYMBOL_NAME(kgdb_registers) #endif +#define curptr a2 + LENOSYS = 38 /* @@ -80,15 +88,15 @@ #define MAX_NOINT_IPL 0 #endif /* machine compilation types */ -LD0 = 0x1C -LORIG_D0 = 0x20 -LSR = 0x28 -LFORMATVEC = 0x2E +LD0 = 0x20 +LORIG_D0 = 0x24 +LSR = 0x2C +LFORMATVEC = 0x32 /* * This defines the normal kernel pt-regs layout. * - * regs are a2-a6 and d6-d7 preserved by C code + * regs a3-a6 and d6-d7 are preserved by C code * the kernel doesn't mess with usp unless it needs to */ #ifndef CONFIG_KGDB @@ -96,7 +104,7 @@ clrl %sp@-; /* stk_adj */ \ movel %d0,%sp@-; /* orig d0 */ \ movel %d0,%sp@-; /* d0 */ \ - moveml %d1-%d5/%a0-%a1,%sp@- + moveml %d1-%d5/%a0-%a1/%curptr,%sp@-; #else /* Need to save the "missing" registers for kgdb... */ @@ -104,25 +112,30 @@ clrl %sp@-; /* stk_adj */ \ movel %d0,%sp@-; /* orig d0 */ \ movel %d0,%sp@-; /* d0 */ \ - moveml %d1-%d5/%a0-%a1,%sp@-; \ + moveml %d1-%d5/%a0-%a1/%curptr,%sp@-; \ moveml %d6-%d7,SYMBOL_NAME(kgdb_registers)+GDBOFFA_D6; \ - moveml %a2-%a6,SYMBOL_NAME(kgdb_registers)+GDBOFFA_A2 + moveml %a3-%a6,SYMBOL_NAME(kgdb_registers)+GDBOFFA_A3; #endif #define RESTORE_ALL \ - moveml %sp@+,%a0-%a1/%d1-%d5; \ + moveml %sp@+,%a0-%a1/%curptr/%d1-%d5; \ movel %sp@+,%d0; \ addql #4,%sp; /* orig d0 */ \ addl %sp@+,%sp; /* stk adj */ \ rte -#define SWITCH_STACK_SIZE (7*4+4) /* includes return address */ +#define SWITCH_STACK_SIZE (6*4+4) /* includes return address */ #define SAVE_SWITCH_STACK \ - moveml %a2-%a6/%d6-%d7,%sp@- + moveml %a3-%a6/%d6-%d7,%sp@- #define RESTORE_SWITCH_STACK \ - moveml %sp@+,%a2-%a6/%d6-%d7 + moveml %sp@+,%a3-%a6/%d6-%d7 + +#define GET_CURRENT(tmp) \ + movel %sp,tmp; \ + andw &-8192,tmp; \ + movel tmp,%curptr; .globl SYMBOL_NAME(system_call), SYMBOL_NAME(buserr), SYMBOL_NAME(trap) .globl SYMBOL_NAME(resume), SYMBOL_NAME(ret_from_exception) @@ -139,6 +152,7 @@ | signifies that the stack frame | is NOT for syscall + GET_CURRENT(%d0) movel %sp,%sp@- | stack frame pointer argument bsrl SYMBOL_NAME(buserr_c) addql #4,%sp @@ -150,6 +164,7 @@ movel %d0,%sp@(LORIG_D0) | a -1 in the ORIG_D0 field | signifies that the stack frame | is NOT for syscall + GET_CURRENT(%d0) movel %sp,%sp@- | stack frame pointer argument bsrl SYMBOL_NAME(trap_c) addql #4,%sp @@ -190,6 +205,7 @@ SAVE_ALL movel %d0,%d2 + GET_CURRENT(%d0) | save top of frame pea %sp@ jbsr SYMBOL_NAME(set_esp0) @@ -197,8 +213,7 @@ cmpl #NR_syscalls,%d2 jcc badsys - movel SYMBOL_NAME(current_set),%a0 - btst #5,%a0@(LTASK_FLAGS+3) | PF_TRACESYS + btst #5,%curptr@(LTASK_FLAGS+3) | PF_TRACESYS jne do_trace jbsr @(SYMBOL_NAME(sys_call_table),%d2:l:4)@(0) movel %d0,%sp@(LD0) | save the return value @@ -208,24 +223,23 @@ bnes 2f | if so, skip resched, signals tstl SYMBOL_NAME(need_resched) jne SYMBOL_NAME(reschedule) - movel SYMBOL_NAME(current_set),%a0 - cmpl #SYMBOL_NAME(task),%a0 | task[0] cannot have signals + cmpl #SYMBOL_NAME(task),%curptr | task[0] cannot have signals jeq 2f - bclr #5,%a0@(LTASK_FLAGS+1) | check for delayed trace + bclr #5,%curptr@(LTASK_FLAGS+1) | check for delayed trace jne do_delayed_trace 5: - tstl %a0@(LTASK_STATE) | state + tstl %curptr@(LTASK_STATE) | state jne SYMBOL_NAME(reschedule) - tstl %a0@(LTASK_COUNTER) | counter + tstl %curptr@(LTASK_COUNTER) | counter jeq SYMBOL_NAME(reschedule) - movel %a0@(LTASK_BLOCKED),%d0 + movel %curptr@(LTASK_BLOCKED),%d0 movel %d0,%d1 | save blocked in d1 for sig handling notl %d0 - btst #4,%a0@(LTASK_FLAGS+3) | PF_PTRACED + btst #4,%curptr@(LTASK_FLAGS+3) | PF_PTRACED jeq 1f moveq #-1,%d0 | let the debugger see all signals -1: andl %a0@(LTASK_SIGNAL),%d0 +1: andl %curptr@(LTASK_SIGNAL),%d0 jne Lsignal_return 2: RESTORE_ALL @@ -248,7 +262,6 @@ jbsr SYMBOL_NAME(send_sig) addql #8,%sp addql #4,%sp - movel SYMBOL_NAME(current_set),%a0 jra 5b /* @@ -260,6 +273,7 @@ movel %d0,%sp@(LORIG_D0) | a -1 in the ORIG_D0 field | signifies that the stack frame | is NOT for syscall + GET_CURRENT(%d0) addql #1,SYMBOL_NAME(local_irq_count) | put exception # in d0 bfextu %sp@(LFORMATVEC){#4,#10},%d0 @@ -270,28 +284,31 @@ addql #8,%sp | pop parameters off stack SYMBOL_NAME_LABEL(ret_from_interrupt) - /* check if we need to do software interrupts */ - movel SYMBOL_NAME(local_irq_count),%d1 - subql #1,%d1 - jne 4f -#if 0 - bfextu %sp@(LSR){#5,#3},%d0 | Check for nested interrupt. + subql #1,SYMBOL_NAME(local_irq_count) + jeq 1f +2: + RESTORE_ALL +1: +#if 1 + bfextu %sp@(LSR){#5,#3},%d0 | Check for nested interrupt. #if MAX_NOINT_IPL > 0 cmpiw #MAX_NOINT_IPL,%d0 #endif - jhi 4f + jhi 2b #endif + /* Let the rest run with interrupts allowed. This is safe since + the kernel never uses a non-standard ipl and this is the outer + level interrupt. */ + andw #ALLOWINT,%sr + + /* check if we need to do software interrupts */ + movel SYMBOL_NAME(bh_active),%d0 andl SYMBOL_NAME(bh_mask),%d0 - jeq 3f + jeq SYMBOL_NAME(ret_from_exception) - jbsr SYMBOL_NAME(do_bottom_half) -3: - clrl SYMBOL_NAME(local_irq_count) - jra SYMBOL_NAME(ret_from_exception) -4: - movel %d1,SYMBOL_NAME(local_irq_count) - RESTORE_ALL + pea SYMBOL_NAME(ret_from_exception) + jra SYMBOL_NAME(do_bottom_half) /* Handler for uninitialized and spurious interrupts */ @@ -398,7 +415,7 @@ 3: /* get pointer to tss struct (a1 contains new task) */ - movel %a1,SYMBOL_NAME(current_set) + movel %a1,%curptr addl %d1,%a1 /* Skip address space switching if they are the same. */ @@ -419,7 +436,7 @@ movec %cacr,%d0 oriw #LFLUSH_I_AND_D,%d0 movec %d0,%cacr - + /* switch the root pointer */ pmove %a1@(LTSS_CRP),%crp #endif diff -u --recursive --new-file v2.1.39/linux/arch/m68k/kernel/head.S linux/arch/m68k/kernel/head.S --- v2.1.39/linux/arch/m68k/kernel/head.S Wed Apr 23 19:01:15 1997 +++ linux/arch/m68k/kernel/head.S Sun May 18 17:10:37 1997 @@ -223,17 +223,9 @@ movel %d0,%a0@ /* save cache mode for page tables */ /* - * raise interrupt level with MASTER bit set, copy isp to msp (if not 68060) + * raise interrupt level */ -#ifdef FROM_PL9 - movew #0x3700,%sr - is_060(1f) - movec %isp,%d0 - movel %d0,%sp -1: -#else movew #0x2700,%sr -#endif /* If running on an Atari, determine the I/O base of the @@ -896,8 +888,10 @@ /* * Setup initial stack pointer + * We need to get current loaded up with our first task... */ - lea SYMBOL_NAME(init_user_stack)+PAGESIZE,%sp + lea SYMBOL_NAME(init_task_union),%a2 + lea 8192(%a2),%sp /* jump to the kernel start */ putr() diff -u --recursive --new-file v2.1.39/linux/arch/m68k/kernel/process.c linux/arch/m68k/kernel/process.c --- v2.1.39/linux/arch/m68k/kernel/process.c Tue May 13 22:41:02 1997 +++ linux/arch/m68k/kernel/process.c Sun May 18 17:10:37 1997 @@ -30,6 +30,7 @@ #include #include #include +#include /* * Initial task structure. Make this a per-architecture thing, @@ -37,15 +38,15 @@ * alignment requirements and potentially different initial * setup. */ -static unsigned long init_kernel_stack[1024] = { STACK_MAGIC, }; -unsigned long init_user_stack[1024] = { STACK_MAGIC, }; static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; - struct mm_struct init_mm = INIT_MM; -struct task_struct init_task = INIT_TASK; + +union task_union init_task_union + __attribute__((section("init_task"), aligned(2*PAGE_SIZE))) + = { task: INIT_TASK }; asmlinkage void ret_from_exception(void); @@ -71,6 +72,7 @@ #else /* portable version */ __asm__("stop #0x2000" : : : "cc"); #endif /* machine compilation types */ + run_task_queue(&tq_scheduler); schedule(); } ret = 0; @@ -101,8 +103,8 @@ printk("\n"); printk("Format %02x Vector: %04x PC: %08lx Status: %04x\n", regs->format, regs->vector, regs->pc, regs->sr); - printk("ORIG_D0: %08lx D0: %08lx A1: %08lx\n", - regs->orig_d0, regs->d0, regs->a1); + printk("ORIG_D0: %08lx D0: %08lx A2: %08lx A1: %08lx\n", + regs->orig_d0, regs->d0, regs->a2, regs->a1); printk("A0: %08lx D5: %08lx D4: %08lx\n", regs->a0, regs->d5, regs->d4); printk("D3: %08lx D2: %08lx D1: %08lx\n", @@ -169,8 +171,8 @@ struct switch_stack * childstack, *stack; unsigned long stack_offset, *retp; - stack_offset = PAGE_SIZE - sizeof(struct pt_regs); - childregs = (struct pt_regs *) (p->kernel_stack_page + stack_offset); + stack_offset = 2*PAGE_SIZE - sizeof(struct pt_regs); + childregs = (struct pt_regs *) ((unsigned long) p + stack_offset); *childregs = *regs; childregs->d0 = 0; @@ -256,7 +258,7 @@ dump->regs.d7 = sw->d7; dump->regs.a0 = regs->a0; dump->regs.a1 = regs->a1; - dump->regs.a2 = sw->a2; + dump->regs.a2 = regs->a2; dump->regs.a3 = sw->a3; dump->regs.a4 = sw->a4; dump->regs.a5 = sw->a5; diff -u --recursive --new-file v2.1.39/linux/arch/m68k/kernel/ptrace.c linux/arch/m68k/kernel/ptrace.c --- v2.1.39/linux/arch/m68k/kernel/ptrace.c Tue May 13 22:41:02 1997 +++ linux/arch/m68k/kernel/ptrace.c Sun May 18 17:10:37 1997 @@ -47,7 +47,7 @@ static int regoff[] = { PT_REG(d1), PT_REG(d2), PT_REG(d3), PT_REG(d4), PT_REG(d5), SW_REG(d6), SW_REG(d7), PT_REG(a0), - PT_REG(a1), SW_REG(a2), SW_REG(a3), SW_REG(a4), + PT_REG(a1), PT_REG(a2), SW_REG(a3), SW_REG(a4), SW_REG(a5), SW_REG(a6), PT_REG(d0), -1, PT_REG(orig_d0), PT_REG(sr), PT_REG(pc), }; @@ -104,7 +104,7 @@ repeat: pgdir = pgd_offset(vma->vm_mm, addr); if (pgd_none(*pgdir)) { - do_no_page(tsk, vma, addr, 0); + handle_mm_fault(tsk, vma, addr, 0); goto repeat; } if (pgd_bad(*pgdir)) { @@ -114,7 +114,7 @@ } pgmiddle = pmd_offset(pgdir,addr); if (pmd_none(*pgmiddle)) { - do_no_page(tsk, vma, addr, 0); + handle_mm_fault(tsk, vma, addr, 0); goto repeat; } if (pmd_bad(*pgmiddle)) { @@ -125,7 +125,7 @@ } pgtable = pte_offset(pgmiddle, addr); if (!pte_present(*pgtable)) { - do_no_page(tsk, vma, addr, 0); + handle_mm_fault(tsk, vma, addr, 0); goto repeat; } page = pte_page(*pgtable); @@ -156,7 +156,7 @@ repeat: pgdir = pgd_offset(vma->vm_mm, addr); if (!pgd_present(*pgdir)) { - do_no_page(tsk, vma, addr, 1); + handle_mm_fault(tsk, vma, addr, 1); goto repeat; } if (pgd_bad(*pgdir)) { @@ -166,7 +166,7 @@ } pgmiddle = pmd_offset(pgdir,addr); if (pmd_none(*pgmiddle)) { - do_no_page(tsk, vma, addr, 1); + handle_mm_fault(tsk, vma, addr, 1); goto repeat; } if (pmd_bad(*pgmiddle)) { @@ -177,12 +177,12 @@ } pgtable = pte_offset(pgmiddle, addr); if (!pte_present(*pgtable)) { - do_no_page(tsk, vma, addr, 1); + handle_mm_fault(tsk, vma, addr, 1); goto repeat; } page = pte_page(*pgtable); if (!pte_write(*pgtable)) { - do_wp_page(tsk, vma, addr, 2); + handle_mm_fault(tsk, vma, addr, 1); goto repeat; } /* this is a hack for non-kernel-mapped video buffers and similar */ diff -u --recursive --new-file v2.1.39/linux/arch/m68k/kernel/sys_m68k.c linux/arch/m68k/kernel/sys_m68k.c --- v2.1.39/linux/arch/m68k/kernel/sys_m68k.c Wed Apr 23 19:01:15 1997 +++ linux/arch/m68k/kernel/sys_m68k.c Sun May 18 17:10:37 1997 @@ -532,12 +532,15 @@ */ vma = find_vma (current->mm, addr); ret = -EINVAL; + /* Check for overflow. */ + if (addr + len < addr) + goto out; if (vma == NULL || addr < vma->vm_start || addr + len > vma->vm_end) goto out; } if (CPU_IS_020_OR_030) { - if (scope == FLUSH_SCOPE_LINE) { + if (scope == FLUSH_SCOPE_LINE && len < 256) { unsigned long cacr; __asm__ ("movec %%cacr, %0" : "=r" (cacr)); if (cache & FLUSH_CACHE_INSN) diff -u --recursive --new-file v2.1.39/linux/arch/m68k/kernel/traps.c linux/arch/m68k/kernel/traps.c --- v2.1.39/linux/arch/m68k/kernel/traps.c Tue May 13 22:41:02 1997 +++ linux/arch/m68k/kernel/traps.c Sun May 18 17:10:37 1997 @@ -933,16 +933,15 @@ #endif console_verbose(); printk("%s: %08x\n",str,nr); - printk("PC: [<%08lx>]\nSR: %04x SP: %p\n", fp->pc, fp->sr, fp); + printk("PC: [<%08lx>]\nSR: %04x SP: %p a2: %08lx\n", + fp->pc, fp->sr, fp, fp->a2); printk("d0: %08lx d1: %08lx d2: %08lx d3: %08lx\n", fp->d0, fp->d1, fp->d2, fp->d3); printk("d4: %08lx d5: %08lx a0: %08lx a1: %08lx\n", fp->d4, fp->d5, fp->a0, fp->a1); - if (STACK_MAGIC != *(unsigned long *)current->kernel_stack_page) - printk("Corrupted stack page\n"); printk("Process %s (pid: %d, stackpage=%08lx)\n", - current->comm, current->pid, current->kernel_stack_page); + current->comm, current->pid, PAGE_SIZE+(unsigned long)current); #ifdef CONFIG_KGDB } #endif diff -u --recursive --new-file v2.1.39/linux/arch/m68k/lib/semaphore.S linux/arch/m68k/lib/semaphore.S --- v2.1.39/linux/arch/m68k/lib/semaphore.S Wed Apr 23 19:01:15 1997 +++ linux/arch/m68k/lib/semaphore.S Sun May 18 17:10:37 1997 @@ -19,8 +19,7 @@ movel %a1,-(%sp) jbsr SYMBOL_NAME(__down) movel (%sp)+,%a1 - movel (%sp)+,%d0 - movel (%sp)+,%d1 + moveml (%sp)+,%a0/%d0/%d1 rts ENTRY(__down_failed_interruptible) @@ -30,6 +29,7 @@ jbsr SYMBOL_NAME(__down_interruptible) movel (%sp)+,%a1 movel (%sp)+,%d1 + movel (%sp)+,%a0 rts ENTRY(__up_wakeup) @@ -37,6 +37,5 @@ movel %a1,-(%sp) jbsr SYMBOL_NAME(__up) movel (%sp)+,%a1 - movel (%sp)+,%d0 - movel (%sp)+,%d1 + moveml (%sp)+,%a0/%d0/%d1 rts diff -u --recursive --new-file v2.1.39/linux/arch/m68k/mm/fault.c linux/arch/m68k/mm/fault.c --- v2.1.39/linux/arch/m68k/mm/fault.c Wed Apr 23 19:01:16 1997 +++ linux/arch/m68k/mm/fault.c Sun May 18 17:10:37 1997 @@ -32,14 +32,10 @@ asmlinkage int do_page_fault(struct pt_regs *regs, unsigned long address, unsigned long error_code) { - void (*handler)(struct task_struct *, - struct vm_area_struct *, - unsigned long, - int); struct task_struct *tsk = current; struct mm_struct *mm = tsk->mm; struct vm_area_struct * vma; - unsigned long fixup, fault_pc; + unsigned long fixup; int write; #ifdef DEBUG @@ -73,10 +69,8 @@ */ good_area: write = 0; - handler = do_no_page; switch (error_code & 3) { default: /* 3: write, present */ - handler = do_wp_page; /* fall through */ case 2: /* write, not present */ if (!(vma->vm_flags & VM_WRITE)) @@ -89,7 +83,7 @@ if (!(vma->vm_flags & (VM_READ | VM_EXEC))) goto bad_area; } - handler(tsk, vma, address, write); + handle_mm_fault(current, vma, address, write); up(&mm->mmap_sem); /* There seems to be a missing invalidate somewhere in do_no_page. @@ -108,10 +102,10 @@ up(&mm->mmap_sem); /* Are we prepared to handle this fault? */ - fault_pc = regs->pc; - if ((fixup = search_exception_table(fault_pc)) != 0) { + if ((fixup = search_exception_table(regs->pc)) != 0) { struct pt_regs *tregs; - printk(KERN_DEBUG "Exception at [<%lx>] (%lx)\n", fault_pc, fixup); + printk(KERN_DEBUG "%s: Exception at [<%lx>] (%lx)\n", + current->comm, regs->pc, fixup); /* Create a new four word stack frame, discarding the old one. */ regs->stkadj = frame_extra_sizes[regs->format]; diff -u --recursive --new-file v2.1.39/linux/arch/m68k/mm/init.c linux/arch/m68k/mm/init.c --- v2.1.39/linux/arch/m68k/mm/init.c Tue May 13 22:41:02 1997 +++ linux/arch/m68k/mm/init.c Sun May 18 17:10:37 1997 @@ -491,8 +491,6 @@ atomic_set(&mem_map[MAP_NR(addr)].count, 1); free_page(addr); } - printk ("Freeing unused kernel memory: %dk freed\n", - (&__init_end - &__init_begin) >> 10); } void si_meminfo(struct sysinfo *val) diff -u --recursive --new-file v2.1.39/linux/arch/m68k/mm/memory.c linux/arch/m68k/mm/memory.c --- v2.1.39/linux/arch/m68k/mm/memory.c Wed Apr 23 19:01:16 1997 +++ linux/arch/m68k/mm/memory.c Sun May 18 17:10:37 1997 @@ -280,6 +280,8 @@ return( (vaddr & mask) == (base & mask) ); } +static unsigned long mm_vtop_fallback (unsigned long); + /* * The following two routines map from a physical address to a kernel * virtual address and vice versa. @@ -301,7 +303,13 @@ offset += m68k_memory[i].size; i++; }while (i < m68k_num_memory); + return mm_vtop_fallback(vaddr); +} +/* Separate function to make the common case faster (needs to save less + registers) */ +static unsigned long mm_vtop_fallback (unsigned long vaddr) +{ /* not in one of the memory chunks; test for applying transparent * translation */ diff -u --recursive --new-file v2.1.39/linux/arch/m68k/vmlinux.lds linux/arch/m68k/vmlinux.lds --- v2.1.39/linux/arch/m68k/vmlinux.lds Tue May 13 22:41:02 1997 +++ linux/arch/m68k/vmlinux.lds Sun May 18 17:10:37 1997 @@ -9,6 +9,7 @@ .text : { *(.text) *(.fixup) + *(.text.lock) /* out-of-line lock text */ *(.gnu.warning) } = 0x4e75 .rodata : { *(.rodata) } @@ -29,6 +30,9 @@ *(.data) CONSTRUCTORS } + + . = ALIGN(8192); + init_task : { *(init_task) } /* The initial task and kernel stack */ _edata = .; /* End of data section */ diff -u --recursive --new-file v2.1.39/linux/arch/sparc/mm/srmmu.c linux/arch/sparc/mm/srmmu.c --- v2.1.39/linux/arch/sparc/mm/srmmu.c Thu May 15 16:48:02 1997 +++ linux/arch/sparc/mm/srmmu.c Sun May 18 17:10:37 1997 @@ -1,4 +1,4 @@ -/* $Id: srmmu.c,v 1.145 1997/05/04 10:02:15 ecd Exp $ +/* $Id: srmmu.c,v 1.146 1997/05/18 21:11:09 davem Exp $ * srmmu.c: SRMMU specific routines for memory management. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -2255,7 +2255,7 @@ start += PAGE_SIZE; } } - } while ((vmaring = vmaring->vm_next_share) != inode->i_mmap); + } while ((vmaring = vmaring->vm_next_share) != NULL); if(alias_found && !(pte_val(pte) & _SUN4C_PAGE_NOCACHE)) { pgdp = srmmu_pgd_offset(vma->vm_mm, address); diff -u --recursive --new-file v2.1.39/linux/arch/sparc/mm/sun4c.c linux/arch/sparc/mm/sun4c.c --- v2.1.39/linux/arch/sparc/mm/sun4c.c Tue May 13 22:41:03 1997 +++ linux/arch/sparc/mm/sun4c.c Sun May 18 17:10:37 1997 @@ -1,4 +1,4 @@ -/* $Id: sun4c.c,v 1.147 1997/05/01 08:53:42 davem Exp $ +/* $Id: sun4c.c,v 1.148 1997/05/18 21:11:19 davem Exp $ * sun4c.c: Doing in software what should be done in hardware. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -2467,7 +2467,7 @@ start += PAGE_SIZE; } } - } while ((vmaring = vmaring->vm_next_share) != inode->i_mmap); + } while ((vmaring = vmaring->vm_next_share) != NULL); if(alias_found && !(pte_val(pte) & _SUN4C_PAGE_NOCACHE)) { pgdp = sun4c_pgd_offset(vma->vm_mm, address); diff -u --recursive --new-file v2.1.39/linux/arch/sparc64/kernel/Makefile linux/arch/sparc64/kernel/Makefile --- v2.1.39/linux/arch/sparc64/kernel/Makefile Thu May 15 16:48:02 1997 +++ linux/arch/sparc64/kernel/Makefile Sun May 18 17:10:37 1997 @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.17 1997/05/04 07:20:58 davem Exp $ +# $Id: Makefile,v 1.20 1997/05/18 08:42:11 davem Exp $ # Makefile for the linux kernel. # # Note! Dependencies are done automagically by 'make dep', which also @@ -18,7 +18,7 @@ O_TARGET := kernel.o O_OBJS := etrap.o rtrap.o hack.o process.o setup.o cpu.o idprom.o \ systbls.o traps.o entry.o devices.o auxio.o ioport.o \ - irq.o time.o sys_sparc.o + irq.o time.o sys_sparc.o signal.o winfixup.o OX_OBJS := sparc64_ksyms.o ifdef CONFIG_SPARC32_COMPAT @@ -26,11 +26,16 @@ endif ifdef CONFIG_BINFMT_ELF32 - O_OBJS += sparcelf32.o + O_OBJS += binfmt_elf32.o endif head.o: head.S ttable.S itlb_miss.S dtlb_miss.S dtlb_prot.S $(CC) -D__ASSEMBLY__ -ansi -c $*.S -o $*.o + +# +# This is just to get the dependencies... +# +binfmt_elf32.o: $(TOPDIR)/fs/binfmt_elf.c check_asm: dummy @echo "#include " > tmp.c diff -u --recursive --new-file v2.1.39/linux/arch/sparc64/kernel/binfmt_elf32.c linux/arch/sparc64/kernel/binfmt_elf32.c --- v2.1.39/linux/arch/sparc64/kernel/binfmt_elf32.c Wed Dec 31 16:00:00 1969 +++ linux/arch/sparc64/kernel/binfmt_elf32.c Sun May 18 17:10:37 1997 @@ -0,0 +1,34 @@ +/* binfmt_elf32.c: Support 32-bit Sparc ELF binaries on Ultra. + * + */ + +#define ELF_ARCH EM_SPARC +#define ELF_CLASS ELFCLASS32 +#define ELF_DATA ELFDATA2MSB; + +#include +#include +#include + +#define elf_addr_t u32 +#define elf_caddr_t u32 +#undef start_thread +#define start_thread start_thread32 +#define init_elf_binfmt init_elf32_binfmt +#undef CONFIG_BINFMT_ELF +#ifdef CONFIG_BINFMT_ELF32 +#define CONFIG_BINFMT_ELF CONFIG_BINFMT_ELF32 +#endif +#undef CONFIG_BINFMT_ELF_MODULE +#ifdef CONFIG_BINFMT_ELF32_MODULE +#define CONFIG_BINFMT_ELF_MODULE CONFIG_BINFMT_ELF32_MODULE +#endif +#define ELF_FLAGS_INIT current->tss.flags |= SPARC_FLAG_32BIT + +MODULE_DESCRIPTION("Binary format loader for compatibility with 32bit SparcLinux binaries on the Ultra"); +MODULE_AUTHOR("Eric Youngdale, David S. Miller, Jakub Jelinek"); + +#undef MODULE_DESCRIPTION +#undef MODULE_AUTHOR + +#include "../../../fs/binfmt_elf.c" diff -u --recursive --new-file v2.1.39/linux/arch/sparc64/kernel/dtlb_prot.S linux/arch/sparc64/kernel/dtlb_prot.S --- v2.1.39/linux/arch/sparc64/kernel/dtlb_prot.S Mon Apr 14 16:28:09 1997 +++ linux/arch/sparc64/kernel/dtlb_prot.S Sun May 18 17:10:37 1997 @@ -1,4 +1,4 @@ -/* $Id: dtlb_prot.S,v 1.10 1997/03/25 09:47:13 davem Exp $ +/* $Id: dtlb_prot.S,v 1.12 1997/05/18 10:04:43 davem Exp $ * dtlb_prot.S: Data TLB protection code, this is included directly * into the trap table. * @@ -18,30 +18,30 @@ /*0x04*/ srlx %g1, 8, %g3 ! Position PGD offset /*0x08*/ sllx %g1, 2, %g4 ! Position PMD offset /*0x0c*/ and %g3, %g2, %g3 ! Mask PGD offset - /*0x10*/ and %g4, %g2, %g3 ! Mask PMD offset + /*0x10*/ and %g4, %g2, %g4 ! Mask PMD offset /*0x14*/ ldxa [%g7 + %g3] ASI_PHYS_USE_EC, %g5 ! Load PGD - /*0x18*/ ldxa [%g5 + %g3] ASI_PHYS_USE_EC, %g4 ! Load PMD + /*0x18*/ ldxa [%g5 + %g4] ASI_PHYS_USE_EC, %g4 ! Load PMD /*0x1c*/ ldxa [%g0] ASI_DMMU_TSB_8KB_PTR, %g1 ! For PTE offset /* ICACHE line 2 */ /*0x20*/ srlx %g1, 1, %g1 ! PTE offset /*0x24*/ ldxa [%g4 + %g1] ASI_PHYS_USE_EC, %g3 ! Load PTE /*0x28*/ andcc %g3, _PAGE_WRITE, %g0 ! Writable? - /*0x2c*/ be,pt %xcc, sparc64_dtlb_fault ! Nope... + /*0x2c*/ be,pt %xcc, sparc64_dtlb_prot_catch ! Nope... /*0x30*/ or %g3, (MODIFIED_BITS), %g3 ! Yes it is /*0x34*/ mov TLB_TAG_ACCESS, %g5 ! Get the page - /*0x38*/ ldxa [%g5] ASI_DMMU, %g1 ! From MMU - /*0x3c*/ add %g2, 7, %g5 ! Compute mask + /*0x38*/ add %g1, %g4, %g1 ! to get a tmpreg + /*0x3c*/ ldxa [%g5] ASI_DMMU, %g4 ! From MMU /* ICACHE line 3 */ - /*0x40*/ andn %g1, %g5, %g1 ! Mask page - /*0x44*/ or %g1, 0x10, %g1 ! 2ndary Context - /*0x48*/ stxa %g0, [%g1] ASI_DMMU_DEMAP ! TLB flush page - /*0x4c*/ membar #Sync ! Synchronize - /*0x50*/ stxa %g3, [%g4 + %g1] ASI_PHYS_USE_EC ! Update sw PTE - /*0x54*/ stxa %g3, [%g0] ASI_DTLB_DATA_IN ! TLB load - /*0x58*/ retry ! Trap return - /*0x5c*/ nop + /*0x40*/ add %g2, 7, %g5 ! Compute mask + /*0x44*/ andn %g4, %g5, %g4 ! Mask page + /*0x48*/ or %g4, 0x10, %g4 ! 2ndary Context + /*0x4c*/ stxa %g0, [%g4] ASI_DMMU_DEMAP ! TLB flush page + /*0x50*/ membar #Sync ! Synchronize + /*0x54*/ stxa %g3, [%g1] ASI_PHYS_USE_EC ! Update sw PTE + /*0x58*/ stxa %g3, [%g0] ASI_DTLB_DATA_IN ! TLB load + /*0x5c*/ retry ! Trap return /* ICACHE line 4 */ /*0x60*/ nop diff -u --recursive --new-file v2.1.39/linux/arch/sparc64/kernel/entry.S linux/arch/sparc64/kernel/entry.S --- v2.1.39/linux/arch/sparc64/kernel/entry.S Tue May 13 22:41:04 1997 +++ linux/arch/sparc64/kernel/entry.S Sun May 18 17:10:37 1997 @@ -1,4 +1,4 @@ -/* $Id: entry.S,v 1.15 1997/04/28 14:57:08 davem Exp $ +/* $Id: entry.S,v 1.21 1997/05/18 10:04:44 davem Exp $ * arch/sparc64/kernel/entry.S: Sparc64 trap low-level entry points. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -24,35 +24,55 @@ .text .align 4 -/* FIXME: This is still debugging hack */ - .globl sparc64_dtlb_fault, sparc64_dtlb_refbit_catch, sparc64_itlb_refbit_catch -sparc64_dtlb_fault: - rdpr %pstate, %g1 - wrpr %g1, PSTATE_AG|PSTATE_MG, %pstate - ba,pt %xcc, etrap - rd %pc, %g7 - call sparc64_dtlb_fault_handler - nop + .globl sparc64_dtlb_prot_catch, sparc64_dtlb_refbit_catch + .globl sparc64_itlb_refbit_catch /* Note, DMMU SFAR not updated for fast tlb data access miss * traps, so we must use tag access to find the right page. + * However for DMMU fast protection traps it is updated so + * we use, but we must also clear it _before_ we enable interrupts + * and save state because there is a race where we can push a user + * window right now in etrap, a protection fault happens (for example + * to update the dirty bit) and since we left crap in the sfsr + * it will not get updated properly. */ -sparc64_dtlb_refbit_catch: +sparc64_dtlb_prot_catch: wr %g0, ASI_DMMU, %asi rdpr %pstate, %g1 wrpr %g1, PSTATE_AG|PSTATE_MG, %pstate + rdpr %tl, %g2 ldxa [%g0 + TLB_TAG_ACCESS] %asi, %g5 - srlx %g5, PAGE_SHIFT, %g5 ldxa [%g0 + TLB_SFSR] %asi, %g4 - sllx %g5, PAGE_SHIFT, %g5 + cmp %g2, 1 + stxa %g0, [%g0 + TLB_SFSR] %asi + bgu,a %icc, winfix_trampoline + rdpr %tpc, %g5 ba,pt %xcc, etrap rd %pc, %g7 + b,a,pt %xcc, 1f +sparc64_dtlb_refbit_catch: + wr %g0, ASI_DMMU, %asi + rdpr %pstate, %g1 + wrpr %g1, PSTATE_AG|PSTATE_MG, %pstate + rdpr %tl, %g2 + ldxa [%g0 + TLB_TAG_ACCESS] %asi, %g5 + cmp %g2, 1 + clr %g4 ! sfsr not updated for tlb misses + bgu,a %icc, winfix_trampoline + rdpr %tpc, %g5 + ba,pt %xcc, etrap + rd %pc, %g7 +1: + mov %l5, %o4 ! raw tag access + mov %l4, %o5 ! raw sfsr + srlx %l5, PAGE_SHIFT, %o3 clr %o1 ! text_fault == 0 - mov %l5, %o3 ! address == sfar + sllx %o3, PAGE_SHIFT, %o3 ! address and %l4, 0x4, %o2 ! write == sfsr.W call do_sparc64_fault add %sp, STACK_BIAS + REGWIN_SZ, %o0 ! pt_regs ptr + ba,a,pt %xcc, rtrap sparc64_itlb_refbit_catch: rdpr %pstate, %g1 @@ -63,8 +83,11 @@ ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC], %o3 mov 1, %o1 ! text_fault == 1 clr %o2 ! write == 0 + clr %o4 ! tag access (N/A) + clr %o5 ! raw sfsr (N/A) call do_sparc64_fault add %sp, STACK_BIAS + REGWIN_SZ, %o0 ! pt_regs ptr + ba,a,pt %xcc, rtrap /* Note check out head.h, this code isn't even used for UP, * for SMP things will be different. In particular the data @@ -137,6 +160,7 @@ ba,a,pt %xcc, rtrap .globl sys_pipe, sys_execve, sys_sigpause, sys_nis_syscall + .globl sys_sigsuspend, sys_sigreturn sys_pipe: sethi %hi(sparc_pipe), %g1 @@ -163,12 +187,35 @@ ld [%curptr + AOFF_task_flags], %l5 andcc %l5, 0x20, %g0 + be,pt %icc, rtrap + nop + call syscall_trace + nop + ba,a,pt %xcc, rtrap + +sys_sigsuspend: + call do_sigsuspend + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + + ld [%curptr + AOFF_task_flags], %l5 + andcc %l5, 0x20, %g0 be,pt %icc, ret_sys_call - clr %o0 + nop call syscall_trace nop - ba,pt %xcc, ret_sys_call - clr %o0 + ba,a,pt %xcc, rtrap + +sys_sigreturn: + call do_sigreturn + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + + ld [%curptr + AOFF_task_flags], %l5 + andcc %l5, 0x20, %g0 + be,pt %icc, ret_sys_call + nop + call syscall_trace + nop + ba,a,pt %xcc, rtrap /* This is how fork() was meant to be done, 11 instruction entry. -DaveM */ .globl sys_fork, sys_vfork, sys_clone @@ -216,7 +263,7 @@ .globl ret_from_syscall ret_from_syscall: ba,pt %xcc, ret_sys_call - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_I0], %o0 + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0], %o0 /* Linux native and SunOS system calls enter here... */ .align 4 @@ -248,13 +295,18 @@ call %l7 mov %i5, %o5 - stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_I0] - + stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0] +#if 0 + /* Debugging... */ + call syscall_trace_exit + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0], %o0 +#endif .globl ret_sys_call ret_sys_call: ldx [%curptr + AOFF_task_flags], %l6 mov %ulo(TSTATE_XCARRY | TSTATE_ICARRY), %g2 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_TSTATE], %g3 + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TSTATE], %g3 cmp %o0, -ENOIOCTLCMD sllx %g2, 32, %g2 bgeu,pn %xcc, 1f @@ -263,34 +315,34 @@ /* System call success, clear Carry condition code. */ andn %g3, %g2, %g3 clr %l6 - stx %g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_TSTATE] + stx %g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TSTATE] bne,pn %icc, linux_syscall_trace2 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_TNPC], %l1 /* pc = npc */ + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC], %l1 /* pc = npc */ add %l1, 0x4, %l2 /* npc = npc+4 */ - stx %l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_TPC] + stx %l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC] ba,pt %xcc, rtrap - stx %l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_TNPC] + stx %l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC] 1: /* System call failure, set Carry condition code. * Also, get abs(errno) to return to the process. */ sub %g0, %o0, %o0 or %g3, %g2, %g3 - stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_I0] + stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0] mov 1, %l6 - stx %g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_TSTATE] + stx %g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TSTATE] bne,pn %icc, linux_syscall_trace2 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_TNPC], %l1 /* pc = npc */ + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC], %l1 /* pc = npc */ add %l1, 0x4, %l2 /* npc = npc+4 */ - stx %l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_TPC] + stx %l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC] ba,pt %xcc, rtrap - stx %l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_TNPC] + stx %l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC] linux_syscall_trace2: call syscall_trace add %l1, 0x4, %l2 /* npc = npc+4 */ - stx %l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_TPC] + stx %l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC] ba,pt %xcc, rtrap - stx %l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_TNPC] + stx %l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC] /* End of entry.S */ diff -u --recursive --new-file v2.1.39/linux/arch/sparc64/kernel/etrap.S linux/arch/sparc64/kernel/etrap.S --- v2.1.39/linux/arch/sparc64/kernel/etrap.S Thu May 15 16:48:02 1997 +++ linux/arch/sparc64/kernel/etrap.S Sun May 18 17:10:37 1997 @@ -1,4 +1,4 @@ -/* $Id: etrap.S,v 1.13 1997/05/04 07:21:00 davem Exp $ +/* $Id: etrap.S,v 1.17 1997/05/18 22:52:09 davem Exp $ * etrap.S: Preparing for entry into the kernel on Sparc V9. * * Copyright (C) 1996, 1997 David S. Miller (davem@caip.rutgers.edu) @@ -50,27 +50,11 @@ mov SECONDARY_CONTEXT, %g1 stxa %g2, [%g1] ASI_DMMU - rdpr %wstate, %g1 - sll %g1, 3, %g1 - wrpr %g1, %wstate - - sethi %uhi(KERNBASE), %g2 - or %g2, %ulo(KERNBASE), %g2 - sethi %hi(current_set), %g1 - or %g1, %lo(current_set), %g1 - sllx %g2, 32, %g2 - ldx [%g1 + %g2], %g1 -#ifdef __SMP__ -/* FIXME: Fix the above insn for SMP */ -#endif - rdpr %canrestore, %g2 - wrpr %g0, 0, %canrestore - wrpr %g2, 0, %otherwin - - mov 1, %g2 - sllx %g2, (PAGE_SHIFT + 1), %g2 - sub %g2, (TRACEREG_SZ + REGWIN_SZ), %g2 + rd %pic, %g1 + sethi %hi((PAGE_SIZE<<1)-TRACEREG_SZ-REGWIN_SZ), %g2 + or %g2, %lo((PAGE_SIZE<<1)-TRACEREG_SZ-REGWIN_SZ), %g2 add %g1, %g2, %g2 + rdpr %tstate, %g1 1: stx %g1, [%g2 + REGWIN_SZ + PT_V9_TSTATE] rdpr %tpc, %g1 @@ -81,9 +65,23 @@ stx %g1, [%g2 + REGWIN_SZ + PT_V9_Y] wrpr %g0, 0x0, %tl - rdpr %pstate, %g1 save %g2, -STACK_BIAS, %sp + + /* Must guarentee that here andcc of TSTATE_PRIV at the top is + * still valid in %ccr register. Don't show this trick to your + * mom. -DaveM + */ + bne,pn %xcc, 1f + rdpr %canrestore, %g3 + wrpr %g0, 0, %canrestore + wrpr %g3, 0, %otherwin + + rdpr %wstate, %g6 + sll %g6, 3, %g6 + wrpr %g6, %wstate + +1: mov %g1, %l1 mov %g4, %l4 mov %g5, %l5 @@ -105,15 +103,10 @@ stx %i6, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I6] stx %i7, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I7] wrpr %l1, (PSTATE_IE | PSTATE_AG), %pstate - sethi %uhi(KERNBASE), %g4 - or %g4, %ulo(KERNBASE), %g4 - srlx %sp, (PAGE_SHIFT + 1), %g6 - sllx %g4, 32, %g4 + srlx %sp, 43, %g4 + rd %pic, %g6 jmpl %l2 + 0x4, %g0 - sllx %g6, (PAGE_SHIFT + 1), %g6 -#ifdef __SMP__ -/* FIXME: Fix the above insn for SMP */ -#endif + sllx %g4, 43, %g4 .globl etraptl1 etraptl1: diff -u --recursive --new-file v2.1.39/linux/arch/sparc64/kernel/hack.S linux/arch/sparc64/kernel/hack.S --- v2.1.39/linux/arch/sparc64/kernel/hack.S Mon Apr 14 16:28:09 1997 +++ linux/arch/sparc64/kernel/hack.S Sun May 18 17:10:37 1997 @@ -4,8 +4,6 @@ to compile... */ .text .align 8 - .globl _sigpause_common -_sigpause_common: retl;nop .globl breakpoint breakpoint: retl;nop .globl do_cee @@ -36,8 +34,6 @@ do_iae: retl;nop .globl do_iae_tl1 do_iae_tl1: retl;nop - .globl do_ill -do_ill: retl;nop .globl do_ill_tl1 do_ill_tl1: retl;nop .globl do_irq @@ -48,8 +44,6 @@ do_lddfmna: retl;nop .globl do_lddfmna_tl1 do_lddfmna_tl1: retl;nop - .globl do_mna -do_mna: retl;nop .globl do_mna_tl1 do_mna_tl1: retl;nop .globl do_paw @@ -60,8 +54,6 @@ do_privact: retl;nop .globl do_privop do_privop: retl;nop - .globl do_signal -do_signal: retl;nop .globl do_stdfmna do_stdfmna: retl;nop .globl do_stdfmna_tl1 @@ -200,15 +192,7 @@ sunos_writev: retl;nop .globl sys_ptrace sys_ptrace: retl;nop - .globl sys_sigreturn -sys_sigreturn: retl;nop - .globl sys_sigstack -sys_sigstack: retl;nop - .globl sys_sigsuspend -sys_sigsuspend: retl;nop .globl syscall_trace syscall_trace: retl;nop .globl sys32_ptrace sys32_ptrace: retl;nop - .globl do_sigpause -do_sigpause: retl;nop diff -u --recursive --new-file v2.1.39/linux/arch/sparc64/kernel/head.S linux/arch/sparc64/kernel/head.S --- v2.1.39/linux/arch/sparc64/kernel/head.S Thu May 15 16:48:02 1997 +++ linux/arch/sparc64/kernel/head.S Sun May 18 17:10:37 1997 @@ -1,4 +1,4 @@ -/* $Id: head.S,v 1.28 1997/05/04 07:21:02 davem Exp $ +/* $Id: head.S,v 1.30 1997/05/18 22:52:12 davem Exp $ * head.S: Initial boot code for the Sparc64 port of Linux. * * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu) @@ -268,6 +268,10 @@ mov %sp, %l6 mov %o4, %l7 + /* Setup "Linux Current Register", thanks Sun 8-) */ + wr %g0, 0x1, %pcr + wr %g6, 0x0, %pic + mov 1, %g5 sllx %g5, (PAGE_SHIFT + 1), %g5 sub %g5, (REGWIN_SZ + STACK_BIAS), %g5 @@ -289,14 +293,20 @@ add %l2, 1, %l2 add %l0, %g4, %o0 1: - call bzero_1page + clr %o1 + sethi %hi(PAGE_SIZE), %o2 + or %o2, %lo(PAGE_SIZE), %o2 + call __memset add %l0, %l2, %l0 cmp %l0, %l1 blu,pt %xcc, 1b add %l0, %g4, %o0 /* Now clear empty_zero_page */ - call bzero_1page + clr %o1 + sethi %hi(PAGE_SIZE), %o2 + or %o2, %lo(PAGE_SIZE), %o2 + call __memset mov %g4, %o0 mov %l6, %o1 ! OpenPROM stack diff -u --recursive --new-file v2.1.39/linux/arch/sparc64/kernel/process.c linux/arch/sparc64/kernel/process.c --- v2.1.39/linux/arch/sparc64/kernel/process.c Thu May 15 16:48:02 1997 +++ linux/arch/sparc64/kernel/process.c Sun May 18 17:10:37 1997 @@ -1,4 +1,4 @@ -/* $Id: process.c,v 1.8 1997/05/14 20:45:06 davem Exp $ +/* $Id: process.c,v 1.11 1997/05/18 22:52:19 davem Exp $ * arch/sparc64/kernel/process.c * * Copyright (C) 1995, 1996 David S. Miller (davem@caip.rutgers.edu) @@ -37,8 +37,6 @@ #include #include -struct task_struct *current_set[NR_CPUS] = {&init_task, }; - #ifndef __SMP__ /* @@ -348,7 +346,14 @@ } /* Now, this task is no longer a kernel thread. */ - current->tss.flags &= ~SPARC_FLAG_KTHREAD; + if(current->tss.flags & SPARC_FLAG_KTHREAD) { + current->tss.flags &= ~SPARC_FLAG_KTHREAD; + + /* exec_mmap() set context to NO_CONTEXT, here is + * where we grab a new one. + */ + get_mmu_context(current); + } current->tss.current_ds = USER_DS; } @@ -418,6 +423,64 @@ return sp; } +/* Standard stuff. */ +static inline void shift_window_buffer(int first_win, int last_win, + struct thread_struct *tp) +{ + int i; + + for(i = first_win; i < last_win; i++) { + tp->rwbuf_stkptrs[i] = tp->rwbuf_stkptrs[i+1]; + memcpy(&tp->reg_window[i], &tp->reg_window[i+1], + sizeof(struct reg_window)); + } +} + +void synchronize_user_stack(void) +{ + struct thread_struct *tp = ¤t->tss; + unsigned long window = tp->w_saved; + + flush_user_windows(); + if(window) { + int winsize = REGWIN_SZ; + + if(tp->flags & SPARC_FLAG_32BIT) + winsize = REGWIN32_SZ; + + window -= 1; + do { + unsigned long sp = tp->rwbuf_stkptrs[window]; + struct reg_window *rwin = &tp->reg_window[window]; + + if(!copy_to_user((char *)sp, rwin, winsize)) { + shift_window_buffer(window, tp->w_saved - 1, tp); + tp->w_saved--; + } + } while(window--); + } +} + +void fault_in_user_windows(struct pt_regs *regs) +{ + struct thread_struct *tp = ¤t->tss; + unsigned long window = tp->w_saved; + int winsize = REGWIN_SZ; + + if(tp->flags & SPARC_FLAG_32BIT) + winsize = REGWIN32_SZ; + if(window) { + window -= 1; + do { + unsigned long sp = tp->rwbuf_stkptrs[window]; + struct reg_window *rwin = &tp->reg_window[window]; + + if(copy_to_user((char *)sp, rwin, winsize)) + do_exit(SIGILL); + } while(window--); + } + current->tss.w_saved = 0; +} /* Copy a Sparc thread. The fork() return value conventions * under SunOS are nothing short of bletcherous: diff -u --recursive --new-file v2.1.39/linux/arch/sparc64/kernel/rtrap.S linux/arch/sparc64/kernel/rtrap.S --- v2.1.39/linux/arch/sparc64/kernel/rtrap.S Mon Apr 14 16:28:09 1997 +++ linux/arch/sparc64/kernel/rtrap.S Sun May 18 17:10:37 1997 @@ -1,4 +1,4 @@ -/* $Id: rtrap.S,v 1.11 1997/04/03 13:03:50 davem Exp $ +/* $Id: rtrap.S,v 1.14 1997/05/18 08:42:14 davem Exp $ * rtrap.S: Preparing for return from trap on Sparc V9. * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -71,12 +71,11 @@ /* We came here from to_user, ie. we have now AG. * Also have to push user context back into primary. */ - restore - mov SECONDARY_CONTEXT, %g6 mov PRIMARY_CONTEXT, %g7 ldxa [%g6] ASI_DMMU, %g4 stxa %g4, [%g7] ASI_DMMU + membar #Sync /* XXX flushi would be better -DaveM */ rdpr %wstate, %g1 rdpr %otherwin, %g2 @@ -84,7 +83,6 @@ wrpr %g2, %g0, %canrestore wrpr %g1, %g0, %wstate wrpr %g0, %g0, %otherwin - retry 1: restore retry @@ -101,9 +99,14 @@ ldx [%g6 + AOFF_task_blocked], %o0 or %l7, PSTATE_AG, %l7 ! Will need this for setting back wstate andncc %l0, %o0, %g0 - be,pt %xcc, 3b + be,pt %xcc, check_user_wins mov %l5, %o2 mov %l6, %o3 - add %sp, STACK_BIAS + REGWIN_SZ, %o1 call do_signal + add %sp, STACK_BIAS + REGWIN_SZ, %o1 +check_user_wins: + ldx [%g6 + AOFF_task_tss + AOFF_thread_w_saved], %o2 + brz,pt %o2, 3b + add %sp, STACK_BIAS + REGWIN_SZ, %o1 + call fault_in_user_windows add %o7, 3b-.-4, %o7 diff -u --recursive --new-file v2.1.39/linux/arch/sparc64/kernel/signal.c linux/arch/sparc64/kernel/signal.c --- v2.1.39/linux/arch/sparc64/kernel/signal.c Wed Dec 31 16:00:00 1969 +++ linux/arch/sparc64/kernel/signal.c Sun May 18 17:10:37 1997 @@ -0,0 +1,438 @@ +/* $Id: signal.c,v 1.2 1997/05/18 08:42:15 davem Exp $ + * arch/sparc64/kernel/signal.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) + * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define _S(nr) (1<<((nr)-1)) + +#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) + +asmlinkage int sys_wait4(pid_t pid, unsigned long *stat_addr, + int options, unsigned long *ru); + +asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs, + unsigned long orig_o0, int ret_from_syscall); + +/* This turned off for production... */ +/* #define DEBUG_SIGNALS 1 */ + +/* + * The new signal frame, intended to be used for Linux applications only + * (we have enough in there to work with clone). + * All the interesting bits are in the info field. + */ + +struct new_signal_frame { + struct sparc_stackf ss; + __siginfo_t info; + __siginfo_fpu_t * fpu_save; + unsigned int insns [2]; + __siginfo_fpu_t fpu_state; +}; + +/* Align macros */ +#define NF_ALIGNEDSZ (((sizeof(struct new_signal_frame) + 7) & (~7))) + +/* + * atomically swap in the new signal mask, and wait for a signal. + * This is really tricky on the Sparc, watch out... + */ +asmlinkage void _sigpause_common(unsigned int set, struct pt_regs *regs) +{ + unsigned int mask; + +#ifdef CONFIG_SPARC32_COMPAT + if (current->tss.flags & SPARC_FLAG_32BIT) { + extern asmlinkage void _sigpause32_common(unsigned int, struct pt_regs *); + _sigpause32_common(set, regs); + return; + } +#endif + spin_lock_irq(¤t->sigmask_lock); + mask = current->blocked; + current->blocked = set & _BLOCKABLE; + spin_unlock_irq(¤t->sigmask_lock); + + regs->tpc = regs->tnpc; + regs->tnpc += 4; + + /* Condition codes and return value where set here for sigpause, + * and so got used by setup_frame, which again causes sigreturn() + * to return -EINTR. + */ + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + /* + * Return -EINTR and set condition code here, + * so the interrupted system call actually returns + * these. + */ + regs->tstate |= (TSTATE_ICARRY|TSTATE_XCARRY); + regs->u_regs[UREG_I0] = EINTR; + if (do_signal(mask, regs, 0, 0)) + return; + } +} + +asmlinkage void do_sigpause(unsigned int set, struct pt_regs *regs) +{ + _sigpause_common(set, regs); +} + +asmlinkage void do_sigsuspend(struct pt_regs *regs) +{ + _sigpause_common(regs->u_regs[UREG_I0], regs); +} + + +static inline void +restore_fpu_state(struct pt_regs *regs, __siginfo_fpu_t *fpu) +{ +#ifdef __SMP__ + if (current->flags & PF_USEDFPU) + regs->tstate &= ~(TSTATE_PEF); +#else + if (current == last_task_used_math) { + last_task_used_math = 0; + regs->tstate &= ~(TSTATE_PEF); + } +#endif + current->used_math = 1; + current->flags &= ~PF_USEDFPU; + + copy_from_user(¤t->tss.float_regs[0], + &fpu->si_float_regs[0], + (sizeof(unsigned int) * 64)); + __get_user(current->tss.fsr, &fpu->si_fsr); +} + +void do_sigreturn(struct pt_regs *regs) +{ + struct new_signal_frame *sf; + unsigned long tpc, tnpc, tstate; + __siginfo_fpu_t *fpu_save; + int mask; + +#ifdef CONFIG_SPARC32_COMPAT + if (current->tss.flags & SPARC_FLAG_32BIT) { + extern asmlinkage void do_sigreturn32(struct pt_regs *); + do_sigreturn32(regs); + return; + } +#endif + synchronize_user_stack (); + sf = (struct new_signal_frame *) regs->u_regs [UREG_FP]; + /* 1. Make sure we are not getting garbage from the user */ + if (verify_area (VERIFY_READ, sf, sizeof (*sf))){ + goto segv; + } + if (((unsigned long) sf) & 3){ + goto segv; + } + get_user(tpc, &sf->info.si_regs.tpc); + __get_user(tnpc, &sf->info.si_regs.tnpc); + if ((tpc | tnpc) & 3){ + goto segv; + } + regs->tpc = tpc; + regs->tnpc = tnpc; + + /* 2. Restore the state */ + __get_user(regs->y, &sf->info.si_regs.y); + __get_user(tstate, &sf->info.si_regs.tstate); + copy_from_user(regs->u_regs, sf->info.si_regs.u_regs, sizeof(regs->u_regs)); + + /* User can only change condition codes and FPU enabling in %tstate. */ + regs->tstate &= ~(TSTATE_ICC | TSTATE_PEF); + regs->tstate |= (tstate & (TSTATE_ICC | TSTATE_PEF)); + + __get_user(fpu_save, &sf->fpu_save); + if (fpu_save) + restore_fpu_state(regs, &sf->fpu_state); + __get_user(mask, &sf->info.si_mask); + current->blocked = mask & _BLOCKABLE; + return; +segv: + lock_kernel(); + do_exit(SIGSEGV); +} + +/* Checks if the fp is valid */ +static int invalid_frame_pointer(void *fp, int fplen) +{ + if ((((unsigned long) fp) & 7) || ((unsigned long)fp) > 0x80000000000ULL - fplen) + return 1; + return 0; +} + +static inline void +save_fpu_state(struct pt_regs *regs, __siginfo_fpu_t *fpu) +{ +#ifdef __SMP__ + if (current->flags & PF_USEDFPU) { + fpsave(¤t->tss.float_regs[0], ¤t->tss.fsr); + regs->tstate &= ~(TSTATE_PEF); + current->flags &= ~(PF_USEDFPU); + } +#else + if (current == last_task_used_math) { + fpsave((unsigned long *)¤t->tss.float_regs[0], ¤t->tss.fsr); + last_task_used_math = 0; + regs->tstate &= ~(TSTATE_PEF); + } +#endif + copy_to_user(&fpu->si_float_regs[0], ¤t->tss.float_regs[0], + (sizeof(unsigned int) * 64)); + __put_user(current->tss.fsr, &fpu->si_fsr); + current->used_math = 0; +} + +static inline void +new_setup_frame(struct sigaction *sa, struct pt_regs *regs, + int signo, unsigned long oldmask) +{ + struct new_signal_frame *sf; + int sigframe_size; + unsigned long tmp; + int i; + + /* 1. Make sure everything is clean */ + synchronize_user_stack(); + sigframe_size = NF_ALIGNEDSZ; + if (!current->used_math) + sigframe_size -= sizeof(__siginfo_fpu_t); + + sf = (struct new_signal_frame *)(regs->u_regs[UREG_FP] - sigframe_size); + + if (invalid_frame_pointer (sf, sigframe_size)){ + lock_kernel (); + do_exit(SIGILL); + } + + if (current->tss.w_saved != 0){ + printk ("%s[%d]: Invalid user stack frame for " + "signal delivery.\n", current->comm, current->pid); + lock_kernel (); + do_exit (SIGILL); + } + + /* 2. Save the current process state */ + copy_to_user(&sf->info.si_regs, regs, sizeof (*regs)); + + if (current->used_math) { + save_fpu_state(regs, &sf->fpu_state); + __put_user((u64)&sf->fpu_state, &sf->fpu_save); + } else { + __put_user(0, &sf->fpu_save); + } + + __put_user(oldmask, &sf->info.si_mask); + for (i = 0; i < sizeof(struct reg_window)/8; i++) { + __get_user(tmp, (((u64 *)regs->u_regs[UREG_FP])+i)); + __put_user(tmp, (((u64 *)sf)+i)); + } + + /* 3. return to kernel instructions */ + __put_user(0x821020d8, &sf->insns[0]); /* mov __NR_sigreturn, %g1 */ + __put_user(0x91d02011, &sf->insns[1]); /* t 0x11 */ + + /* 4. signal handler back-trampoline and parameters */ + regs->u_regs[UREG_FP] = (unsigned long) sf; + regs->u_regs[UREG_I0] = signo; + regs->u_regs[UREG_I1] = (unsigned long) &sf->info; + regs->u_regs[UREG_I7] = (unsigned long) (&(sf->insns[0]) - 2); + + /* 5. signal handler */ + regs->tpc = (unsigned long) sa->sa_handler; + regs->tnpc = (regs->tpc + 4); + + /* Flush instruction space. */ + __asm__ __volatile__ ("flush %0; flush %0 + 4" : : "r" (&(sf->insns[0]))); + +} + +static inline void handle_signal(unsigned long signr, struct sigaction *sa, + unsigned long oldmask, struct pt_regs *regs) +{ + new_setup_frame(sa, regs, signr, oldmask); + if(sa->sa_flags & SA_ONESHOT) + sa->sa_handler = NULL; + if(!(sa->sa_flags & SA_NOMASK)) + current->blocked |= (sa->sa_mask | _S(signr)) & _BLOCKABLE; +} + +static inline void syscall_restart(unsigned long orig_i0, struct pt_regs *regs, + struct sigaction *sa) +{ + switch(regs->u_regs[UREG_I0]) { + case ERESTARTNOHAND: + no_system_call_restart: + regs->u_regs[UREG_I0] = EINTR; + regs->tstate |= (TSTATE_ICARRY|TSTATE_XCARRY); + break; + case ERESTARTSYS: + if(!(sa->sa_flags & SA_RESTART)) + goto no_system_call_restart; + /* fallthrough */ + case ERESTARTNOINTR: + regs->u_regs[UREG_I0] = orig_i0; + regs->tpc -= 4; + regs->tnpc -= 4; + } +} + +/* Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + */ +asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs, + unsigned long orig_i0, int restart_syscall) +{ + unsigned long signr, mask = ~current->blocked; + struct sigaction *sa; + +#ifdef CONFIG_SPARC32_COMPAT + if (current->tss.flags & SPARC_FLAG_32BIT) { + extern asmlinkage int do_signal32(unsigned long, struct pt_regs *, + unsigned long, int); + return do_signal32(oldmask, regs, orig_i0, restart_syscall); + } +#endif + while ((signr = current->signal & mask) != 0) { + signr = ffz(~signr); + clear_bit(signr, ¤t->signal); + sa = current->sig->action + signr; + signr++; + if ((current->flags & PF_PTRACED) && signr != SIGKILL) { + current->exit_code = signr; + current->state = TASK_STOPPED; + notify_parent(current); + schedule(); + if (!(signr = current->exit_code)) + continue; + current->exit_code = 0; + if (signr == SIGSTOP) + continue; + if (_S(signr) & current->blocked) { + current->signal |= _S(signr); + continue; + } + sa = current->sig->action + signr - 1; + } + if(sa->sa_handler == SIG_IGN) { + if(signr != SIGCHLD) + continue; + + /* sys_wait4() grabs the master kernel lock, so + * we need not do so, that sucker should be + * threaded and would not be that difficult to + * do anyways. + */ + while(sys_wait4(-1, NULL, WNOHANG, NULL) > 0) + ; + continue; + } + if(sa->sa_handler == SIG_DFL) { + if(current->pid == 1) + continue; + switch(signr) { + case SIGCONT: case SIGCHLD: case SIGWINCH: + continue; + + case SIGTSTP: case SIGTTIN: case SIGTTOU: + if (is_orphaned_pgrp(current->pgrp)) + continue; + + case SIGSTOP: + if (current->flags & PF_PTRACED) + continue; + current->state = TASK_STOPPED; + current->exit_code = signr; + if(!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags & + SA_NOCLDSTOP)) + notify_parent(current); + schedule(); + continue; + + case SIGQUIT: case SIGILL: case SIGTRAP: + case SIGABRT: case SIGFPE: case SIGSEGV: case SIGBUS: + if(current->binfmt && current->binfmt->core_dump) { + if(current->binfmt->core_dump(signr, regs)) + signr |= 0x80; + } +#ifdef DEBUG_SIGNALS + /* Very useful to debug dynamic linker problems */ + printk ("Sig ILL going...\n"); + show_regs (regs); +#endif + /* fall through */ + default: + current->signal |= _S(signr & 0x7f); + current->flags |= PF_SIGNALED; + do_exit(signr); + } + } + if(restart_syscall) + syscall_restart(orig_i0, regs, sa); + handle_signal(signr, sa, oldmask, regs); + return 1; + } + if(restart_syscall && + (regs->u_regs[UREG_I0] == ERESTARTNOHAND || + regs->u_regs[UREG_I0] == ERESTARTSYS || + regs->u_regs[UREG_I0] == ERESTARTNOINTR)) { + /* replay the system call when we are done */ + regs->u_regs[UREG_I0] = orig_i0; + regs->tpc -= 4; + regs->tnpc -= 4; + } + return 0; +} + +asmlinkage int +sys_sigstack(struct sigstack *ssptr, struct sigstack *ossptr) +{ + int ret = -EFAULT; + + lock_kernel(); + /* First see if old state is wanted. */ + if(ossptr) { + if (put_user ((u64)current->tss.sstk_info.the_stack, &ossptr->the_stack) || + __put_user (current->tss.sstk_info.cur_status, &ossptr->cur_status)) + goto out; + } + + /* Now see if we want to update the new state. */ + if(ssptr) { + if (get_user ((u64)current->tss.sstk_info.the_stack, &ssptr->the_stack) || + __put_user (current->tss.sstk_info.cur_status, &ssptr->cur_status)) + goto out; + } + ret = 0; +out: + unlock_kernel(); + return ret; +} diff -u --recursive --new-file v2.1.39/linux/arch/sparc64/kernel/signal32.c linux/arch/sparc64/kernel/signal32.c --- v2.1.39/linux/arch/sparc64/kernel/signal32.c Wed Apr 23 19:01:16 1997 +++ linux/arch/sparc64/kernel/signal32.c Sun May 18 17:10:37 1997 @@ -1,4 +1,4 @@ -/* $Id: signal32.c,v 1.6 1997/04/16 10:27:17 jj Exp $ +/* $Id: signal32.c,v 1.8 1997/05/18 08:42:15 davem Exp $ * arch/sparc64/kernel/signal32.c * * Copyright (C) 1991, 1992 Linus Torvalds @@ -30,8 +30,6 @@ #define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) -#define synchronize_user_stack() do { } while (0) - asmlinkage int sys_wait4(pid_t pid, unsigned long *stat_addr, int options, unsigned long *ru); @@ -117,17 +115,6 @@ } } -asmlinkage void do_sigpause32(unsigned int set, struct pt_regs *regs) -{ - _sigpause32_common(set, regs); -} - -asmlinkage void do_sigsuspend32(struct pt_regs *regs) -{ - _sigpause32_common(regs->u_regs[UREG_I0], regs); -} - - static inline void restore_fpu_state32(struct pt_regs *regs, __siginfo_fpu32_t *fpu) { @@ -248,7 +235,9 @@ { struct signal_sframe32 *sframep; struct sigcontext32 *sc; +#if 0 int window = 0; +#endif int old_status = current->tss.sstk_info.cur_status; unsigned psr; int i; @@ -424,7 +413,9 @@ svr4_mcontext_t *mc; svr4_gwindows_t *gw; svr4_ucontext_t *uc; +#if 0 int window = 0; +#endif unsigned psr; int i; diff -u --recursive --new-file v2.1.39/linux/arch/sparc64/kernel/sparcelf32.c linux/arch/sparc64/kernel/sparcelf32.c --- v2.1.39/linux/arch/sparc64/kernel/sparcelf32.c Thu May 15 16:48:02 1997 +++ linux/arch/sparc64/kernel/sparcelf32.c Wed Dec 31 16:00:00 1969 @@ -1,1281 +0,0 @@ -/* sparcelf32.c: Support 32-bit Sparc ELF binaries on Ultra. - * - * This is just binfmt_elf.c with hooks so that the types are those - * for a 32-bit ELF binaries. - */ - -/* This makes it work. */ -#define ELF_ARCH EM_SPARC -#define ELF_CLASS ELFCLASS32 -#define ELF_DATA ELFDATA2MSB; - -/* - * linux/fs/binfmt_elf.c - * - * These are the functions used to load ELF format executables as used - * on SVr4 machines. Information on the format may be found in the book - * "UNIX SYSTEM V RELEASE 4 Programmers Guide: Ansi C and Programming Support - * Tools". - * - * Copyright 1993, 1994: Eric Youngdale (ericy@cais.com). - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#define DLINFO_ITEMS 12 - -#include - -static int load_elf32_binary(struct linux_binprm * bprm, struct pt_regs * regs); -static int load_elf32_library(int fd); -extern int dump_fpu (struct pt_regs *, elf_fpregset_t *); -extern void dump_thread(struct pt_regs *, struct user *); - -extern unsigned long get_unmapped_area(unsigned long addr, unsigned long len); - -/* - * If we don't support core dumping, then supply a NULL so we - * don't even try. - */ -#ifdef USE_ELF_CORE_DUMP -static int elf32_core_dump(long signr, struct pt_regs * regs); -#else -#define elf32_core_dump NULL -#endif - -#define ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(ELF_EXEC_PAGESIZE-1)) -#define ELF_PAGEOFFSET(_v) ((_v) & (ELF_EXEC_PAGESIZE-1)) - -static struct linux_binfmt elf32_format = { -#ifndef MODULE - NULL, NULL, load_elf32_binary, load_elf32_library, elf32_core_dump -#else - NULL, &__this_module, load_elf32_binary, load_elf32_library, elf32_core_dump -#endif -}; - -static void set_brk(unsigned long start, unsigned long end) -{ - start = PAGE_ALIGN(start); - end = PAGE_ALIGN(end); - if (end <= start) - return; - do_mmap(NULL, start, end - start, - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_FIXED | MAP_PRIVATE, 0); -} - - -/* We need to explicitly zero any fractional pages - after the data section (i.e. bss). This would - contain the junk from the file that should not - be in memory */ - - -static void padzero(unsigned long elf_bss) -{ - unsigned long nbyte; - - nbyte = elf_bss & (PAGE_SIZE-1); - if (nbyte) { - nbyte = PAGE_SIZE - nbyte; - clear_user((void *) elf_bss, nbyte); - } -} - -__u32 *create_elf32_tables(char *p, int argc, int envc, - struct elfhdr * exec, - unsigned long load_addr, - unsigned long interp_load_addr, int ibcs) -{ - __u32 *argv, *envp; - __u32 *sp, *csp; - - /* - * Force 16 byte alignment here for generality. - */ - sp = (__u32 *) (~15UL & (unsigned long) p); - csp = sp; - csp -= exec ? DLINFO_ITEMS*2 : 2; - csp -= envc+1; - csp -= argc+1; - if (!(((unsigned long) csp) & 4)) - sp--; - - /* - * Put the ELF interpreter info on the stack - */ -#define NEW_AUX_ENT(nr, id, val) \ - __put_user ((id), sp+(nr*2)); \ - __put_user ((val), sp+(nr*2+1)); \ - - sp -= 2; - NEW_AUX_ENT(0, AT_NULL, 0); - - if (exec) { - sp -= 11*2; - - NEW_AUX_ENT (0, AT_PHDR, load_addr + exec->e_phoff); - NEW_AUX_ENT (1, AT_PHENT, sizeof (struct elf_phdr)); - NEW_AUX_ENT (2, AT_PHNUM, exec->e_phnum); - NEW_AUX_ENT (3, AT_PAGESZ, PAGE_SIZE); - NEW_AUX_ENT (4, AT_BASE, interp_load_addr); - NEW_AUX_ENT (5, AT_FLAGS, 0); - NEW_AUX_ENT (6, AT_ENTRY, (__u32) exec->e_entry); - NEW_AUX_ENT (7, AT_UID, (__u32) current->uid); - NEW_AUX_ENT (8, AT_EUID, (__u32) current->euid); - NEW_AUX_ENT (9, AT_GID, (__u32) current->gid); - NEW_AUX_ENT (10, AT_EGID, (__u32) current->egid); - } -#undef NEW_AUX_ENT - - sp -= envc+1; - envp = (__u32 *) sp; - sp -= argc+1; - argv = (__u32 *) sp; - if (!ibcs) { - __put_user(((__u32)(long) envp),--sp); - __put_user(((__u32)(long) argv),--sp); - } - - __put_user((__u32)argc,--sp); - current->mm->arg_start = (unsigned long) p; - while (argc-->0) { - __put_user(((__u32)(long)p),argv++); - p += strlen_user(p); - } - __put_user(NULL, argv); - current->mm->arg_end = current->mm->env_start = (unsigned long) p; - while (envc-->0) { - __put_user(((__u32)(long)p),envp++); - p += strlen_user(p); - } - __put_user(NULL, envp); - current->mm->env_end = (unsigned long) p; - return sp; -} - - -/* This is much more generalized than the library routine read function, - so we keep this separate. Technically the library read function - is only provided so that we can read a.out libraries that have - an ELF header */ - -static unsigned long load_elf32_interp(struct elfhdr * interp_elf_ex, - struct inode * interpreter_inode, - unsigned long *interp_load_addr) -{ - struct file * file; - struct elf_phdr *elf_phdata = NULL; - struct elf_phdr *eppnt; - unsigned long load_addr; - int load_addr_set = 0; - int elf_exec_fileno; - int retval; - unsigned long last_bss, elf_bss; - unsigned long error; - int i; - - elf_bss = 0; - last_bss = 0; - error = load_addr = 0; - - /* First of all, some simple consistency checks */ - if ((interp_elf_ex->e_type != ET_EXEC && - interp_elf_ex->e_type != ET_DYN) || - !elf_check_arch(interp_elf_ex->e_machine) || - (!interpreter_inode->i_op || - !interpreter_inode->i_op->default_file_ops->mmap)){ - return ~0UL; - } - - /* Now read in all of the header information */ - - if (sizeof(struct elf_phdr) * interp_elf_ex->e_phnum > PAGE_SIZE) - return ~0UL; - - elf_phdata = (struct elf_phdr *) - kmalloc(sizeof(struct elf_phdr) * interp_elf_ex->e_phnum, - GFP_KERNEL); - if (!elf_phdata) - return ~0UL; - - /* - * If the size of this structure has changed, then punt, since - * we will be doing the wrong thing. - */ - if (interp_elf_ex->e_phentsize != sizeof(struct elf_phdr)) - { - kfree(elf_phdata); - return ~0UL; - } - - retval = read_exec(interpreter_inode, interp_elf_ex->e_phoff, - (char *) elf_phdata, - sizeof(struct elf_phdr) * interp_elf_ex->e_phnum, 1); - - if (retval < 0) { - kfree (elf_phdata); - return retval; - } - - elf_exec_fileno = open_inode(interpreter_inode, O_RDONLY); - if (elf_exec_fileno < 0) { - kfree(elf_phdata); - return ~0UL; - } - - file = current->files->fd[elf_exec_fileno]; - - eppnt = elf_phdata; - for(i=0; ie_phnum; i++, eppnt++) - if (eppnt->p_type == PT_LOAD) { - int elf_type = MAP_PRIVATE | MAP_DENYWRITE; - int elf_prot = 0; - unsigned long vaddr = 0; - unsigned long k; - - if (eppnt->p_flags & PF_R) elf_prot = PROT_READ; - if (eppnt->p_flags & PF_W) elf_prot |= PROT_WRITE; - if (eppnt->p_flags & PF_X) elf_prot |= PROT_EXEC; - if (interp_elf_ex->e_type == ET_EXEC || load_addr_set) { - elf_type |= MAP_FIXED; - vaddr = eppnt->p_vaddr; - } else { - load_addr = get_unmapped_area(0, eppnt->p_filesz + - ELF_PAGEOFFSET(eppnt->p_vaddr)); - } - - error = do_mmap(file, - load_addr + ELF_PAGESTART(vaddr), - eppnt->p_filesz + ELF_PAGEOFFSET(eppnt->p_vaddr), - elf_prot, - elf_type, - eppnt->p_offset - ELF_PAGEOFFSET(eppnt->p_vaddr)); - - if (error > -1024UL) { - /* Real error */ - sys_close(elf_exec_fileno); - kfree(elf_phdata); - return ~0UL; - } - - if (!load_addr_set && interp_elf_ex->e_type == ET_DYN) { - load_addr = error; - load_addr_set = 1; - } - - /* - * Find the end of the file mapping for this phdr, and keep - * track of the largest address we see for this. - */ - k = load_addr + eppnt->p_vaddr + eppnt->p_filesz; - if (k > elf_bss) elf_bss = k; - - /* - * Do the same thing for the memory mapping - between - * elf_bss and last_bss is the bss section. - */ - k = load_addr + eppnt->p_memsz + eppnt->p_vaddr; - if (k > last_bss) last_bss = k; - } - - /* Now use mmap to map the library into memory. */ - - sys_close(elf_exec_fileno); - - /* - * Now fill out the bss section. First pad the last page up - * to the page boundary, and then perform a mmap to make sure - * that there are zeromapped pages up to and including the last - * bss page. - */ - padzero(elf_bss); - elf_bss = ELF_PAGESTART(elf_bss + ELF_EXEC_PAGESIZE - 1); /* What we have mapped so far */ - - /* Map the last of the bss segment */ - if (last_bss > elf_bss) - do_mmap(NULL, elf_bss, last_bss-elf_bss, - PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_FIXED|MAP_PRIVATE, 0); - kfree(elf_phdata); - - *interp_load_addr = load_addr; - return ((unsigned long) interp_elf_ex->e_entry) + load_addr; -} - -static unsigned long load_aout32_interp(struct exec * interp_ex, - struct inode * interpreter_inode) -{ - int retval; - unsigned long elf_entry; - - current->mm->brk = interp_ex->a_bss + - (current->mm->end_data = interp_ex->a_data + - (current->mm->end_code = interp_ex->a_text)); - elf_entry = interp_ex->a_entry; - - - if (N_MAGIC(*interp_ex) == OMAGIC) { - do_mmap(NULL, 0, interp_ex->a_text+interp_ex->a_data, - PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_FIXED|MAP_PRIVATE, 0); - retval = read_exec(interpreter_inode, 32, (char *) 0, - interp_ex->a_text+interp_ex->a_data, 0); - } else if (N_MAGIC(*interp_ex) == ZMAGIC || N_MAGIC(*interp_ex) == QMAGIC) { - do_mmap(NULL, 0, interp_ex->a_text+interp_ex->a_data, - PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_FIXED|MAP_PRIVATE, 0); - retval = read_exec(interpreter_inode, - N_TXTOFF(*interp_ex) , - (char *) N_TXTADDR(*interp_ex), - interp_ex->a_text+interp_ex->a_data, 0); - } else - retval = -1; - - if (retval >= 0) - do_mmap(NULL, ELF_PAGESTART(interp_ex->a_text + interp_ex->a_data + ELF_EXEC_PAGESIZE - 1), - interp_ex->a_bss, - PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_FIXED|MAP_PRIVATE, 0); - if (retval < 0) return ~0UL; - return elf_entry; -} - -/* - * These are the functions used to load ELF style executables and shared - * libraries. There is no binary dependent code anywhere else. - */ - -#define INTERPRETER_NONE 0 -#define INTERPRETER_AOUT 1 -#define INTERPRETER_ELF 2 - -static inline int -do_load_elf32_binary(struct linux_binprm * bprm, struct pt_regs * regs) -{ - struct elfhdr elf_ex; - struct elfhdr interp_elf_ex; - struct file * file; - struct exec interp_ex; - struct inode *interpreter_inode; - unsigned long load_addr; - int load_addr_set = 0; - unsigned int interpreter_type = INTERPRETER_NONE; - unsigned char ibcs2_interpreter; - int i; - int old_fs; - int error; - struct elf_phdr * elf_ppnt, *elf_phdata; - int elf_exec_fileno; - unsigned long elf_bss, k, elf_brk; - int retval; - char * elf_interpreter; - unsigned long elf_entry, interp_load_addr = 0; - int status; - unsigned long start_code, end_code, end_data; - unsigned long elf_stack; - char passed_fileno[6]; - - ibcs2_interpreter = 0; - status = 0; - load_addr = 0; - elf_ex = *((struct elfhdr *) bprm->buf); /* exec-header */ - - if (elf_ex.e_ident[0] != 0x7f || - strncmp(&elf_ex.e_ident[1], "ELF",3) != 0) { - return -ENOEXEC; - } - - - /* First of all, some simple consistency checks */ - if ((elf_ex.e_type != ET_EXEC && - elf_ex.e_type != ET_DYN) || - (! elf_check_arch(elf_ex.e_machine)) || - (!bprm->inode->i_op || !bprm->inode->i_op->default_file_ops || - !bprm->inode->i_op->default_file_ops->mmap)){ - return -ENOEXEC; - } - - /* Now read in all of the header information */ - - elf_phdata = (struct elf_phdr *) kmalloc(elf_ex.e_phentsize * - elf_ex.e_phnum, GFP_KERNEL); - if (elf_phdata == NULL) { - return -ENOMEM; - } - - retval = read_exec(bprm->inode, elf_ex.e_phoff, (char *) elf_phdata, - elf_ex.e_phentsize * elf_ex.e_phnum, 1); - if (retval < 0) { - kfree (elf_phdata); - return retval; - } - - elf_ppnt = elf_phdata; - - elf_bss = 0; - elf_brk = 0; - - elf_exec_fileno = open_inode(bprm->inode, O_RDONLY); - - if (elf_exec_fileno < 0) { - kfree (elf_phdata); - return elf_exec_fileno; - } - - file = current->files->fd[elf_exec_fileno]; - - elf_stack = ~0UL; - elf_interpreter = NULL; - start_code = ~0UL; - end_code = 0; - end_data = 0; - - for(i=0;i < elf_ex.e_phnum; i++){ - if (elf_ppnt->p_type == PT_INTERP) { - if ( elf_interpreter != NULL ) - { - kfree (elf_phdata); - kfree(elf_interpreter); - sys_close(elf_exec_fileno); - return -EINVAL; - } - - /* This is the program interpreter used for - * shared libraries - for now assume that this - * is an a.out format binary - */ - - elf_interpreter = (char *) kmalloc(elf_ppnt->p_filesz, - GFP_KERNEL); - if (elf_interpreter == NULL) { - kfree (elf_phdata); - sys_close(elf_exec_fileno); - return -ENOMEM; - } - - retval = read_exec(bprm->inode,elf_ppnt->p_offset, - elf_interpreter, - elf_ppnt->p_filesz, 1); - /* If the program interpreter is one of these two, - then assume an iBCS2 image. Otherwise assume - a native linux image. */ - if (strcmp(elf_interpreter,"/usr/lib/libc.so.1") == 0 || - strcmp(elf_interpreter,"/usr/lib/ld.so.1") == 0) - ibcs2_interpreter = 1; -#if 0 - printk("Using ELF interpreter %s\n", elf_interpreter); -#endif - if (retval >= 0) { - old_fs = get_fs(); /* This could probably be optimized */ - set_fs(get_ds()); - retval = open_namei(elf_interpreter, 0, 0, - &interpreter_inode, NULL); - set_fs(old_fs); - } - - if (retval >= 0) - retval = read_exec(interpreter_inode,0,bprm->buf,128, 1); - - if (retval >= 0) { - interp_ex = *((struct exec *) bprm->buf); /* exec-header */ - interp_elf_ex = *((struct elfhdr *) bprm->buf); /* exec-header */ - - } - if (retval < 0) { - kfree (elf_phdata); - kfree(elf_interpreter); - sys_close(elf_exec_fileno); - return retval; - } - } - elf_ppnt++; - } - - /* Some simple consistency checks for the interpreter */ - if (elf_interpreter){ - interpreter_type = INTERPRETER_ELF | INTERPRETER_AOUT; - - /* Now figure out which format our binary is */ - if ((N_MAGIC(interp_ex) != OMAGIC) && - (N_MAGIC(interp_ex) != ZMAGIC) && - (N_MAGIC(interp_ex) != QMAGIC)) - interpreter_type = INTERPRETER_ELF; - - if (interp_elf_ex.e_ident[0] != 0x7f || - strncmp(&interp_elf_ex.e_ident[1], "ELF",3) != 0) - interpreter_type &= ~INTERPRETER_ELF; - - if (!interpreter_type) - { - kfree(elf_interpreter); - kfree(elf_phdata); - sys_close(elf_exec_fileno); - return -ELIBBAD; - } - } - - /* OK, we are done with that, now set up the arg stuff, - and then start this sucker up */ - - if (!bprm->sh_bang) { - char * passed_p; - - if (interpreter_type == INTERPRETER_AOUT) { - sprintf(passed_fileno, "%d", elf_exec_fileno); - passed_p = passed_fileno; - - if (elf_interpreter) { - bprm->p = copy_strings(1,&passed_p,bprm->page,bprm->p,2); - bprm->argc++; - } - } - if (!bprm->p) { - if (elf_interpreter) { - kfree(elf_interpreter); - } - kfree (elf_phdata); - sys_close(elf_exec_fileno); - return -E2BIG; - } - } - - /* OK, This is the point of no return */ - flush_old_exec(bprm); - - current->mm->end_data = 0; - current->mm->end_code = 0; - current->mm->start_mmap = ELF_START_MMAP; - current->mm->mmap = NULL; - elf_entry = (unsigned long) elf_ex.e_entry; - - /* Do this so that we can load the interpreter, if need be. We will - change some of these later */ - current->mm->rss = 0; - current->tss.flags |= SPARC_FLAG_32BIT; - bprm->p = setup_arg_pages(bprm->p, bprm); - current->mm->start_stack = bprm->p; - - /* Now we do a little grungy work by mmaping the ELF image into - the correct location in memory. At this point, we assume that - the image should be loaded at fixed address, not at a variable - address. */ - - old_fs = get_fs(); - set_fs(get_ds()); - for(i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; i++, elf_ppnt++) { - if (elf_ppnt->p_type == PT_LOAD) { - int elf_prot = 0; - if (elf_ppnt->p_flags & PF_R) elf_prot |= PROT_READ; - if (elf_ppnt->p_flags & PF_W) elf_prot |= PROT_WRITE; - if (elf_ppnt->p_flags & PF_X) elf_prot |= PROT_EXEC; - - error = do_mmap(file, - ELF_PAGESTART(elf_ppnt->p_vaddr), - (elf_ppnt->p_filesz + - ELF_PAGEOFFSET(elf_ppnt->p_vaddr)), - elf_prot, - (MAP_FIXED | MAP_PRIVATE | - MAP_DENYWRITE | MAP_EXECUTABLE), - (elf_ppnt->p_offset - - ELF_PAGEOFFSET(elf_ppnt->p_vaddr))); - -#ifdef LOW_ELF_STACK - if (ELF_PAGESTART(elf_ppnt->p_vaddr) < elf_stack) - elf_stack = ELF_PAGESTART(elf_ppnt->p_vaddr); -#endif - - if (!load_addr_set) { - load_addr = elf_ppnt->p_vaddr - elf_ppnt->p_offset; - load_addr_set = 1; - } - k = elf_ppnt->p_vaddr; - if (k < start_code) start_code = k; - k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz; - if (k > elf_bss) elf_bss = k; -#if 1 - if ((elf_ppnt->p_flags & PF_X) && end_code < k) -#else - if ( !(elf_ppnt->p_flags & PF_W) && end_code < k) -#endif - end_code = k; - if (end_data < k) end_data = k; - k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz; - if (k > elf_brk) elf_brk = k; - } - } - set_fs(old_fs); - - if (elf_interpreter) { - if (interpreter_type & 1) - elf_entry = load_aout32_interp(&interp_ex, - interpreter_inode); - else if (interpreter_type & 2) - elf_entry = load_elf32_interp(&interp_elf_ex, - interpreter_inode, - &interp_load_addr); - - iput(interpreter_inode); - kfree(elf_interpreter); - - if (elf_entry == ~0UL) { - printk("Unable to load interpreter\n"); - kfree(elf_phdata); - send_sig(SIGSEGV, current, 0); - return 0; - } - } - - kfree(elf_phdata); - - if (interpreter_type != INTERPRETER_AOUT) sys_close(elf_exec_fileno); - current->personality = (ibcs2_interpreter ? PER_SVR4 : PER_LINUX); - - if (current->exec_domain && current->exec_domain->module) - __MOD_DEC_USE_COUNT(current->exec_domain->module); - if (current->binfmt && current->binfmt->module) - __MOD_DEC_USE_COUNT(current->binfmt->module); - current->exec_domain = lookup_exec_domain(current->personality); - current->binfmt = &elf32_format; - if (current->exec_domain && current->exec_domain->module) - __MOD_INC_USE_COUNT(current->exec_domain->module); - if (current->binfmt && current->binfmt->module) - __MOD_INC_USE_COUNT(current->binfmt->module); - -#ifndef VM_STACK_FLAGS - current->executable = bprm->inode; - bprm->inode->i_count++; -#endif -#ifdef LOW_ELF_STACK - current->start_stack = bprm->p = elf_stack - 4; -#endif - current->suid = current->euid = current->fsuid = bprm->e_uid; - current->sgid = current->egid = current->fsgid = bprm->e_gid; - current->flags &= ~PF_FORKNOEXEC; - bprm->p = (unsigned long) - create_elf32_tables((char *)bprm->p, - bprm->argc, - bprm->envc, - (interpreter_type == INTERPRETER_ELF ? &elf_ex : NULL), - load_addr, - interp_load_addr, - (interpreter_type == INTERPRETER_AOUT ? 0 : 1)); - if (interpreter_type == INTERPRETER_AOUT) - current->mm->arg_start += strlen(passed_fileno) + 1; - current->mm->start_brk = current->mm->brk = elf_brk; - current->mm->end_code = end_code; - current->mm->start_code = start_code; - current->mm->end_data = end_data; - current->mm->start_stack = bprm->p; - - /* Calling set_brk effectively mmaps the pages that we need for the bss and break - sections */ - set_brk(elf_bss, elf_brk); - - padzero(elf_bss); - -#if 0 - printk("(start_brk) %x\n" , current->mm->start_brk); - printk("(end_code) %x\n" , current->mm->end_code); - printk("(start_code) %x\n" , current->mm->start_code); - printk("(end_data) %x\n" , current->mm->end_data); - printk("(start_stack) %x\n" , current->mm->start_stack); - printk("(brk) %x\n" , current->mm->brk); -#endif - - if ( current->personality == PER_SVR4 ) - { - /* Why this, you ask??? Well SVr4 maps page 0 as read-only, - and some applications "depend" upon this behavior. - Since we do not have the power to recompile these, we - emulate the SVr4 behavior. Sigh. */ - error = do_mmap(NULL, 0, 4096, PROT_READ | PROT_EXEC, - MAP_FIXED | MAP_PRIVATE, 0); - } - -#ifdef ELF_PLAT_INIT - /* - * The ABI may specify that certain registers be set up in special - * ways (on i386 %edx is the address of a DT_FINI function, for - * example. This macro performs whatever initialization to - * the regs structure is required. - */ - ELF_PLAT_INIT(regs); -#endif - - - start_thread32(regs, elf_entry, bprm->p); - if (current->flags & PF_PTRACED) - send_sig(SIGTRAP, current, 0); - return 0; -} - -static int -load_elf32_binary(struct linux_binprm * bprm, struct pt_regs * regs) -{ - int retval; - - MOD_INC_USE_COUNT; - retval = do_load_elf32_binary(bprm, regs); - MOD_DEC_USE_COUNT; - return retval; -} - -/* This is really simpleminded and specialized - we are loading an - a.out library that is given an ELF header. */ - -static inline int -do_load_elf32_library(int fd){ - struct file * file; - struct elfhdr elf_ex; - struct elf_phdr *elf_phdata = NULL; - struct inode * inode; - unsigned long len; - int elf_bss; - int retval; - unsigned long bss; - int error; - int i,j, k; - - len = 0; - file = current->files->fd[fd]; - inode = file->f_inode; - elf_bss = 0; - - if (!file || !file->f_op) - return -EACCES; - - /* seek to the beginning of the file */ - if (file->f_op->llseek) { - if ((error = file->f_op->llseek(inode, file, 0, 0)) != 0) - return -ENOEXEC; - } else - file->f_pos = 0; - - set_fs(KERNEL_DS); - error = file->f_op->read(inode, file, (char *) &elf_ex, sizeof(elf_ex)); - set_fs(USER_DS); - if (error != sizeof(elf_ex)) - return -ENOEXEC; - - if (elf_ex.e_ident[0] != 0x7f || - strncmp(&elf_ex.e_ident[1], "ELF",3) != 0) - return -ENOEXEC; - - /* First of all, some simple consistency checks */ - if (elf_ex.e_type != ET_EXEC || elf_ex.e_phnum > 2 || - !elf_check_arch(elf_ex.e_machine) || - (!inode->i_op || !inode->i_op->default_file_ops->mmap)) - return -ENOEXEC; - - /* Now read in all of the header information */ - - if (sizeof(struct elf_phdr) * elf_ex.e_phnum > PAGE_SIZE) - return -ENOEXEC; - - elf_phdata = (struct elf_phdr *) - kmalloc(sizeof(struct elf_phdr) * elf_ex.e_phnum, GFP_KERNEL); - if (elf_phdata == NULL) - return -ENOMEM; - - retval = read_exec(inode, elf_ex.e_phoff, (char *) elf_phdata, - sizeof(struct elf_phdr) * elf_ex.e_phnum, 1); - - j = 0; - for(i=0; ip_type == PT_LOAD) j++; - - if (j != 1) { - kfree(elf_phdata); - return -ENOEXEC; - } - - while(elf_phdata->p_type != PT_LOAD) elf_phdata++; - - /* Now use mmap to map the library into memory. */ - error = do_mmap(file, - ELF_PAGESTART(elf_phdata->p_vaddr), - (elf_phdata->p_filesz + - ELF_PAGEOFFSET(elf_phdata->p_vaddr)), - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE, - (elf_phdata->p_offset - - ELF_PAGEOFFSET(elf_phdata->p_vaddr))); - - k = elf_phdata->p_vaddr + elf_phdata->p_filesz; - if (k > elf_bss) elf_bss = k; - - if (error != ELF_PAGESTART(elf_phdata->p_vaddr)) { - kfree(elf_phdata); - return error; - } - - padzero(elf_bss); - - len = ELF_PAGESTART(elf_phdata->p_filesz + elf_phdata->p_vaddr+ ELF_EXEC_PAGESIZE - 1); - bss = elf_phdata->p_memsz + elf_phdata->p_vaddr; - if (bss > len) - do_mmap(NULL, len, bss-len, - PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_FIXED|MAP_PRIVATE, 0); - kfree(elf_phdata); - return 0; -} - -static int load_elf32_library(int fd) -{ - int retval; - - MOD_INC_USE_COUNT; - retval = do_load_elf32_library(fd); - MOD_DEC_USE_COUNT; - return retval; -} - -/* - * Note that some platforms still use traditional core dumps and not - * the ELF core dump. Each platform can select it as appropriate. - */ -#ifdef USE_ELF_CORE_DUMP - -/* - * ELF core dumper - * - * Modelled on fs/exec.c:aout_core_dump() - * Jeremy Fitzhardinge - */ -/* - * These are the only things you should do on a core-file: use only these - * functions to write out all the necessary info. - */ -static int dump_write(struct file *file, const void *addr, int nr) -{ - return file->f_op->write(file->f_inode, file, addr, nr) == nr; -} - -static int dump_seek(struct file *file, off_t off) -{ - if (file->f_op->llseek) { - if (file->f_op->llseek(file->f_inode, file, off, 0) != off) - return 0; - } else - file->f_pos = off; - return 1; -} - -/* - * Decide whether a segment is worth dumping; default is yes to be - * sure (missing info is worse than too much; etc). - * Personally I'd include everything, and use the coredump limit... - * - * I think we should skip something. But I am not sure how. H.J. - */ -static inline int maydump(struct vm_area_struct *vma) -{ - if (!(vma->vm_flags & (VM_READ|VM_WRITE|VM_EXEC))) - return 0; -#if 1 - if (vma->vm_flags & (VM_WRITE|VM_GROWSUP|VM_GROWSDOWN)) - return 1; - if (vma->vm_flags & (VM_READ|VM_EXEC|VM_EXECUTABLE|VM_SHARED)) - return 0; -#endif - return 1; -} - -#define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) - -/* An ELF note in memory */ -struct memelfnote -{ - const char *name; - int type; - unsigned int datasz; - void *data; -}; - -static int notesize(struct memelfnote *en) -{ - int sz; - - sz = sizeof(struct elf_note); - sz += roundup(strlen(en->name), 4); - sz += roundup(en->datasz, 4); - - return sz; -} - -/* #define DEBUG */ - -#define DUMP_WRITE(addr, nr) \ - do { if (!dump_write(file, (addr), (nr))) return 0; } while(0) -#define DUMP_SEEK(off) \ - do { if (!dump_seek(file, (off))) return 0; } while(0) - -static int writenote(struct memelfnote *men, struct file *file) -{ - struct elf_note en; - - en.n_namesz = strlen(men->name); - en.n_descsz = men->datasz; - en.n_type = men->type; - - DUMP_WRITE(&en, sizeof(en)); - DUMP_WRITE(men->name, en.n_namesz); - /* XXX - cast from long long to long to avoid need for libgcc.a */ - DUMP_SEEK(roundup((unsigned long)file->f_pos, 4)); /* XXX */ - DUMP_WRITE(men->data, men->datasz); - DUMP_SEEK(roundup((unsigned long)file->f_pos, 4)); /* XXX */ - - return 1; -} -#undef DUMP_WRITE -#undef DUMP_SEEK - -#define DUMP_WRITE(addr, nr) \ - if (!dump_write(&file, (addr), (nr))) \ - goto close_coredump; -#define DUMP_SEEK(off) \ - if (!dump_seek(&file, (off))) \ - goto close_coredump; -/* - * Actual dumper - * - * This is a two-pass process; first we find the offsets of the bits, - * and then they are actually written out. If we run out of core limit - * we just truncate. - */ -static int elf32_core_dump(long signr, struct pt_regs * regs) -{ - int has_dumped = 0; - struct file file; - struct inode *inode; - unsigned short fs; - char corefile[6+sizeof(current->comm)]; - int segs; - int i; - size_t size; - struct vm_area_struct *vma; - struct elfhdr elf; - off_t offset = 0, dataoff; - int limit = current->rlim[RLIMIT_CORE].rlim_cur; - int numnote = 4; - struct memelfnote notes[4]; - struct elf_prstatus prstatus; /* NT_PRSTATUS */ - elf_fpregset_t fpu; /* NT_PRFPREG */ - struct elf_prpsinfo psinfo; /* NT_PRPSINFO */ - - if (!current->dumpable || limit < PAGE_SIZE || current->mm->count != 1) - return 0; - current->dumpable = 0; - -#ifndef CONFIG_BINFMT_ELF32 - MOD_INC_USE_COUNT; -#endif - - /* Count what's needed to dump, up to the limit of coredump size */ - segs = 0; - size = 0; - for(vma = current->mm->mmap; vma != NULL; vma = vma->vm_next) { - if (maydump(vma)) - { - int sz = vma->vm_end-vma->vm_start; - - if (size+sz >= limit) - break; - else - size += sz; - } - - segs++; - } -#ifdef DEBUG - printk("elf_core_dump: %d segs taking %d bytes\n", segs, size); -#endif - - /* Set up header */ - memcpy(elf.e_ident, ELFMAG, SELFMAG); - elf.e_ident[EI_CLASS] = ELF_CLASS; - elf.e_ident[EI_DATA] = ELF_DATA; - elf.e_ident[EI_VERSION] = EV_CURRENT; - memset(elf.e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD); - - elf.e_type = ET_CORE; - elf.e_machine = ELF_ARCH; - elf.e_version = EV_CURRENT; - elf.e_entry = 0; - elf.e_phoff = sizeof(elf); - elf.e_shoff = 0; - elf.e_flags = 0; - elf.e_ehsize = sizeof(elf); - elf.e_phentsize = sizeof(struct elf_phdr); - elf.e_phnum = segs+1; /* Include notes */ - elf.e_shentsize = 0; - elf.e_shnum = 0; - elf.e_shstrndx = 0; - - fs = get_fs(); - set_fs(KERNEL_DS); - memcpy(corefile,"core.",5); -#if 0 - memcpy(corefile+5,current->comm,sizeof(current->comm)); -#else - corefile[4] = '\0'; -#endif - if (open_namei(corefile,O_CREAT | 2 | O_TRUNC,0600,&inode,NULL)) { - inode = NULL; - goto end_coredump; - } - if (!S_ISREG(inode->i_mode)) - goto end_coredump; - if (!inode->i_op || !inode->i_op->default_file_ops) - goto end_coredump; - file.f_mode = 3; - file.f_flags = 0; - file.f_count = 1; - file.f_inode = inode; - file.f_pos = 0; - file.f_reada = 0; - file.f_op = inode->i_op->default_file_ops; - if (file.f_op->open) - if (file.f_op->open(inode,&file)) - goto end_coredump; - if (!file.f_op->write) - goto close_coredump; - has_dumped = 1; - current->flags |= PF_DUMPCORE; - - DUMP_WRITE(&elf, sizeof(elf)); - offset += sizeof(elf); /* Elf header */ - offset += (segs+1) * sizeof(struct elf_phdr); /* Program headers */ - - /* - * Set up the notes in similar form to SVR4 core dumps made - * with info from their /proc. - */ - memset(&psinfo, 0, sizeof(psinfo)); - memset(&prstatus, 0, sizeof(prstatus)); - - notes[0].name = "CORE"; - notes[0].type = NT_PRSTATUS; - notes[0].datasz = sizeof(prstatus); - notes[0].data = &prstatus; - prstatus.pr_info.si_signo = prstatus.pr_cursig = signr; - prstatus.pr_sigpend = current->signal; - prstatus.pr_sighold = current->blocked; - psinfo.pr_pid = prstatus.pr_pid = current->pid; - psinfo.pr_ppid = prstatus.pr_ppid = current->p_pptr->pid; - psinfo.pr_pgrp = prstatus.pr_pgrp = current->pgrp; - psinfo.pr_sid = prstatus.pr_sid = current->session; - prstatus.pr_utime.tv_sec = CT_TO_SECS(current->utime); - prstatus.pr_utime.tv_usec = CT_TO_USECS(current->utime); - prstatus.pr_stime.tv_sec = CT_TO_SECS(current->stime); - prstatus.pr_stime.tv_usec = CT_TO_USECS(current->stime); - prstatus.pr_cutime.tv_sec = CT_TO_SECS(current->cutime); - prstatus.pr_cutime.tv_usec = CT_TO_USECS(current->cutime); - prstatus.pr_cstime.tv_sec = CT_TO_SECS(current->cstime); - prstatus.pr_cstime.tv_usec = CT_TO_USECS(current->cstime); - - /* - * This transfers the registers from regs into the standard - * coredump arrangement, whatever that is. - */ -#ifdef ELF_CORE_COPY_REGS - ELF_CORE_COPY_REGS(prstatus.pr_reg, regs) -#else - if (sizeof(elf_gregset_t) != sizeof(struct pt_regs)) - { - printk("sizeof(elf_gregset_t) (%ld) != sizeof(struct pt_regs) (%ld)\n", - sizeof(elf_gregset_t), sizeof(struct pt_regs)); - } - else - *(struct pt_regs *)&prstatus.pr_reg = *regs; -#endif - - notes[1].name = "CORE"; - notes[1].type = NT_PRPSINFO; - notes[1].datasz = sizeof(psinfo); - notes[1].data = &psinfo; - psinfo.pr_state = current->state; - psinfo.pr_sname = (current->state < 0 || current->state > 5) ? '.' : "RSDZTD"[current->state]; - psinfo.pr_zomb = psinfo.pr_sname == 'Z'; - psinfo.pr_nice = current->priority-15; - psinfo.pr_flag = current->flags; - psinfo.pr_uid = current->uid; - psinfo.pr_gid = current->gid; - { - int i, len; - - set_fs(fs); - - len = current->mm->arg_end - current->mm->arg_start; - len = len >= ELF_PRARGSZ ? ELF_PRARGSZ : len; - copy_from_user(&psinfo.pr_psargs, - (const char *)current->mm->arg_start, len); - for(i = 0; i < len; i++) - if (psinfo.pr_psargs[i] == 0) - psinfo.pr_psargs[i] = ' '; - psinfo.pr_psargs[len] = 0; - - set_fs(KERNEL_DS); - } - strncpy(psinfo.pr_fname, current->comm, sizeof(psinfo.pr_fname)); - - notes[2].name = "CORE"; - notes[2].type = NT_TASKSTRUCT; - notes[2].datasz = sizeof(*current); - notes[2].data = current; - - /* Try to dump the fpu. */ - prstatus.pr_fpvalid = dump_fpu (regs, &fpu); - if (!prstatus.pr_fpvalid) - { - numnote--; - } - else - { - notes[3].name = "CORE"; - notes[3].type = NT_PRFPREG; - notes[3].datasz = sizeof(fpu); - notes[3].data = &fpu; - } - - /* Write notes phdr entry */ - { - struct elf_phdr phdr; - int sz = 0; - - for(i = 0; i < numnote; i++) - sz += notesize(¬es[i]); - - phdr.p_type = PT_NOTE; - phdr.p_offset = offset; - phdr.p_vaddr = 0; - phdr.p_paddr = 0; - phdr.p_filesz = sz; - phdr.p_memsz = 0; - phdr.p_flags = 0; - phdr.p_align = 0; - - offset += phdr.p_filesz; - DUMP_WRITE(&phdr, sizeof(phdr)); - } - - /* Page-align dumped data */ - dataoff = offset = roundup(offset, PAGE_SIZE); - - /* Write program headers for segments dump */ - for(vma = current->mm->mmap, i = 0; - i < segs && vma != NULL; vma = vma->vm_next) { - struct elf_phdr phdr; - size_t sz; - - i++; - - sz = vma->vm_end - vma->vm_start; - - phdr.p_type = PT_LOAD; - phdr.p_offset = offset; - phdr.p_vaddr = vma->vm_start; - phdr.p_paddr = 0; - phdr.p_filesz = maydump(vma) ? sz : 0; - phdr.p_memsz = sz; - offset += phdr.p_filesz; - phdr.p_flags = vma->vm_flags & VM_READ ? PF_R : 0; - if (vma->vm_flags & VM_WRITE) phdr.p_flags |= PF_W; - if (vma->vm_flags & VM_EXEC) phdr.p_flags |= PF_X; - phdr.p_align = PAGE_SIZE; - - DUMP_WRITE(&phdr, sizeof(phdr)); - } - - for(i = 0; i < numnote; i++) - if (!writenote(¬es[i], &file)) - goto close_coredump; - - set_fs(fs); - - DUMP_SEEK(dataoff); - - for(i = 0, vma = current->mm->mmap; - i < segs && vma != NULL; - vma = vma->vm_next) { - unsigned long addr = vma->vm_start; - unsigned long len = vma->vm_end - vma->vm_start; - - i++; - if (!maydump(vma)) - continue; -#ifdef DEBUG - printk("elf_core_dump: writing %08lx %lx\n", addr, len); -#endif - DUMP_WRITE((void *)addr, len); - } - - if ((off_t) file.f_pos != offset) { - /* Sanity check */ - printk("elf_core_dump: file.f_pos (%ld) != offset (%ld)\n", - (off_t) file.f_pos, offset); - } - - close_coredump: - if (file.f_op->release) - file.f_op->release(inode,&file); - - end_coredump: - set_fs(fs); - iput(inode); -#ifndef CONFIG_BINFMT_ELF32 - MOD_DEC_USE_COUNT; -#endif - return has_dumped; -} -#endif /* USE_ELF_CORE_DUMP */ - -__initfunc(int init_elf32_binfmt(void)) -{ - return register_binfmt(&elf32_format); -} - -#ifdef MODULE - -int init_module(void) -{ - /* Install the COFF, ELF and XOUT loaders. - * N.B. We *rely* on the table being the right size with the - * right number of free slots... - */ - return init_elf32_binfmt(); -} - - -void cleanup_module( void) -{ - /* Remove the COFF and ELF loaders. */ - unregister_binfmt(&elf32_format); -} -#endif diff -u --recursive --new-file v2.1.39/linux/arch/sparc64/kernel/sys_sparc32.c linux/arch/sparc64/kernel/sys_sparc32.c --- v2.1.39/linux/arch/sparc64/kernel/sys_sparc32.c Thu May 15 16:48:02 1997 +++ linux/arch/sparc64/kernel/sys_sparc32.c Sun May 18 17:10:37 1997 @@ -1,4 +1,4 @@ -/* $Id: sys_sparc32.c,v 1.12 1997/05/14 14:50:58 jj Exp $ +/* $Id: sys_sparc32.c,v 1.13 1997/05/18 04:16:44 davem Exp $ * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls. * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -34,9 +34,12 @@ #include #include -/* As gcc will warn about casting u32 to some ptr, we have to cast it to unsigned long first, and that's what is A() for. - * You just do (void *)A(x), instead of having to type (void *)((unsigned long)x) or instead of just (void *)x, which will - * produce warnings */ +/* As gcc will warn about casting u32 to some ptr, we have to cast it to + * unsigned long first, and that's what is A() for. + * You just do (void *)A(x), instead of having to + * type (void *)((unsigned long)x) or instead of just (void *)x, which will + * produce warnings. + */ #define A(x) ((unsigned long)x) extern asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on); diff -u --recursive --new-file v2.1.39/linux/arch/sparc64/kernel/traps.c linux/arch/sparc64/kernel/traps.c --- v2.1.39/linux/arch/sparc64/kernel/traps.c Mon Apr 14 16:28:10 1997 +++ linux/arch/sparc64/kernel/traps.c Sun May 18 17:10:37 1997 @@ -1,4 +1,4 @@ -/* $Id: traps.c,v 1.5 1997/04/14 06:56:55 davem Exp $ +/* $Id: traps.c,v 1.10 1997/05/18 08:42:16 davem Exp $ * arch/sparc/kernel/traps.c * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -22,6 +22,7 @@ #include #include #include +#include /* #define TRAP_DEBUG */ @@ -42,6 +43,8 @@ void syscall_trace_exit(struct pt_regs *regs) { + printk("Syscall return check, reg dump.\n"); + show_regs(regs); } void sparc64_dtlb_fault_handler (void) @@ -116,25 +119,45 @@ show_regs(regs); printk("Instruction DUMP:"); instruction_dump ((unsigned int *) regs->tpc); + while(1) + barrier(); if(regs->tstate & TSTATE_PRIV) do_exit(SIGKILL); do_exit(SIGSEGV); } -void do_illegal_instruction(struct pt_regs *regs, unsigned long pc, unsigned long npc, - unsigned long tstate) +void do_illegal_instruction(struct pt_regs *regs) { + unsigned long pc = regs->tpc; + unsigned long tstate = regs->tstate; + lock_kernel(); if(tstate & TSTATE_PRIV) die_if_kernel("Kernel illegal instruction", regs); -#ifdef TRAP_DEBUG - printk("Ill instr. at pc=%016lx instruction is %08x\n", - regs->tpc, *(unsigned int *)regs->tpc); +#if 1 + { + unsigned int insn; + + printk("Ill instr. at pc=%016lx ", pc); + get_user(insn, ((unsigned int *)pc)); + printk("insn=[%08x]\n", insn); + } #endif current->tss.sig_address = pc; current->tss.sig_desc = SUBSIG_ILLINST; send_sig(SIGILL, current, 1); unlock_kernel(); + + while(1) + barrier(); +} + +void do_mna(struct pt_regs *regs) +{ + printk("AIEEE: do_mna at %016lx\n", regs->tpc); + show_regs(regs); + while(1) + barrier(); } void do_priv_instruction(struct pt_regs *regs, unsigned long pc, unsigned long npc, diff -u --recursive --new-file v2.1.39/linux/arch/sparc64/kernel/ttable.S linux/arch/sparc64/kernel/ttable.S --- v2.1.39/linux/arch/sparc64/kernel/ttable.S Mon Apr 14 16:28:10 1997 +++ linux/arch/sparc64/kernel/ttable.S Sun May 18 17:10:37 1997 @@ -1,4 +1,4 @@ -/* $Id: ttable.S,v 1.11 1997/03/25 09:47:21 davem Exp $ +/* $Id: ttable.S,v 1.12 1997/05/17 08:22:30 davem Exp $ * ttable.S: Sparc V9 Trap Table(s) with SpitFire extensions. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -13,7 +13,7 @@ tl0_resv009: BTRAP(0x9) tl0_iae: TRAP(do_iae) tl0_resv00b: BTRAP(0xb) BTRAP(0xc) BTRAP(0xd) BTRAP(0xe) BTRAP(0xf) -tl0_ill: TRAP(do_ill) +tl0_ill: TRAP(do_illegal_instruction) tl0_privop: TRAP(do_privop) tl0_resv012: BTRAP(0x12) BTRAP(0x13) BTRAP(0x14) BTRAP(0x15) BTRAP(0x16) BTRAP(0x17) tl0_resv018: BTRAP(0x18) BTRAP(0x19) BTRAP(0x1a) BTRAP(0x1b) BTRAP(0x1c) BTRAP(0x1d) @@ -226,27 +226,3 @@ tl1_f5o: FILL_5_OTHER tl1_f6o: FILL_6_OTHER tl1_f7o: FILL_7_OTHER - -#if 0 -/* Unless we are going to have software trap insns in the kernel code, we - * don't need this. For now we just save 8KB. - */ - -#define BTRAPSTL1(x) BTRAPTL1(x) BTRAPTL1(x+1) BTRAPTL1(x+2) BTRAPTL1(x+3) BTRAPTL1(x+4) BTRAPTL1(x+5) BTRAPTL1(x+6) BTRAPTL1(x+7) - -tl1_sunos: BTRAPTL1(0x100) -tl1_bkpt: BREAKPOINT_TRAP -tl1_resv102: BTRAPTL1(0x102) -tl1_flushw: FLUSH_WINDOW_TRAP -tl1_resv104: BTRAPTL1(0x104) BTRAPTL1(0x105) BTRAPTL1(0x106) -tl1_resv107: BTRAPTL1(0x107) BTRAPTL1(0x108) BTRAPTL1(0x109) BTRAPTL1(0x10a) -tl1_resv10b: BTRAPTL1(0x10b) BTRAPTL1(0x10c) BTRAPTL1(0x10d) BTRAPTL1(0x10e) -tl1_resv10f: BTRAPTL1(0x10f) -tl1_resv110: BTRAPSTL1(0x110) BTRAPSTL1(0x118) -tl1_resv120: BTRAPSTL1(0x120) BTRAPSTL1(0x128) -tl1_resv130: BTRAPSTL1(0x130) BTRAPSTL1(0x138) -tl1_resv140: BTRAPSTL1(0x140) BTRAPSTL1(0x148) -tl1_resv150: BTRAPSTL1(0x150) BTRAPSTL1(0x158) -tl1_resv160: BTRAPSTL1(0x160) BTRAPSTL1(0x168) -tl1_resv170: BTRAPSTL1(0x170) BTRAPSTL1(0x178) -#endif diff -u --recursive --new-file v2.1.39/linux/arch/sparc64/kernel/winfixup.S linux/arch/sparc64/kernel/winfixup.S --- v2.1.39/linux/arch/sparc64/kernel/winfixup.S Wed Dec 31 16:00:00 1969 +++ linux/arch/sparc64/kernel/winfixup.S Sun May 18 17:10:37 1997 @@ -0,0 +1,101 @@ +/* $Id: winfixup.S,v 1.3 1997/05/18 22:52:26 davem Exp $ + * + * winfixup.S: Handle cases where user stack pointer is found to be bogus. + * + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + */ + +#include +#include +#include +#include +#include +#include + + .text + .align 32 + + /* Here are the rules, pay attention. + * + * The kernel is disallowed from touching user space while + * the trap level is greater than zero, except for from within + * the window spill/fill handlers. This must be followed + * so that we can easily detect the case where we tried to + * spill/fill with a bogus (or unmapped) user stack pointer. + * + * These are layed out in a special way for cache reasons, + * don't touch... + */ + .globl winfix_trampoline, fill_fixup, spill_fixup +fill_fixup: + ba,pt %xcc, etrap + rd %pc, %g7 + mov %l5, %o4 + mov %l4, %o5 + srlx %l5, PAGE_SHIFT, %o3 + clr %o1 + sllx %o3, PAGE_SHIFT, %o3 + and %l4, 0x4, %o2 + + call do_sparc64_fault + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + ba,a,pt %xcc, rtrap + nop +winfix_trampoline: + andn %g5, 0x7f, %g5 + add %g5, 0x7c, %g5 + wrpr %g5, %tnpc + done + +spill_fixup: + rd %pic, %g1 + ldx [%g1 + AOFF_task_tss + AOFF_thread_w_saved], %g2 + sll %g2, 3, %g5 + ldx [%g1 + AOFF_task_tss + AOFF_thread_flags], %g7 + add %g1, %g5, %g5 + andcc %g7, SPARC_FLAG_32BIT, %g0 + stx %sp, [%g5 + AOFF_task_tss + AOFF_thread_rwbuf_stkptrs] + sll %g2, 5, %g5 + + bne,pt %xcc, 1f + add %g1, %g5, %g5 + stx %l0, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x00] + stx %l1, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x08] + stx %l2, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x10] + stx %l3, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x18] + stx %l4, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x20] + stx %l5, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x28] + + stx %l6, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x30] + stx %l7, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x38] + stx %i0, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x40] + stx %i1, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x48] + stx %i2, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x50] + stx %i3, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x58] + stx %i4, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x60] + stx %i5, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x68] + + stx %i6, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x70] + stx %i7, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x78] + b,a,pt %xcc, 2f + add %g2, 1, %g2 +1: + std %l0, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x00] + std %l2, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x08] + std %l4, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x10] + std %l6, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x18] + + std %i0, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x20] + std %i2, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x28] + std %i4, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x30] + std %i6, [%g5 + AOFF_task_tss + AOFF_thread_reg_window + 0x38] + add %g2, 1, %g2 +2: + stx %g2, [%g1 + AOFF_task_tss + AOFF_thread_w_saved] + rdpr %tstate, %g1 + nop + + andcc %g1, TSTATE_PRIV, %g0 + be,pn %xcc, fill_fixup + saved + retry diff -u --recursive --new-file v2.1.39/linux/arch/sparc64/lib/blockops.S linux/arch/sparc64/lib/blockops.S --- v2.1.39/linux/arch/sparc64/lib/blockops.S Mon Apr 14 16:28:10 1997 +++ linux/arch/sparc64/lib/blockops.S Sun May 18 17:10:37 1997 @@ -1,4 +1,4 @@ -/* $Id: blockops.S,v 1.5 1997/03/26 18:34:28 jj Exp $ +/* $Id: blockops.S,v 1.6 1997/05/18 04:16:49 davem Exp $ * arch/sparc64/lib/blockops.S: UltraSparc block zero optimized routines. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -31,37 +31,8 @@ .text .align 4 - .globl bzero_2page, bzero_1page -bzero_2page: - /* %o0 = buf */ - mov %o0, %o1 - wr %g0, ASI_BLK_P, %asi - mov 0x10, %g2 - - membar #Sync|#StoreLoad - - fzero %f48 - fzero %f50 - fzero %f52 - fzero %f54 - fzero %f56 - fzero %f58 - fzero %f60 - fzero %f62 -1: - BLAST_BLOCK(%o0, 0x000) - BLAST_BLOCK(%o0, 0x100) - BLAST_BLOCK(%o0, 0x200) - BLAST_BLOCK(%o0, 0x300) - subcc %g2, 1, %g2 - bne,pt %icc, 1b - add %o0, 0x400, %o0 - - membar #Sync|#LoadStore|#StoreStore - - retl - mov %o1, %o0 - +#if 0 + .globl bzero_1page bzero_1page: /* %o0 = buf */ mov %o0, %o1 @@ -89,9 +60,36 @@ retl mov %o1, %o0 +#endif .globl __bfill64 __bfill64: +#if 1 + /* %o0 = buf, %o1 = 64-bit pattern */ +#define FILL_BLOCK(buf, offset) \ + stx %o1, [buf + offset + 0x38]; \ + stx %o1, [buf + offset + 0x30]; \ + stx %o1, [buf + offset + 0x28]; \ + stx %o1, [buf + offset + 0x20]; \ + stx %o1, [buf + offset + 0x18]; \ + stx %o1, [buf + offset + 0x10]; \ + stx %o1, [buf + offset + 0x08]; \ + stx %o1, [buf + offset + 0x00]; + + mov 0x20, %g2 +1: + FILL_BLOCK(%o0, 0x00) + FILL_BLOCK(%o0, 0x40) + FILL_BLOCK(%o0, 0x80) + FILL_BLOCK(%o0, 0xc0) + subcc %g2, 1, %g2 + bne,pt %icc, 1b + add %o0, 0x100, %o0 + retl + nop +#undef FILL_BLOCK + +#else /* %o0 = buf */ stx %o1, [%sp + 0x7ff + 128] wr %g0, ASI_BLK_P, %asi @@ -116,7 +114,9 @@ retl membar #Sync|#LoadStore|#StoreStore +#endif +#if 0 .globl __copy_1page __copy_1page: /* %o0 = dst, %o1 = src */ @@ -135,4 +135,4 @@ retl membar #Sync|#LoadStore|#StoreStore - +#endif diff -u --recursive --new-file v2.1.39/linux/arch/sparc64/lib/strlen_user.S linux/arch/sparc64/lib/strlen_user.S --- v2.1.39/linux/arch/sparc64/lib/strlen_user.S Thu Mar 27 14:40:01 1997 +++ linux/arch/sparc64/lib/strlen_user.S Sun May 18 17:10:37 1997 @@ -8,6 +8,8 @@ * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ +#include + #define LO_MAGIC 0x01010101 #define HI_MAGIC 0x80808080 @@ -19,21 +21,21 @@ be,pt %icc, 9f sethi %hi(HI_MAGIC), %o4 10: - ldub [%o0], %o5 + lduba [%o0] ASI_S, %o5 brz,pn %o5, 21f add %o0, 1, %o0 andcc %o0, 3, %g0 be,pn %icc, 4f or %o4, %lo(HI_MAGIC), %o3 11: - ldub [%o0], %o5 + lduba [%o0] ASI_S, %o5 brz,pn %o5, 22f add %o0, 1, %o0 andcc %o0, 3, %g0 be,pt %icc, 5f sethi %hi(LO_MAGIC), %o4 12: - ldub [%o0], %o5 + lduba [%o0] ASI_S, %o5 brz,pn %o5, 23f add %o0, 1, %o0 ba,pt %icc, 13f @@ -45,7 +47,7 @@ 5: or %o4, %lo(LO_MAGIC), %o2 13: - ld [%o0], %o5 + lda [%o0] ASI_S, %o5 2: sub %o5, %o2, %o4 andcc %o4, %o3, %g0 @@ -68,7 +70,7 @@ andcc %o5, 0xff, %g0 bne,a,pt %icc, 2b 14: - ld [%o0], %o5 + lda [%o0] ASI_S, %o5 add %o4, 1, %o4 1: retl diff -u --recursive --new-file v2.1.39/linux/arch/sparc64/lib/strncpy_from_user.S linux/arch/sparc64/lib/strncpy_from_user.S --- v2.1.39/linux/arch/sparc64/lib/strncpy_from_user.S Thu Mar 27 14:40:01 1997 +++ linux/arch/sparc64/lib/strncpy_from_user.S Sun May 18 17:10:37 1997 @@ -24,14 +24,14 @@ sub %g0, %o2, %o3 add %o0, %o2, %o0 10: - ldub [%o1 + %o3], %o4 + lduba [%o1 + %o3] ASI_S, %o4 1: brz,pn %o4, 2f stb %o4, [%o0 + %o3] addcc %o3, 1, %o3 bne,pt %xcc, 1b 11: - ldub [%o1 + %o3], %o4 + lduba [%o1 + %o3] ASI_S, %o4 retl mov %o2, %o0 2: diff -u --recursive --new-file v2.1.39/linux/arch/sparc64/mm/fault.c linux/arch/sparc64/mm/fault.c --- v2.1.39/linux/arch/sparc64/mm/fault.c Thu May 15 16:48:02 1997 +++ linux/arch/sparc64/mm/fault.c Sun May 18 17:10:37 1997 @@ -1,4 +1,4 @@ -/* $Id: fault.c,v 1.5 1997/05/15 21:14:31 davem Exp $ +/* $Id: fault.c,v 1.8 1997/05/18 04:16:52 davem Exp $ * arch/sparc64/mm/fault.c: Page fault handlers for the 64-bit Sparc. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -134,8 +134,11 @@ return 0; } +/* #define FAULT_TRACER */ + asmlinkage void do_sparc64_fault(struct pt_regs *regs, int text_fault, int write, - unsigned long address) + unsigned long address, unsigned long tag, + unsigned long sfsr) { struct vm_area_struct *vma; struct task_struct *tsk = current; @@ -143,7 +146,19 @@ unsigned long fixup; unsigned long g2; int from_user = !(regs->tstate & TSTATE_PRIV); - +#ifdef FAULT_TRACER + static unsigned long last_addr = 0; + static int rcnt = 0; + + printk("do_sparc64_fault(PC[%016lx],t[%d],w[%d],addr[%016lx]tag[%016lx]" + "sfar[%016lx])\n", regs->tpc, text_fault, write, address, tag, sfsr); + if(address == last_addr && rcnt++ > 5) { + printk("Wheee lotsa bogus faults, something wrong, spinning\n"); + while(1) + barrier(); + } + last_addr = address; +#endif lock_kernel (); down(&mm->mmap_sem); vma = find_vma(mm, address); diff -u --recursive --new-file v2.1.39/linux/arch/sparc64/mm/init.c linux/arch/sparc64/mm/init.c --- v2.1.39/linux/arch/sparc64/mm/init.c Tue May 13 22:41:04 1997 +++ linux/arch/sparc64/mm/init.c Sun May 18 17:10:37 1997 @@ -1,4 +1,4 @@ -/* $Id: init.c,v 1.25 1997/04/28 14:57:11 davem Exp $ +/* $Id: init.c,v 1.28 1997/05/18 04:16:53 davem Exp $ * arch/sparc64/mm/init.c * * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu) @@ -457,16 +457,18 @@ pte_clear(ptep); } -#ifdef DEBUG_MMU void sparc_ultra_dump_itlb(void) { int slot; - prom_printf ("Contents of itlb:\n"); - for (slot = 0; slot < 64; slot+=2) { - prom_printf ("%2x:%016lx,%016lx %2x:%016lx,%016lx\n", + printk ("Contents of itlb: "); + for (slot = 0; slot < 14; slot++) printk (" "); + printk ("%2x:%016lx,%016lx\n", 0, spitfire_get_itlb_tag(0), spitfire_get_itlb_data(0)); + for (slot = 1; slot < 64; slot+=3) { + printk ("%2x:%016lx,%016lx %2x:%016lx,%016lx %2x:%016lx,%016lx\n", slot, spitfire_get_itlb_tag(slot), spitfire_get_itlb_data(slot), - slot+1, spitfire_get_itlb_tag(slot+1), spitfire_get_itlb_data(slot+1)); + slot+1, spitfire_get_itlb_tag(slot+1), spitfire_get_itlb_data(slot+1), + slot+2, spitfire_get_itlb_tag(slot+2), spitfire_get_itlb_data(slot+2)); } } @@ -474,14 +476,16 @@ { int slot; - prom_printf ("Contents of dtlb:\n"); - for (slot = 0; slot < 64; slot+=2) { - prom_printf ("%2x:%016lx,%016lx %2x:%016lx,%016lx\n", + printk ("Contents of dtlb: "); + for (slot = 0; slot < 14; slot++) printk (" "); + printk ("%2x:%016lx,%016lx\n", 0, spitfire_get_dtlb_tag(0), spitfire_get_dtlb_data(0)); + for (slot = 1; slot < 64; slot+=3) { + printk ("%2x:%016lx,%016lx %2x:%016lx,%016lx %2x:%016lx,%016lx\n", slot, spitfire_get_dtlb_tag(slot), spitfire_get_dtlb_data(slot), - slot+1, spitfire_get_dtlb_tag(slot+1), spitfire_get_dtlb_data(slot+1)); + slot+1, spitfire_get_dtlb_tag(slot+1), spitfire_get_dtlb_data(slot+1), + slot+2, spitfire_get_dtlb_tag(slot+2), spitfire_get_dtlb_data(slot+2)); } } -#endif /* paging_init() sets up the page tables */ @@ -643,7 +647,7 @@ high_memory = (void *) end_mem; start_mem = PAGE_ALIGN(start_mem); - num_physpages = (start_mem - phys_base - PAGE_OFFSET) >> PAGE_SHIFT; + num_physpages = (start_mem - PAGE_OFFSET) >> PAGE_SHIFT; addr = PAGE_OFFSET; while(addr < start_mem) { @@ -707,9 +711,14 @@ addr = (unsigned long)(&__init_begin); for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) { - mem_map[MAP_NR(addr)].flags &= ~(1 << PG_reserved); - atomic_set(&mem_map[MAP_NR(addr)].count, 1); - free_page(addr); + unsigned long page = addr; + + if(page < ((unsigned long)__va(phys_base))) + page += phys_base; + + mem_map[MAP_NR(page)].flags &= ~(1 << PG_reserved); + atomic_set(&mem_map[MAP_NR(page)].count, 1); + free_page(page); } } diff -u --recursive --new-file v2.1.39/linux/drivers/block/Makefile linux/drivers/block/Makefile --- v2.1.39/linux/drivers/block/Makefile Sat Dec 21 04:24:02 1996 +++ linux/drivers/block/Makefile Sun May 18 17:10:37 1997 @@ -30,6 +30,22 @@ endif endif +ifeq ($(CONFIG_ATARI_ACSI),y) + LX_OBJS += acsi.o +else + ifeq ($(CONFIG_ATARI_ACSI),m) + MX_OBJS += acsi.o + endif +endif + +ifeq ($(CONFIG_ATARI_SLM),y) + L_OBJS += acsi_slm.o +else + ifeq ($(CONFIG_ATARI_SLM),m) + M_OBJS += acsi_slm.o + endif +endif + ifeq ($(CONFIG_AMIGA_Z2RAM),y) L_OBJS += z2ram.o else diff -u --recursive --new-file v2.1.39/linux/drivers/block/genhd.c linux/drivers/block/genhd.c --- v2.1.39/linux/drivers/block/genhd.c Tue May 13 22:41:04 1997 +++ linux/drivers/block/genhd.c Sun May 18 17:10:37 1997 @@ -573,12 +573,12 @@ spc = be16_to_cpu(label->ntrks) * be16_to_cpu(label->nsect); for(i=0; i < 8; i++, p++) { unsigned long st_sector; + int num_sectors; - /* We register all partitions, even if zero size, so that - * the minor numbers end up ok as per SunOS interpretation. - */ st_sector = first_sector + be32_to_cpu(p->start_cylinder) * spc; - add_partition(hd, current_minor, st_sector, be32_to_cpu(p->num_sectors)); + num_sectors = be32_to_cpu(p->num_sectors); + if (num_sectors) + add_partition(hd, current_minor, st_sector, num_sectors); current_minor++; } printk("\n"); @@ -592,10 +592,10 @@ #include #include -static __inline__ __u32 -checksum_block(__u32 *m, int size) +static __inline__ u32 +checksum_block(u32 *m, int size) { - __u32 sum = 0; + u32 sum = 0; while (size--) sum += htonl(*m++); diff -u --recursive --new-file v2.1.39/linux/drivers/cdrom/mcdx.c linux/drivers/cdrom/mcdx.c --- v2.1.39/linux/drivers/cdrom/mcdx.c Tue May 13 22:41:06 1997 +++ linux/drivers/cdrom/mcdx.c Tue May 20 19:59:56 1997 @@ -359,6 +359,10 @@ msf.cdmsf_sec1 = uint2bcd(msf.cdmsf_sec1); msf.cdmsf_frame1 = uint2bcd(msf.cdmsf_frame1); + stuffp->stop.dt.minute = msf.cdmsf_min1; + stuffp->stop.dt.second = msf.cdmsf_sec1; + stuffp->stop.dt.frame = msf.cdmsf_frame1; + return mcdx_playmsf(stuffp, &msf); } diff -u --recursive --new-file v2.1.39/linux/drivers/char/Makefile linux/drivers/char/Makefile --- v2.1.39/linux/drivers/char/Makefile Tue May 13 22:41:06 1997 +++ linux/drivers/char/Makefile Wed May 21 09:57:45 1997 @@ -39,7 +39,10 @@ ifndef CONFIG_SUN_KEYBOARD ifdef CONFIG_VT -L_OBJS += keyboard.o defkeymap.o +L_OBJS += keyboard.o +endif +ifneq ($(ARCH),m68k) +L_OBJS += pc_keyb.o defkeymap.o endif endif diff -u --recursive --new-file v2.1.39/linux/drivers/char/keyb_m68k.c linux/drivers/char/keyb_m68k.c --- v2.1.39/linux/drivers/char/keyb_m68k.c Tue May 13 22:41:07 1997 +++ linux/drivers/char/keyb_m68k.c Wed Dec 31 16:00:00 1969 @@ -1,876 +0,0 @@ -/* - * linux/drivers/char/keyboard.c - * - * Keyboard driver for Linux v0.99 using Latin-1. - * - * Written for linux by Johan Myreen as a translation from - * the assembly version by Linus (with diacriticals added) - * - * Some additional features added by Christoph Niemann (ChN), March 1993 - * - * Loadable keymaps by Risto Kankkunen, May 1993 - * - * Diacriticals redone & other small changes, aeb@cwi.nl, June 1993 - * Added decr/incr_console, dynamic keymaps, Unicode support, - * dynamic function/string keys, led setting, Sept 1994 - * `Sticky' modifier keys, 951006. - * - */ - -/* - * modified to provide 'generic' keyboard support by Hamish Macdonald - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "kbd_kern.h" -#include "diacr.h" -#include "vt_kern.h" - -#define SIZE(x) (sizeof(x)/sizeof((x)[0])) - -#define KBD_REPORT_ERR -#define KBD_REPORT_UNKN -/* #define KBD_IS_FOCUS_9000 */ - -#ifndef KBD_DEFMODE -#define KBD_DEFMODE ((1 << VC_REPEAT) | (1 << VC_META)) -#endif - -#ifndef KBD_DEFLEDS -/* - * Some laptops take the 789uiojklm,. keys as number pad when NumLock - * is on. This seems a good reason to start with NumLock off. - */ -#define KBD_DEFLEDS 0 -#endif - -#ifndef KBD_DEFLOCK -#define KBD_DEFLOCK 0 -#endif - -/* - * The default IO slowdown is doing 'inb()'s from 0x61, which should be - * safe. But as that is the keyboard controller chip address, we do our - * slowdowns here by doing short jumps: the keyboard controller should - * be able to keep up - */ -#define REALLY_SLOW_IO -#define SLOW_IO_BY_JUMPING -#include -#include - -extern void poke_blanked_console(void); -extern void ctrl_alt_del(void); -extern void reset_vc(unsigned int new_console); -extern void scrollback(int); -extern void scrollfront(int); -extern int vc_cons_allocated(unsigned int); - -unsigned char kbd_read_mask = 0x01; /* modified by psaux.c */ - -struct wait_queue * keypress_wait = NULL; - -void keyboard_wait_for_keypress(void) -{ - sleep_on(&keypress_wait); -} - -/* - * global state includes the following, and various static variables - * in this module: prev_scancode, shift_state, diacr, npadch, dead_key_next. - * (last_console is now a global variable) - */ - -/* shift state counters.. */ -static unsigned char k_down[NR_SHIFT] = {0, }; -/* keyboard key bitmap */ -#define BITS_PER_LONG (8*sizeof(unsigned long)) -static unsigned long key_down[256/BITS_PER_LONG] = { 0, }; - -static int dead_key_next = 0; -/* - * In order to retrieve the shift_state (for the mouse server), either - * the variable must be global, or a new procedure must be created to - * return the value. I chose the former way. - */ -/*static*/ int shift_state = 0; -static int npadch = -1; /* -1 or number assembled on pad */ -static unsigned char diacr = 0; -static char rep = 0; /* flag telling character repeat */ -struct kbd_struct kbd_table[MAX_NR_CONSOLES]; -static struct tty_struct **ttytab; -static struct kbd_struct *kbd = kbd_table; -static struct tty_struct *tty = NULL; - -extern void compute_shiftstate(void); - -typedef void (*k_hand)(unsigned char value, char up_flag); -typedef void (k_handfn)(unsigned char value, char up_flag); - -static k_handfn - do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift, - do_meta, do_ascii, do_lock, do_lowercase, do_slock, do_ignore; - -static k_hand key_handler[16] = { - do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift, - do_meta, do_ascii, do_lock, do_lowercase, do_slock, - do_ignore, do_ignore, do_ignore -}; - -typedef void (*void_fnp)(void); -typedef void (void_fn)(void); - -static void_fn do_null, enter, show_ptregs, send_intr, lastcons, caps_toggle, - num, hold, scroll_forw, scroll_back, boot_it, caps_on, compose, - SAK, decr_console, incr_console, spawn_console, bare_num; - -static void_fnp spec_fn_table[] = { - do_null, enter, show_ptregs, show_mem, - show_state, send_intr, lastcons, caps_toggle, - num, hold, scroll_forw, scroll_back, - boot_it, caps_on, compose, SAK, - decr_console, incr_console, spawn_console, bare_num -}; - -/* maximum values each key_handler can handle */ -const int max_vals[] = { - 255, SIZE(func_table) - 1, SIZE(spec_fn_table) - 1, NR_PAD - 1, - NR_DEAD - 1, 255, 3, NR_SHIFT - 1, - 255, NR_ASCII - 1, NR_LOCK - 1, 255, - NR_LOCK - 1 -}; - -const int NR_TYPES = SIZE(max_vals); - -static void put_queue(int); -static unsigned char handle_diacr(unsigned char); - -/* - * Many other routines do put_queue, but I think either - * they produce ASCII, or they produce some user-assigned - * string, and in both cases we might assume that it is - * in utf-8 already. - */ -void to_utf8(ushort c) { - if (c < 0x80) - put_queue(c); /* 0******* */ - else if (c < 0x800) { - put_queue(0xc0 | (c >> 6)); /* 110***** 10****** */ - put_queue(0x80 | (c & 0x3f)); - } else { - put_queue(0xe0 | (c >> 12)); /* 1110**** 10****** 10****** */ - put_queue(0x80 | ((c >> 6) & 0x3f)); - put_queue(0x80 | (c & 0x3f)); - } - /* UTF-8 is defined for words of up to 31 bits, - but we need only 16 bits here */ -} - -void process_keycode (int keycode) -{ - char up_flag; /* 0 or 0200 */ - char raw_mode; - - do_poke_blanked_console = 1; - mark_bh(KEYBOARD_BH); - add_keyboard_randomness(keycode); - - tty = ttytab[fg_console]; - kbd = kbd_table + fg_console; - if ((raw_mode = (kbd->kbdmode == VC_RAW))) { - put_queue(keycode); - /* we do not return yet, because we want to maintain - the key_down array, so that we have the correct - values when finishing RAW mode or when changing VT's */ - } - - /* - * At this point the variable `keycode' contains the keycode. - * Note: the keycode must not be 0. - * We keep track of the up/down status of the key, and - * return the keycode if in MEDIUMRAW mode. - */ - - up_flag = (keycode & 0200); - keycode &= 0x7f; - if (up_flag) { - rep = 0; - if(!test_and_clear_bit(keycode, key_down)) { - /* unexpected, but this can happen: - maybe this was a key release for a FOCUS 9000 - PF key; if we want to see it, we have to clear - up_flag */ -#ifndef __mc68000__ - if (keycode >= SC_LIM || keycode == 85) - up_flag = 0; -#endif - } - } else - rep = test_and_set_bit(keycode, key_down); - - if (raw_mode) - return; - - if (kbd->kbdmode == VC_MEDIUMRAW) { - /* soon keycodes will require more than one byte */ - put_queue(keycode + up_flag); - return; - } - - /* - * Small change in philosophy: earlier we defined repetition by - * rep = keycode == prev_keycode; - * prev_keycode = keycode; - * but now by the fact that the depressed key was down already. - * Does this ever make a difference? Yes. - */ - - /* - * Repeat a key only if the input buffers are empty or the - * characters get echoed locally. This makes key repeat usable - * with slow applications and under heavy loads. - */ - if (!rep || - (vc_kbd_mode(kbd,VC_REPEAT) && tty && - (L_ECHO(tty) || (tty->driver.chars_in_buffer(tty) == 0)))) { - u_short keysym; - u_char type; - - /* the XOR below used to be an OR */ - int shift_final = shift_state ^ kbd->lockstate ^ kbd->slockstate; - ushort *key_map = key_maps[shift_final]; - - if (key_map != NULL) { - keysym = key_map[keycode]; - type = KTYP(keysym); - - if (type >= 0xf0) { - type -= 0xf0; - if (type == KT_LETTER) { - type = KT_LATIN; - if (vc_kbd_led(kbd, VC_CAPSLOCK)) { - key_map = key_maps[shift_final ^ (1<slockstate = 0; - } else { - /* maybe only if (kbd->kbdmode == VC_UNICODE) ? */ - if (!up_flag) - to_utf8(keysym); - } - } else { - /* maybe beep? */ - /* we have at least to update shift_state */ -#if 1 /* how? two almost equivalent choices follow */ - compute_shiftstate(); -#else - keysym = U(plain_map[keycode]); - type = KTYP(keysym); - if (type == KT_SHIFT) - (*key_handler[type])(keysym & 0xff, up_flag); -#endif - } - } -} - -static void put_queue(int ch) -{ - wake_up(&keypress_wait); - if (tty) { - tty_insert_flip_char(tty, ch, 0); - tty_schedule_flip(tty); - } -} - -static void puts_queue(char *cp) -{ - wake_up(&keypress_wait); - if (!tty) - return; - - while (*cp) { - tty_insert_flip_char(tty, *cp, 0); - cp++; - } - tty_schedule_flip(tty); -} - -static void applkey(int key, char mode) -{ - static char buf[] = { 0x1b, 'O', 0x00, 0x00 }; - - buf[1] = (mode ? 'O' : '['); - buf[2] = key; - puts_queue(buf); -} - -static void enter(void) -{ - put_queue(13); - if (vc_kbd_mode(kbd,VC_CRLF)) - put_queue(10); -} - -static void caps_toggle(void) -{ - if (rep) - return; - chg_vc_kbd_led(kbd, VC_CAPSLOCK); -} - -static void caps_on(void) -{ - if (rep) - return; - set_vc_kbd_led(kbd, VC_CAPSLOCK); -} - -struct pt_regs *pt_regs; - -static void show_ptregs(void) -{ - if (pt_regs) - show_regs(pt_regs); - return; -} - -static void hold(void) -{ - if (rep || !tty) - return; - - /* - * Note: SCROLLOCK will be set (cleared) by stop_tty (start_tty); - * these routines are also activated by ^S/^Q. - * (And SCROLLOCK can also be set by the ioctl KDSKBLED.) - */ - if (tty->stopped) - start_tty(tty); - else - stop_tty(tty); -} - -static void num(void) -{ - if (vc_kbd_mode(kbd,VC_APPLIC)) - applkey('P', 1); - else - bare_num(); -} - -/* - * Bind this to Shift-NumLock if you work in application keypad mode - * but want to be able to change the NumLock flag. - * Bind this to NumLock if you prefer that the NumLock key always - * changes the NumLock flag. - */ -static void bare_num(void) -{ - if (!rep) - chg_vc_kbd_led(kbd,VC_NUMLOCK); -} - -static void lastcons(void) -{ - /* switch to the last used console, ChN */ - set_console(last_console); -} - -static void decr_console(void) -{ - int i; - - for (i = fg_console-1; i != fg_console; i--) { - if (i == -1) - i = MAX_NR_CONSOLES-1; - if (vc_cons_allocated(i)) - break; - } - set_console(i); -} - -static void incr_console(void) -{ - int i; - - for (i = fg_console+1; i != fg_console; i++) { - if (i == MAX_NR_CONSOLES) - i = 0; - if (vc_cons_allocated(i)) - break; - } - set_console(i); -} - -static void send_intr(void) -{ - if (!tty) - return; - tty_insert_flip_char(tty, 0, TTY_BREAK); - tty_schedule_flip(tty); -} - -static void scroll_forw(void) -{ - scrollfront(0); -} - -static void scroll_back(void) -{ - scrollback(0); -} - -static void boot_it(void) -{ - ctrl_alt_del(); -} - -static void compose(void) -{ - dead_key_next = 1; -} - -int spawnpid, spawnsig; - -static void spawn_console(void) -{ - if (spawnpid) - if(kill_proc(spawnpid, spawnsig, 1)) - spawnpid = 0; -} - -static void SAK(void) -{ - do_SAK(tty); -#if 0 - /* - * Need to fix SAK handling to fix up RAW/MEDIUM_RAW and - * vt_cons modes before we can enable RAW/MEDIUM_RAW SAK - * handling. - * - * We should do this some day --- the whole point of a secure - * attention key is that it should be guaranteed to always - * work. - */ - reset_vc(fg_console); - do_unblank_screen(); /* not in interrupt routine? */ -#endif -} - -static void do_ignore(unsigned char value, char up_flag) -{ -} - -static void do_null() -{ - compute_shiftstate(); -} - -static void do_spec(unsigned char value, char up_flag) -{ - if (up_flag) - return; - if (value >= SIZE(spec_fn_table)) - return; - spec_fn_table[value](); -} - -static void do_lowercase(unsigned char value, char up_flag) -{ - printk("keyboard.c: do_lowercase was called - impossible\n"); -} - -static void do_self(unsigned char value, char up_flag) -{ - if (up_flag) - return; /* no action, if this is a key release */ - - if (diacr) - value = handle_diacr(value); - - if (dead_key_next) { - dead_key_next = 0; - diacr = value; - return; - } - - put_queue(value); -} - -#define A_GRAVE '`' -#define A_ACUTE '\'' -#define A_CFLEX '^' -#define A_TILDE '~' -#define A_DIAER '"' -#define A_CEDIL ',' -static unsigned char ret_diacr[NR_DEAD] = - {A_GRAVE, A_ACUTE, A_CFLEX, A_TILDE, A_DIAER, A_CEDIL }; - -/* If a dead key pressed twice, output a character corresponding to it, */ -/* otherwise just remember the dead key. */ - -static void do_dead(unsigned char value, char up_flag) -{ - if (up_flag) - return; - - value = ret_diacr[value]; - if (diacr == value) { /* pressed twice */ - diacr = 0; - put_queue(value); - return; - } - diacr = value; -} - - -/* If space is pressed, return the character corresponding the pending */ -/* dead key, otherwise try to combine the two. */ - -unsigned char handle_diacr(unsigned char ch) -{ - int d = diacr; - int i; - - diacr = 0; - if (ch == ' ') - return d; - - for (i = 0; i < accent_table_size; i++) { - if (accent_table[i].diacr == d && accent_table[i].base == ch) - return accent_table[i].result; - } - - put_queue(d); - return ch; -} - -static void do_cons(unsigned char value, char up_flag) -{ - if (up_flag) - return; - set_console(value); -} - -static void do_fn(unsigned char value, char up_flag) -{ - if (up_flag) - return; - if (value < SIZE(func_table)) { - if (func_table[value]) - puts_queue(func_table[value]); - } else - printk("do_fn called with value=%d\n", value); -} - -static void do_pad(unsigned char value, char up_flag) -{ - static const char *pad_chars = "0123456789+-*/\015,.?"; - static const char *app_map = "pqrstuvwxylSRQMnn?"; - - if (up_flag) - return; /* no action, if this is a key release */ - - /* kludge... shift forces cursor/number keys */ - if (vc_kbd_mode(kbd,VC_APPLIC) && !k_down[KG_SHIFT]) { - applkey(app_map[value], 1); - return; - } - - if (!vc_kbd_led(kbd,VC_NUMLOCK)) - switch (value) { - case KVAL(K_PCOMMA): - case KVAL(K_PDOT): - do_fn(KVAL(K_REMOVE), 0); - return; - case KVAL(K_P0): - do_fn(KVAL(K_INSERT), 0); - return; - case KVAL(K_P1): - do_fn(KVAL(K_SELECT), 0); - return; - case KVAL(K_P2): - do_cur(KVAL(K_DOWN), 0); - return; - case KVAL(K_P3): - do_fn(KVAL(K_PGDN), 0); - return; - case KVAL(K_P4): - do_cur(KVAL(K_LEFT), 0); - return; - case KVAL(K_P6): - do_cur(KVAL(K_RIGHT), 0); - return; - case KVAL(K_P7): - do_fn(KVAL(K_FIND), 0); - return; - case KVAL(K_P8): - do_cur(KVAL(K_UP), 0); - return; - case KVAL(K_P9): - do_fn(KVAL(K_PGUP), 0); - return; - case KVAL(K_P5): - applkey('G', vc_kbd_mode(kbd, VC_APPLIC)); - return; - } - - put_queue(pad_chars[value]); - if (value == KVAL(K_PENTER) && vc_kbd_mode(kbd, VC_CRLF)) - put_queue(10); -} - -static void do_cur(unsigned char value, char up_flag) -{ - static const char *cur_chars = "BDCA"; - if (up_flag) - return; - - applkey(cur_chars[value], vc_kbd_mode(kbd,VC_CKMODE)); -} - -static void do_shift(unsigned char value, char up_flag) -{ - int old_state = shift_state; - - if (rep) - return; - - /* Mimic typewriter: - a CapsShift key acts like Shift but undoes CapsLock */ - if (value == KVAL(K_CAPSSHIFT)) { - value = KVAL(K_SHIFT); - if (!up_flag) - clr_vc_kbd_led(kbd, VC_CAPSLOCK); - } - - if (up_flag) { - /* handle the case that two shift or control - keys are depressed simultaneously */ - if (k_down[value]) - k_down[value]--; - } else - k_down[value]++; - - if (k_down[value]) - shift_state |= (1 << value); - else - shift_state &= ~ (1 << value); - - /* kludge */ - if (up_flag && shift_state != old_state && npadch != -1) { - if (kbd->kbdmode == VC_UNICODE) - to_utf8(npadch & 0xffff); - else - put_queue(npadch & 0xff); - npadch = -1; - } -} - -/* called after returning from RAW mode or when changing consoles - - recompute k_down[] and shift_state from key_down[] */ -/* maybe called when keymap is undefined, so that shiftkey release is seen */ -void compute_shiftstate(void) -{ - int i, j, k, sym, val; - - shift_state = 0; - for(i=0; i < SIZE(k_down); i++) - k_down[i] = 0; - - for(i=0; i < SIZE(key_down); i++) - if(key_down[i]) { /* skip this word if not a single bit on */ - k = i*BITS_PER_LONG; - for(j=0; jledmode = LED_SHOW_IOCTL; - } else - kbd->ledmode = LED_SHOW_FLAGS; - set_leds(); -} - -static struct ledptr { - unsigned int *addr; - unsigned int mask; - unsigned char valid:1; -} ledptrs[3]; - -void register_leds(int console, unsigned int led, - unsigned int *addr, unsigned int mask) { - struct kbd_struct *kbd = kbd_table + console; - if (led < 3) { - ledptrs[led].addr = addr; - ledptrs[led].mask = mask; - ledptrs[led].valid = 1; - kbd->ledmode = LED_SHOW_MEM; - } else - kbd->ledmode = LED_SHOW_FLAGS; -} - -static inline unsigned char getleds(void){ - struct kbd_struct *kbd = kbd_table + fg_console; - unsigned char leds; - - if (kbd->ledmode == LED_SHOW_IOCTL) - return ledioctl; - leds = kbd->ledflagstate; - if (kbd->ledmode == LED_SHOW_MEM) { - if (ledptrs[0].valid) { - if (*ledptrs[0].addr & ledptrs[0].mask) - leds |= 1; - else - leds &= ~1; - } - if (ledptrs[1].valid) { - if (*ledptrs[1].addr & ledptrs[1].mask) - leds |= 2; - else - leds &= ~2; - } - if (ledptrs[2].valid) { - if (*ledptrs[2].addr & ledptrs[2].mask) - leds |= 4; - else - leds &= ~4; - } - } - return leds; -} - -/* - * This routine is the bottom half of the keyboard interrupt - * routine, and runs with all interrupts enabled. It does - * console changing, led setting and copy_to_cooked, which can - * take a reasonably long time. - * - * Aside from timing (which isn't really that important for - * keyboard interrupts as they happen often), using the software - * interrupt routines for this thing allows us to easily mask - * this when we don't want any of the above to happen. Not yet - * used, but this allows for easy and efficient race-condition - * prevention later on. - */ -static void kbd_bh(void) -{ - unsigned char leds = getleds(); - - if (leds != ledstate) { - ledstate = leds; - if (mach_kbd_leds) - mach_kbd_leds(leds); - } -} - -__initfunc(int kbd_init(void)) -{ - int i; - struct kbd_struct kbd0; - extern struct tty_driver console_driver; - - kbd0.ledflagstate = kbd0.default_ledflagstate = KBD_DEFLEDS; - kbd0.ledmode = LED_SHOW_FLAGS; - kbd0.lockstate = KBD_DEFLOCK; - kbd0.slockstate = 0; - kbd0.modeflags = KBD_DEFMODE; - kbd0.kbdmode = VC_XLATE; - - for (i = 0 ; i < MAX_NR_CONSOLES ; i++) - kbd_table[i] = kbd0; - - ttytab = console_driver.table; - - init_bh(KEYBOARD_BH, kbd_bh); - mark_bh(KEYBOARD_BH); - - mach_keyb_init (); - - return 0; -} diff -u --recursive --new-file v2.1.39/linux/drivers/char/keyboard.c linux/drivers/char/keyboard.c --- v2.1.39/linux/drivers/char/keyboard.c Tue May 13 22:41:07 1997 +++ linux/drivers/char/keyboard.c Wed May 21 09:57:45 1997 @@ -13,46 +13,29 @@ * dynamic function/string keys, led setting, Sept 1994 * `Sticky' modifier keys, 951006. * 11-11-96: SAK should now work in the raw mode (Martin Mares) + * + * Modified to provide 'generic' keyboard support by Hamish Macdonald + * Merge with the m68k keyboard driver and split-off of the PC low-level + * parts by Geert Uytterhoeven, May 1997 */ -#define KEYBOARD_IRQ 1 -#define DISABLE_KBD_DURING_INTERRUPTS 0 - #include -#include #include #include #include -#include -#include #include -#include #include #include +#include #include #include "kbd_kern.h" #include "diacr.h" #include "vt_kern.h" -/* - * On non-x86 hardware we do a full keyboard controller - * initialization, in case the bootup software hasn't done - * it. On a x86, the BIOS will already have initialized the - * keyboard. - */ -#ifndef __i386__ -#define INIT_KBD -static int initialize_kbd(void); -#endif - #define SIZE(x) (sizeof(x)/sizeof((x)[0])) -#define KBD_REPORT_ERR -#define KBD_REPORT_UNKN -/* #define KBD_IS_FOCUS_9000 */ - #ifndef KBD_DEFMODE #define KBD_DEFMODE ((1 << VC_REPEAT) | (1 << VC_META)) #endif @@ -69,16 +52,11 @@ #define KBD_DEFLOCK 0 #endif -#include -#include - extern void ctrl_alt_del(void); extern void reset_vc(unsigned int new_console); extern void scrollback(int); extern void scrollfront(int); -unsigned char kbd_read_mask = 0x01; /* modified by psaux.c */ - struct wait_queue * keypress_wait = NULL; void keyboard_wait_for_keypress(void) @@ -113,12 +91,7 @@ static struct kbd_struct * kbd = kbd_table; static struct tty_struct * tty = NULL; -/* used only by send_data - set by keyboard_interrupt */ -static volatile unsigned char reply_expected = 0; -static volatile unsigned char acknowledge = 0; -static volatile unsigned char resend = 0; - -extern void compute_shiftstate(void); +void compute_shiftstate(void); typedef void (*k_hand)(unsigned char value, char up_flag); typedef void (k_handfn)(unsigned char value, char up_flag); @@ -168,23 +141,7 @@ static unsigned char handle_diacr(unsigned char); /* pt_regs - set by keyboard_interrupt(), used by show_ptregs() */ -static struct pt_regs * pt_regs; - -static inline void kb_wait(void) -{ - int i; - - for (i=0; i<0x100000; i++) - if ((inb_p(0x64) & 0x02) == 0) - return; - printk(KERN_WARNING "Keyboard timed out\n"); -} - -static inline void send_cmd(unsigned char c) -{ - kb_wait(); - outb(c,0x64); -} +struct pt_regs * pt_regs; /* * Many other routines do put_queue, but I think either @@ -209,178 +166,25 @@ /* * Translation of escaped scancodes to keycodes. - * This is now user-settable. - * The keycodes 1-88,96-111,119 are fairly standard, and - * should probably not be changed - changing might confuse X. - * X also interprets scancode 0x5d (KEY_Begin). - * - * For 1-88 keycode equals scancode. + * This is now user-settable (for machines were it makes sense). */ -#define E0_KPENTER 96 -#define E0_RCTRL 97 -#define E0_KPSLASH 98 -#define E0_PRSCR 99 -#define E0_RALT 100 -#define E0_BREAK 101 /* (control-pause) */ -#define E0_HOME 102 -#define E0_UP 103 -#define E0_PGUP 104 -#define E0_LEFT 105 -#define E0_RIGHT 106 -#define E0_END 107 -#define E0_DOWN 108 -#define E0_PGDN 109 -#define E0_INS 110 -#define E0_DEL 111 - -#define E1_PAUSE 119 - -/* - * The keycodes below are randomly located in 89-95,112-118,120-127. - * They could be thrown away (and all occurrences below replaced by 0), - * but that would force many users to use the `setkeycodes' utility, where - * they needed not before. It does not matter that there are duplicates, as - * long as no duplication occurs for any single keyboard. - */ -#define SC_LIM 89 - -#define FOCUS_PF1 85 /* actual code! */ -#define FOCUS_PF2 89 -#define FOCUS_PF3 90 -#define FOCUS_PF4 91 -#define FOCUS_PF5 92 -#define FOCUS_PF6 93 -#define FOCUS_PF7 94 -#define FOCUS_PF8 95 -#define FOCUS_PF9 120 -#define FOCUS_PF10 121 -#define FOCUS_PF11 122 -#define FOCUS_PF12 123 - -#define JAP_86 124 -/* tfj@olivia.ping.dk: - * The four keys are located over the numeric keypad, and are - * labelled A1-A4. It's an rc930 keyboard, from - * Regnecentralen/RC International, Now ICL. - * Scancodes: 59, 5a, 5b, 5c. - */ -#define RGN1 124 -#define RGN2 125 -#define RGN3 126 -#define RGN4 127 - -static unsigned char high_keys[128 - SC_LIM] = { - RGN1, RGN2, RGN3, RGN4, 0, 0, 0, /* 0x59-0x5f */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ - 0, 0, 0, 0, 0, FOCUS_PF11, 0, FOCUS_PF12, /* 0x68-0x6f */ - 0, 0, 0, FOCUS_PF2, FOCUS_PF9, 0, 0, FOCUS_PF3, /* 0x70-0x77 */ - FOCUS_PF4, FOCUS_PF5, FOCUS_PF6, FOCUS_PF7, /* 0x78-0x7b */ - FOCUS_PF8, JAP_86, FOCUS_PF10, 0 /* 0x7c-0x7f */ -}; - -/* BTC */ -#define E0_MACRO 112 -/* LK450 */ -#define E0_F13 113 -#define E0_F14 114 -#define E0_HELP 115 -#define E0_DO 116 -#define E0_F17 117 -#define E0_KPMINPLUS 118 -/* - * My OmniKey generates e0 4c for the "OMNI" key and the - * right alt key does nada. [kkoller@nyx10.cs.du.edu] - */ -#define E0_OK 124 -/* - * New microsoft keyboard is rumoured to have - * e0 5b (left window button), e0 5c (right window button), - * e0 5d (menu button). [or: LBANNER, RBANNER, RMENU] - * [or: Windows_L, Windows_R, TaskMan] - */ -#define E0_MSLW 125 -#define E0_MSRW 126 -#define E0_MSTM 127 - -static unsigned char e0_keys[128] = { - 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x07 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08-0x0f */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x17 */ - 0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0, /* 0x18-0x1f */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20-0x27 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 0x28-0x2f */ - 0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR, /* 0x30-0x37 */ - E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP, /* 0x38-0x3f */ - E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME, /* 0x40-0x47 */ - E0_UP, E0_PGUP, 0, E0_LEFT, E0_OK, E0_RIGHT, E0_KPMINPLUS, E0_END,/* 0x48-0x4f */ - E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0, /* 0x50-0x57 */ - 0, 0, 0, E0_MSLW, E0_MSRW, E0_MSTM, 0, 0, /* 0x58-0x5f */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ - 0, 0, 0, 0, 0, 0, 0, E0_MACRO, /* 0x68-0x6f */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77 */ - 0, 0, 0, 0, 0, 0, 0, 0 /* 0x78-0x7f */ -}; - int setkeycode(unsigned int scancode, unsigned int keycode) { - if (scancode < SC_LIM || scancode > 255 || keycode > 127) - return -EINVAL; - if (scancode < 128) - high_keys[scancode - SC_LIM] = keycode; - else - e0_keys[scancode - 128] = keycode; - return 0; + return kbd_setkeycode(scancode, keycode); } int getkeycode(unsigned int scancode) { - return - (scancode < SC_LIM || scancode > 255) ? -EINVAL : - (scancode < 128) ? high_keys[scancode - SC_LIM] : - e0_keys[scancode - 128]; + return kbd_getkeycode(scancode); } -#if DISABLE_KBD_DURING_INTERRUPTS -#define disable_keyboard() do { send_cmd(0xAD); kb_wait(); } while (0) -#define enable_keyboard() send_cmd(0xAE) -#else -#define disable_keyboard() /* nothing */ -#define enable_keyboard() /* nothing */ -#endif - -static void handle_scancode(unsigned char scancode) +void handle_scancode(unsigned char scancode) { unsigned char keycode; - static unsigned int prev_scancode = 0; /* remember E0, E1 */ char up_flag; /* 0 or 0200 */ char raw_mode; - if (reply_expected) { - /* 0xfa, 0xfe only mean "acknowledge", "resend" for most keyboards */ - /* but they are the key-up scancodes for PF6, PF10 on a FOCUS 9000 */ - reply_expected = 0; - if (scancode == 0xfa) { - acknowledge = 1; - return; - } else if (scancode == 0xfe) { - resend = 1; - return; - } - /* strange ... */ - reply_expected = 1; -#if 0 - printk(KERN_DEBUG "keyboard reply expected - got %02x\n", - scancode); -#endif - } - if (scancode == 0) { -#ifdef KBD_REPORT_ERR - printk(KERN_INFO "keyboard buffer overflow\n"); -#endif - prev_scancode = 0; - return; - } do_poke_blanked_console = 1; mark_bh(CONSOLE_BH); add_keyboard_randomness(scancode); @@ -394,121 +198,28 @@ values when finishing RAW mode or when changing VT's */ } - if (scancode == 0xff) { - /* in scancode mode 1, my ESC key generates 0xff */ - /* the calculator keys on a FOCUS 9000 generate 0xff */ -#ifndef KBD_IS_FOCUS_9000 -#ifdef KBD_REPORT_ERR - if (!raw_mode) - printk(KERN_DEBUG "keyboard error\n"); -#endif -#endif - prev_scancode = 0; - return; - } - - if (scancode == 0xe0 || scancode == 0xe1) { - prev_scancode = scancode; - return; - } - + if (!kbd_pretranslate(scancode, raw_mode)) + return; /* - * Convert scancode to keycode, using prev_scancode. + * Convert scancode to keycode */ up_flag = (scancode & 0200); scancode &= 0x7f; - if (prev_scancode) { - /* - * usually it will be 0xe0, but a Pause key generates - * e1 1d 45 e1 9d c5 when pressed, and nothing when released - */ - if (prev_scancode != 0xe0) { - if (prev_scancode == 0xe1 && scancode == 0x1d) { - prev_scancode = 0x100; - return; - } else if (prev_scancode == 0x100 && scancode == 0x45) { - keycode = E1_PAUSE; - prev_scancode = 0; - } else { -#ifdef KBD_REPORT_UNKN - if (!raw_mode) - printk(KERN_INFO "keyboard: unknown e1 escape sequence\n"); -#endif - prev_scancode = 0; - return; - } - } else { - prev_scancode = 0; - /* - * The keyboard maintains its own internal caps lock and - * num lock statuses. In caps lock mode E0 AA precedes make - * code and E0 2A follows break code. In num lock mode, - * E0 2A precedes make code and E0 AA follows break code. - * We do our own book-keeping, so we will just ignore these. - */ - /* - * For my keyboard there is no caps lock mode, but there are - * both Shift-L and Shift-R modes. The former mode generates - * E0 2A / E0 AA pairs, the latter E0 B6 / E0 36 pairs. - * So, we should also ignore the latter. - aeb@cwi.nl - */ - if (scancode == 0x2a || scancode == 0x36) - return; - - if (e0_keys[scancode]) - keycode = e0_keys[scancode]; - else { -#ifdef KBD_REPORT_UNKN - if (!raw_mode) - printk(KERN_INFO "keyboard: unknown scancode e0 %02x\n", - scancode); -#endif - return; - } - } - } else if (scancode >= SC_LIM) { - /* This happens with the FOCUS 9000 keyboard - Its keys PF1..PF12 are reported to generate - 55 73 77 78 79 7a 7b 7c 74 7e 6d 6f - Moreover, unless repeated, they do not generate - key-down events, so we have to zero up_flag below */ - /* Also, Japanese 86/106 keyboards are reported to - generate 0x73 and 0x7d for \ - and \ | respectively. */ - /* Also, some Brazilian keyboard is reported to produce - 0x73 and 0x7e for \ ? and KP-dot, respectively. */ - - keycode = high_keys[scancode - SC_LIM]; - - if (!keycode) { - if (!raw_mode) { -#ifdef KBD_REPORT_UNKN - printk(KERN_INFO "keyboard: unrecognized scancode (%02x)" - " - ignored\n", scancode); -#endif - } - return; - } - } else - keycode = scancode; + if (!kbd_translate(scancode, &keycode, raw_mode)) + return; /* * At this point the variable `keycode' contains the keycode. - * Note: the keycode must not be 0. + * Note: the keycode must not be 0 (++Geert: on m68k 0 is valid). * We keep track of the up/down status of the key, and * return the keycode if in MEDIUMRAW mode. */ if (up_flag) { rep = 0; - if(!test_and_clear_bit(keycode, key_down)) { - /* unexpected, but this can happen: - maybe this was a key release for a FOCUS 9000 - PF key; if we want to see it, we have to clear - up_flag */ - if (keycode >= SC_LIM || keycode == 85) - up_flag = 0; - } + if(!test_and_clear_bit(keycode, key_down)) + up_flag = kbd_unexpected_up(keycode); } else rep = test_and_set_bit(keycode, key_down); @@ -580,31 +291,6 @@ } } -static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - unsigned char status; - - pt_regs = regs; - disable_keyboard(); - - status = inb_p(0x64); - do { - unsigned char scancode; - - /* mouse data? */ - if (status & kbd_read_mask & 0x20) - break; - - scancode = inb(0x60); - if (status & 0x01) - handle_scancode(scancode); - - status = inb(0x64); - } while (status & 0x01); - - mark_bh(KEYBOARD_BH); - enable_keyboard(); -} static void put_queue(int ch) { @@ -894,8 +580,8 @@ static void do_pad(unsigned char value, char up_flag) { - static const char *pad_chars = "0123456789+-*/\015,.?"; - static const char *app_map = "pqrstuvwxylSRQMnn?"; + static const char *pad_chars = "0123456789+-*/\015,.?()"; + static const char *app_map = "pqrstuvwxylSRQMnn?PQ"; if (up_flag) return; /* no action, if this is a key release */ @@ -1071,35 +757,6 @@ } /* - * send_data sends a character to the keyboard and waits - * for an acknowledge, possibly retrying if asked to. Returns - * the success status. - */ -static int send_data(unsigned char data) -{ - int retries = 3; - int i; - - do { - kb_wait(); - acknowledge = 0; - resend = 0; - reply_expected = 1; - outb_p(data, 0x60); - for(i=0; i<0x200000; i++) { - inb_p(0x64); /* just as a delay */ - if (acknowledge) - return 1; - if (resend) - break; - } - if (!resend) - return 0; - } while (retries-- > 0); - return 0; -} - -/* * The leds display either (i) the status of NumLock, CapsLock, ScrollLock, * or (ii) whatever pattern of lights people want to show using KDSETLED, * or (iii) specified bits of specified words in kernel memory. @@ -1188,8 +845,7 @@ if (leds != ledstate) { ledstate = leds; - if (!send_data(0xed) || !send_data(leds)) - send_data(0xf4); /* re-enable kbd if any errors */ + kbd_leds(leds); } } @@ -1211,194 +867,8 @@ ttytab = console_driver.table; - request_irq(KEYBOARD_IRQ, keyboard_interrupt, 0, "keyboard", NULL); - request_region(0x60,16,"keyboard"); -#ifdef INIT_KBD - initialize_kbd(); -#endif + kbd_init_hw(); init_bh(KEYBOARD_BH, kbd_bh); mark_bh(KEYBOARD_BH); return 0; } - -#ifdef INIT_KBD -/* - * keyboard controller registers - */ -#define KBD_STATUS_REG (unsigned int) 0x64 -#define KBD_CNTL_REG (unsigned int) 0x64 -#define KBD_DATA_REG (unsigned int) 0x60 -/* - * controller commands - */ -#define KBD_READ_MODE (unsigned int) 0x20 -#define KBD_WRITE_MODE (unsigned int) 0x60 -#define KBD_SELF_TEST (unsigned int) 0xAA -#define KBD_SELF_TEST2 (unsigned int) 0xAB -#define KBD_CNTL_ENABLE (unsigned int) 0xAE -/* - * keyboard commands - */ -#define KBD_ENABLE (unsigned int) 0xF4 -#define KBD_DISABLE (unsigned int) 0xF5 -#define KBD_RESET (unsigned int) 0xFF -/* - * keyboard replies - */ -#define KBD_ACK (unsigned int) 0xFA -#define KBD_POR (unsigned int) 0xAA -/* - * status register bits - */ -#define KBD_OBF (unsigned int) 0x01 -#define KBD_IBF (unsigned int) 0x02 -#define KBD_GTO (unsigned int) 0x40 -#define KBD_PERR (unsigned int) 0x80 -/* - * keyboard controller mode register bits - */ -#define KBD_EKI (unsigned int) 0x01 -#define KBD_SYS (unsigned int) 0x04 -#define KBD_DMS (unsigned int) 0x20 -#define KBD_KCC (unsigned int) 0x40 - -#define TIMEOUT_CONST 500000 - -static int kbd_wait_for_input(void) -{ - int n; - int status, data; - - n = TIMEOUT_CONST; - do { - status = inb(KBD_STATUS_REG); - /* - * Wait for input data to become available. This bit will - * then be cleared by the following read of the DATA - * register. - */ - - if (!(status & KBD_OBF)) - continue; - - data = inb(KBD_DATA_REG); - - /* - * Check to see if a timeout error has occurred. This means - * that transmission was started but did not complete in the - * normal time cycle. PERR is set when a parity error occurred - * in the last transmission. - */ - if (status & (KBD_GTO | KBD_PERR)) { - continue; - } - return (data & 0xff); - } while (--n); - return (-1); /* timed-out if fell through to here... */ -} - -static void kbd_write(int address, int data) -{ - int status; - - do { - status = inb(KBD_STATUS_REG); /* spin until input buffer empty*/ - } while (status & KBD_IBF); - outb(data, address); /* write out the data*/ -} - -__initfunc(static int initialize_kbd(void)) -{ - unsigned long flags; - - save_flags(flags); cli(); - - /* Flush any pending input. */ - while (kbd_wait_for_input() != -1) - continue; - - /* - * Test the keyboard interface. - * This seems to be the only way to get it going. - * If the test is successful a x55 is placed in the input buffer. - */ - kbd_write(KBD_CNTL_REG, KBD_SELF_TEST); - if (kbd_wait_for_input() != 0x55) { - printk(KERN_WARNING "initialize_kbd: " - "keyboard failed self test.\n"); - restore_flags(flags); - return(-1); - } - - /* - * Perform a keyboard interface test. This causes the controller - * to test the keyboard clock and data lines. The results of the - * test are placed in the input buffer. - */ - kbd_write(KBD_CNTL_REG, KBD_SELF_TEST2); - if (kbd_wait_for_input() != 0x00) { - printk(KERN_WARNING "initialize_kbd: " - "keyboard failed self test 2.\n"); - restore_flags(flags); - return(-1); - } - - /* Enable the keyboard by allowing the keyboard clock to run. */ - kbd_write(KBD_CNTL_REG, KBD_CNTL_ENABLE); - - /* - * Reset keyboard. If the read times out - * then the assumption is that no keyboard is - * plugged into the machine. - * This defaults the keyboard to scan-code set 2. - */ - kbd_write(KBD_DATA_REG, KBD_RESET); - if (kbd_wait_for_input() != KBD_ACK) { - printk(KERN_WARNING "initialize_kbd: " - "reset kbd failed, no ACK.\n"); - restore_flags(flags); - return(-1); - } - - if (kbd_wait_for_input() != KBD_POR) { - printk(KERN_WARNING "initialize_kbd: " - "reset kbd failed, not POR.\n"); - restore_flags(flags); - return(-1); - } - - /* - * now do a DEFAULTS_DISABLE always - */ - kbd_write(KBD_DATA_REG, KBD_DISABLE); - if (kbd_wait_for_input() != KBD_ACK) { - printk(KERN_WARNING "initialize_kbd: " - "disable kbd failed, no ACK.\n"); - restore_flags(flags); - return(-1); - } - - /* - * Enable keyboard interrupt, operate in "sys" mode, - * enable keyboard (by clearing the disable keyboard bit), - * disable mouse, do conversion of keycodes. - */ - kbd_write(KBD_CNTL_REG, KBD_WRITE_MODE); - kbd_write(KBD_DATA_REG, KBD_EKI|KBD_SYS|KBD_DMS|KBD_KCC); - - /* - * now ENABLE the keyboard to set it scanning... - */ - kbd_write(KBD_DATA_REG, KBD_ENABLE); - if (kbd_wait_for_input() != KBD_ACK) { - printk(KERN_WARNING "initialize_kbd: " - "keyboard enable failed.\n"); - restore_flags(flags); - return(-1); - } - - restore_flags(flags); - - return (1); -} -#endif /* INIT_KBD */ diff -u --recursive --new-file v2.1.39/linux/drivers/char/pc_keyb.c linux/drivers/char/pc_keyb.c --- v2.1.39/linux/drivers/char/pc_keyb.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/pc_keyb.c Wed May 21 09:57:45 1997 @@ -0,0 +1,609 @@ +/* + * linux/drivers/char/pc_keyb.c + * + * Written for linux by Johan Myreen as a translation from + * the assembly version by Linus (with diacriticals added) + * + * Some additional features added by Christoph Niemann (ChN), March 1993 + * + * Loadable keymaps by Risto Kankkunen, May 1993 + * + * Diacriticals redone & other small changes, aeb@cwi.nl, June 1993 + * Added decr/incr_console, dynamic keymaps, Unicode support, + * dynamic function/string keys, led setting, Sept 1994 + * `Sticky' modifier keys, 951006. + * 11-11-96: SAK should now work in the raw mode (Martin Mares) + * + * Separation of the PC low-level part by Geert Uytterhoeven, May 1997 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * On non-x86 hardware we do a full keyboard controller + * initialization, in case the bootup software hasn't done + * it. On a x86, the BIOS will already have initialized the + * keyboard. + */ +#ifdef INIT_KBD +static int initialize_kbd(void); +#endif + +#include +#include + +unsigned char kbd_read_mask = 0x01; /* modified by psaux.c */ + +/* used only by send_data - set by keyboard_interrupt */ +static volatile unsigned char reply_expected = 0; +static volatile unsigned char acknowledge = 0; +static volatile unsigned char resend = 0; + +/* pt_regs - set by keyboard_interrupt(), used by show_ptregs() */ +static inline void kb_wait(void) +{ + int i; + + for (i=0; i<0x100000; i++) + if ((inb_p(0x64) & 0x02) == 0) + return; + printk(KERN_WARNING "Keyboard timed out\n"); +} + +extern struct pt_regs *pt_regs; + +extern void handle_scancode(unsigned char scancode); + + +/* + * Translation of escaped scancodes to keycodes. + * This is now user-settable. + * The keycodes 1-88,96-111,119 are fairly standard, and + * should probably not be changed - changing might confuse X. + * X also interprets scancode 0x5d (KEY_Begin). + * + * For 1-88 keycode equals scancode. + */ + +#define E0_KPENTER 96 +#define E0_RCTRL 97 +#define E0_KPSLASH 98 +#define E0_PRSCR 99 +#define E0_RALT 100 +#define E0_BREAK 101 /* (control-pause) */ +#define E0_HOME 102 +#define E0_UP 103 +#define E0_PGUP 104 +#define E0_LEFT 105 +#define E0_RIGHT 106 +#define E0_END 107 +#define E0_DOWN 108 +#define E0_PGDN 109 +#define E0_INS 110 +#define E0_DEL 111 + +#define E1_PAUSE 119 + +/* + * The keycodes below are randomly located in 89-95,112-118,120-127. + * They could be thrown away (and all occurrences below replaced by 0), + * but that would force many users to use the `setkeycodes' utility, where + * they needed not before. It does not matter that there are duplicates, as + * long as no duplication occurs for any single keyboard. + */ +#define SC_LIM 89 + +#define FOCUS_PF1 85 /* actual code! */ +#define FOCUS_PF2 89 +#define FOCUS_PF3 90 +#define FOCUS_PF4 91 +#define FOCUS_PF5 92 +#define FOCUS_PF6 93 +#define FOCUS_PF7 94 +#define FOCUS_PF8 95 +#define FOCUS_PF9 120 +#define FOCUS_PF10 121 +#define FOCUS_PF11 122 +#define FOCUS_PF12 123 + +#define JAP_86 124 +/* tfj@olivia.ping.dk: + * The four keys are located over the numeric keypad, and are + * labelled A1-A4. It's an rc930 keyboard, from + * Regnecentralen/RC International, Now ICL. + * Scancodes: 59, 5a, 5b, 5c. + */ +#define RGN1 124 +#define RGN2 125 +#define RGN3 126 +#define RGN4 127 + +static unsigned char high_keys[128 - SC_LIM] = { + RGN1, RGN2, RGN3, RGN4, 0, 0, 0, /* 0x59-0x5f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ + 0, 0, 0, 0, 0, FOCUS_PF11, 0, FOCUS_PF12, /* 0x68-0x6f */ + 0, 0, 0, FOCUS_PF2, FOCUS_PF9, 0, 0, FOCUS_PF3, /* 0x70-0x77 */ + FOCUS_PF4, FOCUS_PF5, FOCUS_PF6, FOCUS_PF7, /* 0x78-0x7b */ + FOCUS_PF8, JAP_86, FOCUS_PF10, 0 /* 0x7c-0x7f */ +}; + +/* BTC */ +#define E0_MACRO 112 +/* LK450 */ +#define E0_F13 113 +#define E0_F14 114 +#define E0_HELP 115 +#define E0_DO 116 +#define E0_F17 117 +#define E0_KPMINPLUS 118 +/* + * My OmniKey generates e0 4c for the "OMNI" key and the + * right alt key does nada. [kkoller@nyx10.cs.du.edu] + */ +#define E0_OK 124 +/* + * New microsoft keyboard is rumoured to have + * e0 5b (left window button), e0 5c (right window button), + * e0 5d (menu button). [or: LBANNER, RBANNER, RMENU] + * [or: Windows_L, Windows_R, TaskMan] + */ +#define E0_MSLW 125 +#define E0_MSRW 126 +#define E0_MSTM 127 + +static unsigned char e0_keys[128] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x07 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08-0x0f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x17 */ + 0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0, /* 0x18-0x1f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20-0x27 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x28-0x2f */ + 0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR, /* 0x30-0x37 */ + E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP, /* 0x38-0x3f */ + E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME, /* 0x40-0x47 */ + E0_UP, E0_PGUP, 0, E0_LEFT, E0_OK, E0_RIGHT, E0_KPMINPLUS, E0_END,/* 0x48-0x4f */ + E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0, /* 0x50-0x57 */ + 0, 0, 0, E0_MSLW, E0_MSRW, E0_MSTM, 0, 0, /* 0x58-0x5f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ + 0, 0, 0, 0, 0, 0, 0, E0_MACRO, /* 0x68-0x6f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77 */ + 0, 0, 0, 0, 0, 0, 0, 0 /* 0x78-0x7f */ +}; + +static unsigned int prev_scancode = 0; /* remember E0, E1 */ + +int pckbd_setkeycode(unsigned int scancode, unsigned int keycode) +{ + if (scancode < SC_LIM || scancode > 255 || keycode > 127) + return -EINVAL; + if (scancode < 128) + high_keys[scancode - SC_LIM] = keycode; + else + e0_keys[scancode - 128] = keycode; + return 0; +} + +int pckbd_getkeycode(unsigned int scancode) +{ + return + (scancode < SC_LIM || scancode > 255) ? -EINVAL : + (scancode < 128) ? high_keys[scancode - SC_LIM] : + e0_keys[scancode - 128]; +} + +#if DISABLE_KBD_DURING_INTERRUPTS +static inline void send_cmd(unsigned char c) +{ + kb_wait(); + outb(c,0x64); +} + +#define disable_keyboard() do { send_cmd(0xAD); kb_wait(); } while (0) +#define enable_keyboard() send_cmd(0xAE) +#else +#define disable_keyboard() /* nothing */ +#define enable_keyboard() /* nothing */ +#endif + +static int do_acknowledge(unsigned char scancode) +{ + if (reply_expected) { + /* 0xfa, 0xfe only mean "acknowledge", "resend" for most keyboards */ + /* but they are the key-up scancodes for PF6, PF10 on a FOCUS 9000 */ + reply_expected = 0; + if (scancode == 0xfa) { + acknowledge = 1; + return 0; + } else if (scancode == 0xfe) { + resend = 1; + return 0; + } + /* strange ... */ + reply_expected = 1; +#if 0 + printk(KERN_DEBUG "keyboard reply expected - got %02x\n", + scancode); +#endif + } + if (scancode == 0) { +#ifdef KBD_REPORT_ERR + printk(KERN_INFO "keyboard buffer overflow\n"); +#endif + prev_scancode = 0; + return 0; + } + return 1; +} + +int pckbd_pretranslate(unsigned char scancode, char raw_mode) +{ + if (scancode == 0xff) { + /* in scancode mode 1, my ESC key generates 0xff */ + /* the calculator keys on a FOCUS 9000 generate 0xff */ +#ifndef KBD_IS_FOCUS_9000 +#ifdef KBD_REPORT_ERR + if (!raw_mode) + printk(KERN_DEBUG "keyboard error\n"); +#endif +#endif + prev_scancode = 0; + return 0; + } + + if (scancode == 0xe0 || scancode == 0xe1) { + prev_scancode = scancode; + return 0; + } + return 1; +} + +int pckbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode) +{ + if (prev_scancode) { + /* + * usually it will be 0xe0, but a Pause key generates + * e1 1d 45 e1 9d c5 when pressed, and nothing when released + */ + if (prev_scancode != 0xe0) { + if (prev_scancode == 0xe1 && scancode == 0x1d) { + prev_scancode = 0x100; + return 0; + } else if (prev_scancode == 0x100 && scancode == 0x45) { + *keycode = E1_PAUSE; + prev_scancode = 0; + } else { +#ifdef KBD_REPORT_UNKN + if (!raw_mode) + printk(KERN_INFO "keyboard: unknown e1 escape sequence\n"); +#endif + prev_scancode = 0; + return 0; + } + } else { + prev_scancode = 0; + /* + * The keyboard maintains its own internal caps lock and + * num lock statuses. In caps lock mode E0 AA precedes make + * code and E0 2A follows break code. In num lock mode, + * E0 2A precedes make code and E0 AA follows break code. + * We do our own book-keeping, so we will just ignore these. + */ + /* + * For my keyboard there is no caps lock mode, but there are + * both Shift-L and Shift-R modes. The former mode generates + * E0 2A / E0 AA pairs, the latter E0 B6 / E0 36 pairs. + * So, we should also ignore the latter. - aeb@cwi.nl + */ + if (scancode == 0x2a || scancode == 0x36) + return 0; + + if (e0_keys[scancode]) + *keycode = e0_keys[scancode]; + else { +#ifdef KBD_REPORT_UNKN + if (!raw_mode) + printk(KERN_INFO "keyboard: unknown scancode e0 %02x\n", + scancode); +#endif + return 0; + } + } + } else if (scancode >= SC_LIM) { + /* This happens with the FOCUS 9000 keyboard + Its keys PF1..PF12 are reported to generate + 55 73 77 78 79 7a 7b 7c 74 7e 6d 6f + Moreover, unless repeated, they do not generate + key-down events, so we have to zero up_flag below */ + /* Also, Japanese 86/106 keyboards are reported to + generate 0x73 and 0x7d for \ - and \ | respectively. */ + /* Also, some Brazilian keyboard is reported to produce + 0x73 and 0x7e for \ ? and KP-dot, respectively. */ + + *keycode = high_keys[scancode - SC_LIM]; + + if (!*keycode) { + if (!raw_mode) { +#ifdef KBD_REPORT_UNKN + printk(KERN_INFO "keyboard: unrecognized scancode (%02x)" + " - ignored\n", scancode); +#endif + } + return 0; + } + } else + *keycode = scancode; + return 1; +} + +char pckbd_unexpected_up(unsigned char keycode) +{ + /* unexpected, but this can happen: maybe this was a key release for a + FOCUS 9000 PF key; if we want to see it, we have to clear up_flag */ + if (keycode >= SC_LIM || keycode == 85) + return 0; + else + return 0200; +} + +static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned char status; + + pt_regs = regs; + disable_keyboard(); + + status = inb_p(0x64); + do { + unsigned char scancode; + + /* mouse data? */ + if (status & kbd_read_mask & 0x20) + break; + + scancode = inb(0x60); + if ((status & 0x01) && do_acknowledge(scancode)) + handle_scancode(scancode); + + status = inb(0x64); + } while (status & 0x01); + + mark_bh(KEYBOARD_BH); + enable_keyboard(); +} + +/* + * send_data sends a character to the keyboard and waits + * for an acknowledge, possibly retrying if asked to. Returns + * the success status. + */ +static int send_data(unsigned char data) +{ + int retries = 3; + int i; + + do { + kb_wait(); + acknowledge = 0; + resend = 0; + reply_expected = 1; + outb_p(data, 0x60); + for(i=0; i<0x200000; i++) { + inb_p(0x64); /* just as a delay */ + if (acknowledge) + return 1; + if (resend) + break; + } + if (!resend) + return 0; + } while (retries-- > 0); + return 0; +} + +void pckbd_leds(unsigned char leds) +{ + if (!send_data(0xed) || !send_data(leds)) + send_data(0xf4); /* re-enable kbd if any errors */ +} + +__initfunc(void pckbd_init_hw(void)) +{ + request_irq(KEYBOARD_IRQ, keyboard_interrupt, 0, "keyboard", NULL); + request_region(0x60,16,"keyboard"); +#ifdef INIT_KBD + initialize_kbd(); +#endif +} + +#ifdef INIT_KBD +/* + * keyboard controller registers + */ +#define KBD_STATUS_REG (unsigned int) 0x64 +#define KBD_CNTL_REG (unsigned int) 0x64 +#define KBD_DATA_REG (unsigned int) 0x60 +/* + * controller commands + */ +#define KBD_READ_MODE (unsigned int) 0x20 +#define KBD_WRITE_MODE (unsigned int) 0x60 +#define KBD_SELF_TEST (unsigned int) 0xAA +#define KBD_SELF_TEST2 (unsigned int) 0xAB +#define KBD_CNTL_ENABLE (unsigned int) 0xAE +/* + * keyboard commands + */ +#define KBD_ENABLE (unsigned int) 0xF4 +#define KBD_DISABLE (unsigned int) 0xF5 +#define KBD_RESET (unsigned int) 0xFF +/* + * keyboard replies + */ +#define KBD_ACK (unsigned int) 0xFA +#define KBD_POR (unsigned int) 0xAA +/* + * status register bits + */ +#define KBD_OBF (unsigned int) 0x01 +#define KBD_IBF (unsigned int) 0x02 +#define KBD_GTO (unsigned int) 0x40 +#define KBD_PERR (unsigned int) 0x80 +/* + * keyboard controller mode register bits + */ +#define KBD_EKI (unsigned int) 0x01 +#define KBD_SYS (unsigned int) 0x04 +#define KBD_DMS (unsigned int) 0x20 +#define KBD_KCC (unsigned int) 0x40 + +#define TIMEOUT_CONST 500000 + +static int kbd_wait_for_input(void) +{ + int n; + int status, data; + + n = TIMEOUT_CONST; + do { + status = inb(KBD_STATUS_REG); + /* + * Wait for input data to become available. This bit will + * then be cleared by the following read of the DATA + * register. + */ + + if (!(status & KBD_OBF)) + continue; + + data = inb(KBD_DATA_REG); + + /* + * Check to see if a timeout error has occurred. This means + * that transmission was started but did not complete in the + * normal time cycle. PERR is set when a parity error occurred + * in the last transmission. + */ + if (status & (KBD_GTO | KBD_PERR)) { + continue; + } + return (data & 0xff); + } while (--n); + return (-1); /* timed-out if fell through to here... */ +} + +static void kbd_write(int address, int data) +{ + int status; + + do { + status = inb(KBD_STATUS_REG); /* spin until input buffer empty*/ + } while (status & KBD_IBF); + outb(data, address); /* write out the data*/ +} + +__initfunc(static int initialize_kbd(void)) +{ + unsigned long flags; + + save_flags(flags); cli(); + + /* Flush any pending input. */ + while (kbd_wait_for_input() != -1) + continue; + + /* + * Test the keyboard interface. + * This seems to be the only way to get it going. + * If the test is successful a x55 is placed in the input buffer. + */ + kbd_write(KBD_CNTL_REG, KBD_SELF_TEST); + if (kbd_wait_for_input() != 0x55) { + printk(KERN_WARNING "initialize_kbd: " + "keyboard failed self test.\n"); + restore_flags(flags); + return(-1); + } + + /* + * Perform a keyboard interface test. This causes the controller + * to test the keyboard clock and data lines. The results of the + * test are placed in the input buffer. + */ + kbd_write(KBD_CNTL_REG, KBD_SELF_TEST2); + if (kbd_wait_for_input() != 0x00) { + printk(KERN_WARNING "initialize_kbd: " + "keyboard failed self test 2.\n"); + restore_flags(flags); + return(-1); + } + + /* Enable the keyboard by allowing the keyboard clock to run. */ + kbd_write(KBD_CNTL_REG, KBD_CNTL_ENABLE); + + /* + * Reset keyboard. If the read times out + * then the assumption is that no keyboard is + * plugged into the machine. + * This defaults the keyboard to scan-code set 2. + */ + kbd_write(KBD_DATA_REG, KBD_RESET); + if (kbd_wait_for_input() != KBD_ACK) { + printk(KERN_WARNING "initialize_kbd: " + "reset kbd failed, no ACK.\n"); + restore_flags(flags); + return(-1); + } + + if (kbd_wait_for_input() != KBD_POR) { + printk(KERN_WARNING "initialize_kbd: " + "reset kbd failed, not POR.\n"); + restore_flags(flags); + return(-1); + } + + /* + * now do a DEFAULTS_DISABLE always + */ + kbd_write(KBD_DATA_REG, KBD_DISABLE); + if (kbd_wait_for_input() != KBD_ACK) { + printk(KERN_WARNING "initialize_kbd: " + "disable kbd failed, no ACK.\n"); + restore_flags(flags); + return(-1); + } + + /* + * Enable keyboard interrupt, operate in "sys" mode, + * enable keyboard (by clearing the disable keyboard bit), + * disable mouse, do conversion of keycodes. + */ + kbd_write(KBD_CNTL_REG, KBD_WRITE_MODE); + kbd_write(KBD_DATA_REG, KBD_EKI|KBD_SYS|KBD_DMS|KBD_KCC); + + /* + * now ENABLE the keyboard to set it scanning... + */ + kbd_write(KBD_DATA_REG, KBD_ENABLE); + if (kbd_wait_for_input() != KBD_ACK) { + printk(KERN_WARNING "initialize_kbd: " + "keyboard enable failed.\n"); + restore_flags(flags); + return(-1); + } + + restore_flags(flags); + + return (1); +} +#endif /* INIT_KBD */ diff -u --recursive --new-file v2.1.39/linux/drivers/net/defxx.c linux/drivers/net/defxx.c --- v2.1.39/linux/drivers/net/defxx.c Wed Apr 23 19:01:19 1997 +++ linux/drivers/net/defxx.c Sun May 18 16:38:34 1997 @@ -226,6 +226,11 @@ #include "defxx.h" +#define DYNAMIC_BUFFERS 1 + +#define SKBUFF_RX_COPYBREAK 200 +#define NEW_SKB_SIZE (PI_RCV_DATA_K_SIZE_MAX) + /* Define global routines */ int dfx_probe(struct device *dev); @@ -1083,7 +1088,9 @@ alloc_size = sizeof(PI_DESCR_BLOCK) + PI_CMD_REQ_K_SIZE_MAX + PI_CMD_RSP_K_SIZE_MAX + +#ifndef DYNAMIC_BUFFERS (bp->rcv_bufs_to_post * PI_RCV_DATA_K_SIZE_MAX) + +#endif sizeof(PI_CONSUMER_BLOCK) + (PI_ALIGN_K_DESC_BLK - 1); top_v = (char *) kmalloc(alloc_size, GFP_KERNEL); @@ -1135,8 +1142,11 @@ bp->rcv_block_virt = curr_v; bp->rcv_block_phys = curr_p; + +#ifndef DYNAMIC_BUFFERS curr_v += (bp->rcv_bufs_to_post * PI_RCV_DATA_K_SIZE_MAX); curr_p += (bp->rcv_bufs_to_post * PI_RCV_DATA_K_SIZE_MAX); +#endif /* Reserve space for the consumer block */ @@ -2926,6 +2936,22 @@ * driver initialization when we allocated memory for the receive buffers. */ +#ifdef DYNAMIC_BUFFERS + for (i = 0; i < (int)(bp->rcv_bufs_to_post); i++) + for (j = 0; (i + j) < (int)PI_RCV_DATA_K_NUM_ENTRIES; j += bp->rcv_bufs_to_post) + { + struct sk_buff *newskb; + bp->descr_block_virt->rcv_data[i+j].long_0 = (u32) (PI_RCV_DESCR_M_SOP | + ((PI_RCV_DATA_K_SIZE_MAX / PI_ALIGN_K_RCV_DATA_BUFF) << PI_RCV_DESCR_V_SEG_LEN)); + newskb = dev_alloc_skb(NEW_SKB_SIZE); + bp->descr_block_virt->rcv_data[i+j].long_1 = virt_to_bus(newskb->data); + /* + * p_rcv_buff_va is only used inside the + * kernel so we put the skb pointer here. + */ + bp->p_rcv_buff_va[i+j] = (char *) newskb; + } +#else for (i=0; i < (int)(bp->rcv_bufs_to_post); i++) for (j=0; (i + j) < (int)PI_RCV_DATA_K_NUM_ENTRIES; j += bp->rcv_bufs_to_post) { @@ -2934,6 +2960,7 @@ bp->descr_block_virt->rcv_data[i+j].long_1 = (u32) (bp->rcv_block_phys + (i * PI_RCV_DATA_K_SIZE_MAX)); bp->p_rcv_buff_va[i+j] = (char *) (bp->rcv_block_virt + (i * PI_RCV_DATA_K_SIZE_MAX)); } +#endif /* Update receive producer and Type 2 register */ @@ -2985,6 +3012,8 @@ u32 descr, pkt_len; /* FMC descriptor field and packet length */ struct sk_buff *skb; /* pointer to a sk_buff to hold incoming packet data */ + static int testing_dyn; + /* Service all consumed LLC receive frames */ p_type_2_cons = (PI_TYPE_2_CONSUMER *)(&bp->cons_block_virt->xmt_rcv_data); @@ -2992,7 +3021,14 @@ { /* Process any errors */ - p_buff = (char *) bp->p_rcv_buff_va[bp->rcv_xmt_reg.index.rcv_comp]; + int entry; + + entry = bp->rcv_xmt_reg.index.rcv_comp; +#ifdef DYNAMIC_BUFFERS + p_buff = (char *) (((struct sk_buff *)bp->p_rcv_buff_va[entry])->data); +#else + p_buff = (char *) bp->p_rcv_buff_va[entry]; +#endif memcpy(&descr, p_buff + RCV_BUFF_K_DESCR, sizeof(u32)); if (descr & PI_FMC_DESCR_M_RCC_FLUSH) @@ -3003,29 +3039,60 @@ bp->rcv_frame_status_errors++; } else - { + { + int rx_in_place = 0; + /* The frame was received without errors - verify packet length */ pkt_len = (u32)((descr & PI_FMC_DESCR_M_LEN) >> PI_FMC_DESCR_V_LEN); pkt_len -= 4; /* subtract 4 byte CRC */ if (!IN_RANGE(pkt_len, FDDI_K_LLC_ZLEN, FDDI_K_LLC_LEN)) bp->rcv_length_errors++; - else - { - skb = dev_alloc_skb(pkt_len+3); /* alloc new buffer to pass up, add room for PRH */ + else{ +#ifdef DYNAMIC_BUFFERS + if (pkt_len > SKBUFF_RX_COPYBREAK) { + struct sk_buff *newskb; + + newskb = dev_alloc_skb(NEW_SKB_SIZE); + if (newskb){ + rx_in_place = 1; +#define JES_TESTING +#ifdef JES_TESTING + if(testing_dyn++ < 5) + printk("Skipping a memcpy\n"); + skb = (struct sk_buff *)bp->p_rcv_buff_va[entry]; + skb->data += RCV_BUFF_K_PADDING; + bp->p_rcv_buff_va[entry] = (char *)newskb; + bp->descr_block_virt->rcv_data[entry].long_1 = virt_to_bus(newskb->data); +#else + memcpy(newskb->data, p_buff + RCV_BUFF_K_PADDING, pkt_len+3); + skb = newskb; +#endif + } else + skb = 0; + } else +#endif + skb = dev_alloc_skb(pkt_len+3); /* alloc new buffer to pass up, add room for PRH */ if (skb == NULL) { printk("%s: Could not allocate receive buffer. Dropping packet.\n", bp->dev->name); bp->rcv_discards++; + break; } - else + else { +#ifndef DYNAMIC_BUFFERS + if (! rx_in_place) +#endif { - /* Receive buffer allocated, pass receive packet up */ + /* Receive buffer allocated, pass receive packet up */ + + memcpy(skb->data, p_buff + RCV_BUFF_K_PADDING, pkt_len+3); + } - memcpy(skb->data, p_buff + RCV_BUFF_K_PADDING, pkt_len+3); skb->data += 3; /* adjust data field so that it points to FC byte */ skb->len = pkt_len; /* pass up packet length, NOT including CRC */ skb->dev = bp->dev; /* pass up device pointer */ + skb->protocol = fddi_type_trans(skb, bp->dev); netif_rx(skb); @@ -3034,9 +3101,9 @@ bp->rcv_total_frames++; if (*(p_buff + RCV_BUFF_K_DA) & 0x01) bp->rcv_multicast_frames++; - } } } + } /* * Advance the producer (for recycling) and advance the completion @@ -3140,6 +3207,7 @@ dev->name, skb->len); bp->xmt_length_errors++; /* bump error counter */ dev_tint(dev); /* dequeue packets from xmt queue and send them */ + dev_kfree_skb(skb, FREE_WRITE); return(0); /* return "success" */ } /* diff -u --recursive --new-file v2.1.39/linux/drivers/net/dummy.c linux/drivers/net/dummy.c --- v2.1.39/linux/drivers/net/dummy.c Wed Apr 23 19:01:19 1997 +++ linux/drivers/net/dummy.c Sun May 18 16:38:34 1997 @@ -134,7 +134,7 @@ { /* Find a name for this unit */ int err=dev_alloc_name(&dev_dummy,"dummy%d"); - if(err) + if(err<0) return err; if (register_netdev(&dev_dummy) != 0) return -EIO; diff -u --recursive --new-file v2.1.39/linux/drivers/net/hp100.c linux/drivers/net/hp100.c --- v2.1.39/linux/drivers/net/hp100.c Wed Apr 23 19:01:19 1997 +++ linux/drivers/net/hp100.c Mon May 19 12:49:29 1997 @@ -1,89 +1,54 @@ /* - * hp100.c: Hewlett Packard HP10/100VG ANY LAN ethernet driver for Linux. - * - * Author: Jaroslav Kysela, - * - * Supports only the following Hewlett Packard cards: - * - * HP J2577 10/100 EISA card with REVA Cascade chip - * HP J2573 10/100 ISA card with REVA Cascade chip - * HP 27248B 10 only EISA card with Cascade chip - * HP J2577 10/100 EISA card with Cascade chip - * HP J2573 10/100 ISA card with Cascade chip - * HP J2585 10/100 PCI card - * - * Other ATT2MD01 Chip based boards might be supported in the future - * (there are some minor changes needed). - * - * This driver is based on the 'hpfepkt' crynwr packet driver. - * - * This source/code is public free; you can distribute it and/or modify - * it under terms of the GNU General Public License (published by the - * Free Software Foundation) either version two of this License, or any - * later version. - * ---------------------------------------------------------------------------- - * - * Note: Some routines (interrupt handling, transmit) assumes that - * there is the PERFORMANCE page selected... - * - * ---------------------------------------------------------------------------- - * - * If you are going to use the module version of this driver, you may - * change this values at the "insert time" : - * - * Variable Description - * - * hp100_rx_ratio Range 1-99 - onboard memory used for RX - * packets in %. - * hp100_priority_tx If this variable is nonzero - all outgoing - * packets will be transmitted as priority. - * hp100_port Adapter port (for example 0x380). - * - * ---------------------------------------------------------------------------- - * MY BEST REGARDS GOING TO: - * - * IPEX s.r.o which lend me two HP J2573 cards and - * the HP AdvanceStack 100VG Hub-15 for debugging. - * - * Russel Nellson for help with obtaining sources - * of the 'hpfepkt' packet driver. - * - * Also thanks to Abacus Electric s.r.o which let me to use their - * motherboard for my second computer. - * - * ---------------------------------------------------------------------------- - * - * TO DO: - * ====== - * - ioctl handling - some runtime setup things - * - 100Mb/s Voice Grade AnyLAN network adapter/hub services support - * - 802.5 frames - * - promiscuous mode - * - bridge mode - * - cascaded repeater mode - * - 100Mbit MAC - * - * Revision history: - * ================= - * - * Version Date Description - * - * 0.1 14-May-95 Initial writing. ALPHA code was released. - * Only HP J2573 on 10Mb/s (two machines) tested. - * 0.11 14-Jun-95 Reset interface bug fixed? - * Little bug in hp100_close function fixed. - * 100Mb/s connection debugged. - * 0.12 14-Jul-95 Link down is now handled better. - * 0.20 01-Aug-95 Added PCI support for HP J2585A card. - * Statistics bug fixed. - * 0.21 04-Aug-95 Memory mapped access support for PCI card. - * Added priority transmit support for 100Mb/s - * Voice Grade AnyLAN network. - * - */ +** hp100.c +** HP CASCADE Architecture Driver for 100VG-AnyLan Network Adapters +** +** $Id: hp100.c,v 1.52 1997/04/21 14:20:20 perex Exp perex $ +** +** Based on the HP100 driver written by Jaroslav Kysela +** Extended for new busmaster capable chipsets by +** Siegfried "Frieder" Loeffler (dg1sek) +** +** Maintained by: Jaroslav Kysela +** +** This driver has only been tested with +** -- HP J2585B 10/100 Mbit/s PCI Busmaster +** -- HP J2585A 10/100 Mbit/s PCI +** -- HP J2970 10 Mbit/s PCI Combo 10base-T/BNC +** -- HP J2973 10 Mbit/s PCI 10base-T +** -- HP J2573 10/100 ISA +** -- Compex ReadyLink ENET100-VG4 10/100 Mbit/s PCI / EISA +** +** but it should also work with the other CASCADE based adapters. +** +** TODO: +** - J2573 seems to hang sometimes when in shared memory mode. +** - Mode for Priority TX +** - Check PCI registers, performance might be improved? +** - To reduce interrupt load in busmaster, one could switch off +** the interrupts that are used to refill the queues whenever the +** queues are filled up to more than a certain threshold. +** +** +** This source/code is public free; you can distribute it and/or modify +** it under terms of the GNU General Public License (published by the +** Free Software Foundation) either version two of this License, or any +** later version. +** +*/ + +#define HP100_DEFAULT_PRIORITY_TX 0 + +#undef HP100_DEBUG +#undef HP100_DEBUG_B /* Trace */ +#undef HP100_DEBUG_BM /* Debug busmaster code (PDL stuff) */ + +#undef HP100_DEBUG_TRAINING /* Debug login-to-hub procedure */ +#undef HP100_DEBUG_TX +#undef HP100_DEBUG_IRQ +#undef HP100_DEBUG_RX +#include #include - #include #include #include @@ -102,7 +67,17 @@ #include #include -#include /* for CONFIG_PCI */ +#include /* for CONFIG_PCI */ +#include + +#if LINUX_VERSION_CODE < 0x020100 +#define ioremap vremap +#define iounmap vfree +typedef struct enet_statistics hp100_stats_t; +#else +#define LINUX_2_1 +typedef struct net_device_stats hp100_stats_t; +#endif #include "hp100.h" @@ -110,18 +85,28 @@ * defines */ -#define HP100_BUS_ISA 0 -#define HP100_BUS_EISA 1 -#define HP100_BUS_PCI 2 +#define HP100_BUS_ISA 0 +#define HP100_BUS_EISA 1 +#define HP100_BUS_PCI 2 -#define HP100_REGION_SIZE 0x20 +#ifndef PCI_DEVICE_ID_HP_J2585B +#define PCI_DEVICE_ID_HP_J2585B 0x1031 +#endif +#ifndef PCI_VENDOR_ID_COMPEX +#define PCI_VENDOR_ID_COMPEX 0x11f6 +#endif +#ifndef PCI_DEVICE_ID_COMPEX_ENET100VG4 +#define PCI_DEVICE_ID_COMPEX_ENET100VG4 0x0112 +#endif + +#define HP100_REGION_SIZE 0x20 /* for ioports */ -#define HP100_MAX_PACKET_SIZE (1536+4) -#define HP100_MIN_PACKET_SIZE 60 +#define HP100_MAX_PACKET_SIZE (1536+4) +#define HP100_MIN_PACKET_SIZE 60 #ifndef HP100_DEFAULT_RX_RATIO -/* default - 65% onboard memory on the card are used for RX packets */ -#define HP100_DEFAULT_RX_RATIO 65 +/* default - 75% onboard memory on the card are used for RX packets */ +#define HP100_DEFAULT_RX_RATIO 75 #endif #ifndef HP100_DEFAULT_PRIORITY_TX @@ -141,18 +126,37 @@ struct hp100_private { struct hp100_eisa_id *id; + u_short chip; u_short soft_model; - u_int memory_size; - u_short rx_ratio; /* 1 - 99 */ - u_short priority_tx; /* != 0 - priority tx */ - short mem_mapped; /* memory mapped access */ - u_char *mem_ptr_virt; /* virtual memory mapped area, maybe NULL */ - u_char *mem_ptr_phys; /* physical memory mapped area */ - short lan_type; /* 10Mb/s, 100Mb/s or -1 (error) */ - int hub_status; /* login to hub was successful? */ + u_int memory_size; + u_short rx_ratio; /* 1 - 99 */ + u_short priority_tx; /* != 0 - priority tx */ + u_short mode; /* PIO, Shared Mem or Busmaster */ + u_char bus; + u_char pci_bus; + u_char pci_device_fn; + short mem_mapped; /* memory mapped access */ + u_int *mem_ptr_virt; /* virtual memory mapped area, maybe NULL */ + u_int *mem_ptr_phys; /* physical memory mapped area */ + short lan_type; /* 10Mb/s, 100Mb/s or -1 (error) */ + int hub_status; /* was login to hub successful? */ u_char mac1_mode; u_char mac2_mode; - struct net_device_stats stats; + hp100_stats_t stats; + + /* Rings for busmaster mode: */ + hp100_ring_t *rxrhead; /* Head (oldest) index into rxring */ + hp100_ring_t *rxrtail; /* Tail (newest) index into rxring */ + hp100_ring_t *txrhead; /* Head (oldest) index into txring */ + hp100_ring_t *txrtail; /* Tail (newest) index into txring */ + + hp100_ring_t rxring[ MAX_RX_PDL ]; + hp100_ring_t txring[ MAX_TX_PDL ]; + + u_int *page_vaddr; /* Virtual address of allocated page */ + u_int *page_vaddr_algn; /* Aligned virtual address of allocated page */ + int rxrcommit; /* # Rx PDLs commited to adapter */ + int txrcommit; /* # Tx PDLs commited to adapter */ }; /* @@ -161,78 +165,124 @@ static struct hp100_eisa_id hp100_eisa_ids[] = { - /* 10/100 EISA card with REVA Cascade chip */ - { 0x080F1F022, "HP J2577 rev A", HP100_BUS_EISA }, + /* 10/100 EISA card with revision A Cascade chip */ + { 0x80F1F022, "HP J2577 rev A", HP100_BUS_EISA }, - /* 10/100 ISA card with REVA Cascade chip */ - { 0x050F1F022, "HP J2573 rev A", HP100_BUS_ISA }, + /* 10/100 ISA card with revision A Cascade chip */ + { 0x50F1F022, "HP J2573 rev A", HP100_BUS_ISA }, /* 10 only EISA card with Cascade chip */ - { 0x02019F022, "HP 27248B", HP100_BUS_EISA }, + { 0x2019F022, "HP 27248B", HP100_BUS_EISA }, /* 10/100 EISA card with Cascade chip */ - { 0x04019F022, "HP J2577", HP100_BUS_EISA }, + { 0x4019F022, "HP J2577", HP100_BUS_EISA }, /* 10/100 ISA card with Cascade chip */ - { 0x05019F022, "HP J2573", HP100_BUS_ISA }, + { 0x5019F022, "HP J2573", HP100_BUS_ISA }, + + /* 10/100 PCI card - old J2585A */ + { 0x1030103c, "HP J2585A", HP100_BUS_PCI }, + + /* 10/100 PCI card - new J2585B - master capable */ + { 0x1041103c, "HP J2585B", HP100_BUS_PCI }, + + /* 10 Mbit Combo Adapter */ + { 0x1042103c, "HP J2970", HP100_BUS_PCI }, - /* 10/100 PCI card */ - /* Note: ID for this card is same as PCI vendor/device numbers. */ - { 0x01030103c, "HP J2585", HP100_BUS_PCI }, + /* 10 Mbit 10baseT Adapter */ + { 0x1040103c, "HP J2973", HP100_BUS_PCI }, + + /* 10/100 EISA card from Compex */ + { 0x0103180e, "ReadyLink ENET100-VG4", HP100_BUS_EISA }, + + /* 10/100 PCI card from Compex (J2585A compatible) */ + { 0x011211f6, "ReadyLink ENET100-VG4", HP100_BUS_PCI } }; static int hp100_rx_ratio = HP100_DEFAULT_RX_RATIO; static int hp100_priority_tx = HP100_DEFAULT_PRIORITY_TX; +static int hp100_mode = 1; + +#ifdef LINUX_2_1 +MODULE_PARM( hp100_rx_ratio, "1i" ); +MODULE_PARM( hp100_priority_tx, "1i" ); +MODULE_PARM( hp100_mode, "1i" ); +#endif /* * prototypes */ -static int hp100_probe1( struct device *dev, int ioaddr, int bus ); -static int hp100_open( struct device *dev ); -static int hp100_close( struct device *dev ); -static int hp100_start_xmit( struct sk_buff *skb, struct device *dev ); +static int hp100_probe1( struct device *dev, int ioaddr, u_char bus, u_char pci_bus, u_char pci_device_fn ); +static int hp100_open( struct device *dev ); +static int hp100_close( struct device *dev ); +static int hp100_start_xmit( struct sk_buff *skb, struct device *dev ); +static int hp100_start_xmit_bm (struct sk_buff *skb, struct device *dev ); static void hp100_rx( struct device *dev ); -static struct net_device_stats *hp100_get_stats( struct device *dev ); +static hp100_stats_t *hp100_get_stats( struct device *dev ); static void hp100_update_stats( struct device *dev ); static void hp100_clear_stats( int ioaddr ); static void hp100_set_multicast_list( struct device *dev); static void hp100_interrupt( int irq, void *dev_id, struct pt_regs *regs ); - static void hp100_start_interface( struct device *dev ); static void hp100_stop_interface( struct device *dev ); static void hp100_load_eeprom( struct device *dev ); -static int hp100_sense_lan( struct device *dev ); -static int hp100_login_to_vg_hub( struct device *dev ); -static int hp100_down_vg_link( struct device *dev ); +static int hp100_sense_lan( struct device *dev ); +static int hp100_login_to_vg_hub( struct device *dev, u_short force_relogin ); +static int hp100_down_vg_link( struct device *dev ); +static void hp100_cascade_reset( struct device *dev, u_short enable ); +static void hp100_BM_shutdown( struct device *dev ); +static void hp100_mmuinit( struct device *dev ); +static void hp100_init_pdls( struct device *dev ); +static int hp100_init_rxpdl( register hp100_ring_t *ringptr, register u_int *pdlptr); +static int hp100_init_txpdl( register hp100_ring_t *ringptr, register u_int *pdlptr); +static void hp100_rxfill( struct device *dev ); +static void hp100_hwinit( struct device *dev ); +static void hp100_clean_txring( struct device *dev ); +#ifdef HP100_DEBUG +static void hp100_RegisterDump( struct device *dev ); +#endif + +/* TODO: This function should not really be needed in a good design... */ +static void wait( void ) +{ + udelay( 1000 ); +} /* * probe functions + * These functions should - if possible - avoid doing write operations + * since this could cause problems when the card is not installed. */ __initfunc(int hp100_probe( struct device *dev )) { int base_addr = dev ? dev -> base_addr : 0; - int ioaddr; + int ioaddr = 0; #ifdef CONFIG_PCI int pci_start_index = 0; #endif +#ifdef HP100_DEBUG_B + hp100_outw( 0x4200, TRACE ); + printk( "hp100: probe\n" ); +#endif + if ( base_addr > 0xff ) /* Check a single specified location. */ { if ( check_region( base_addr, HP100_REGION_SIZE ) ) return -EINVAL; if ( base_addr < 0x400 ) - return hp100_probe1( dev, base_addr, HP100_BUS_ISA ); - else - return hp100_probe1( dev, base_addr, HP100_BUS_EISA ); + return hp100_probe1( dev, base_addr, HP100_BUS_ISA, 0, 0 ); + else + return hp100_probe1( dev, base_addr, HP100_BUS_EISA, 0, 0 ); } - else + else #ifdef CONFIG_PCI - if ( base_addr > 0 && base_addr < 8 + 1 ) - pci_start_index = 0x100 | ( base_addr - 1 ); - else + if ( base_addr > 0 && base_addr < 8 + 1 ) + pci_start_index = 0x100 | ( base_addr - 1 ); + else #endif - if ( base_addr != 0 ) return -ENXIO; + if ( base_addr != 0 ) return -ENXIO; /* at first - scan PCI bus(es) */ @@ -249,65 +299,80 @@ u_char pci_bus, pci_device_fn; u_short pci_command; - if ( pcibios_find_device( PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585A, - pci_index, &pci_bus, - &pci_device_fn ) != 0 ) break; + if ((pcibios_find_device( PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585A, + pci_index, &pci_bus, + &pci_device_fn ) != 0 ) && + (pcibios_find_device( PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585B, + pci_index, &pci_bus, + &pci_device_fn ) != 0 ) && + (pcibios_find_device( PCI_VENDOR_ID_COMPEX, PCI_DEVICE_ID_COMPEX_ENET100VG4, + pci_index, &pci_bus, + &pci_device_fn ) != 0 ) ) break; + pcibios_read_config_dword( pci_bus, pci_device_fn, - PCI_BASE_ADDRESS_0, &ioaddr ); + PCI_BASE_ADDRESS_0, &ioaddr ); - ioaddr &= ~3; /* remove I/O space marker in bit 0. */ + ioaddr &= ~3; /* remove I/O space marker in bit 0. */ if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue; pcibios_read_config_word( pci_bus, pci_device_fn, - PCI_COMMAND, &pci_command ); + PCI_COMMAND, &pci_command ); if ( !( pci_command & PCI_COMMAND_MASTER ) ) { -#ifdef HP100_DEBUG_PCI +#ifdef HP100_DEBUG printk( "hp100: PCI Master Bit has not been set. Setting...\n" ); #endif pci_command |= PCI_COMMAND_MASTER; pcibios_write_config_word( pci_bus, pci_device_fn, - PCI_COMMAND, pci_command ); + PCI_COMMAND, pci_command ); } -#ifdef HP100_DEBUG_PCI +#ifdef HP100_DEBUG printk( "hp100: PCI adapter found at 0x%x\n", ioaddr ); #endif - if ( hp100_probe1( dev, ioaddr, HP100_BUS_PCI ) == 0 ) return 0; + if ( hp100_probe1( dev, ioaddr, HP100_BUS_PCI, pci_bus, pci_device_fn ) == 0 ) + return 0; } } if ( pci_start_index > 0 ) return -ENODEV; #endif /* CONFIG_PCI */ - /* at second - probe all EISA possible port regions (if EISA bus present) */ - + /* Second: Probe all EISA possible port regions (if EISA bus present) */ for ( ioaddr = 0x1c38; EISA_bus && ioaddr < 0x10000; ioaddr += 0x400 ) { if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue; - if ( hp100_probe1( dev, ioaddr, HP100_BUS_EISA ) == 0 ) return 0; + if ( hp100_probe1( dev, ioaddr, HP100_BUS_EISA, 0, 0 ) == 0 ) return 0; } - /* at third - probe all ISA possible port regions */ - + /* Third Probe all ISA possible port regions */ for ( ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x20 ) { if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue; - if ( hp100_probe1( dev, ioaddr, HP100_BUS_ISA ) == 0 ) return 0; + if ( hp100_probe1( dev, ioaddr, HP100_BUS_ISA, 0, 0 ) == 0 ) return 0; } return -ENODEV; } -__initfunc(static int hp100_probe1( struct device *dev, int ioaddr, int bus )) + +__initfunc(static int hp100_probe1( struct device *dev, int ioaddr, u_char bus, u_char pci_bus, u_char pci_device_fn )) { int i; + u_char uc, uc_1; u_int eisa_id; + u_int chip; + u_int memory_size = 0; short mem_mapped; - u_char *mem_ptr_phys, *mem_ptr_virt; + u_int *mem_ptr_phys, *mem_ptr_virt; struct hp100_private *lp; struct hp100_eisa_id *eid; +#ifdef HP100_DEBUG_B + hp100_outw( 0x4201, TRACE ); + printk("hp100: probe1\n"); +#endif + if ( dev == NULL ) { #ifdef HP100_DEBUG @@ -315,19 +380,27 @@ #endif return EIO; } + + if ( hp100_inw( HW_ID ) != HP100_HW_ID_CASCADE ) + { + return -ENODEV; + } + else + { + chip = hp100_inw( PAGING ) & HP100_CHIPID_MASK; +#ifdef HP100_DEBUG + if ( chip == HP100_CHIPID_SHASTA ) + printk("hp100: Shasta Chip detected. (This is a pre 802.12 chip)\n"); + else if ( chip == HP100_CHIPID_RAINIER ) + printk("hp100: Rainier Chip detected. (This is a pre 802.12 chip)\n"); + else if ( chip == HP100_CHIPID_LASSEN ) + printk("hp100: Lassen Chip detected.\n"); + else + printk("hp100: Warning: Unknown CASCADE chip (id=0x%.4x).\n",chip); +#endif + } - if ( bus != HP100_BUS_PCI ) /* don't check PCI cards again */ - if ( inb( ioaddr + 0 ) != HP100_HW_ID_0 || - inb( ioaddr + 1 ) != HP100_HW_ID_1 || - ( inb( ioaddr + 2 ) & 0xf0 ) != HP100_HW_ID_2_REVA || - inb( ioaddr + 3 ) != HP100_HW_ID_3 ) - return -ENODEV; - - dev -> base_addr = ioaddr; - -#ifdef HP100_DEBUG_PROBE1 - printk( "hp100_probe1: card found at port 0x%x\n", ioaddr ); -#endif + dev->base_addr = ioaddr; hp100_page( ID_MAC_ADDR ); for ( i = uc = eisa_id = 0; i < 4; i++ ) @@ -339,17 +412,13 @@ } uc += hp100_inb( BOARD_ID + 4 ); -#ifdef HP100_DEBUG_PROBE1 - printk( "hp100_probe1: EISA ID = 0x%08x checksum = 0x%02x\n", eisa_id, uc ); -#endif - - if ( uc != 0xff ) /* bad checksum? */ + if ( uc != 0xff ) /* bad checksum? */ { - printk( "hp100_probe: bad EISA ID checksum at base port 0x%x\n", ioaddr ); + printk("hp100_probe: bad EISA ID checksum at base port 0x%x\n", ioaddr ); return -ENODEV; } - for ( i = 0; i < sizeof( hp100_eisa_ids ) / sizeof( struct hp100_eisa_id ); i++ ) + for ( i=0; i= sizeof( hp100_eisa_ids ) / sizeof( struct hp100_eisa_id ) ) @@ -358,10 +427,10 @@ return -ENODEV; } eid = &hp100_eisa_ids[ i ]; - if ( ( eid -> id & 0x0f000000 ) < ( eisa_id & 0x0f000000 ) ) + if ( ( eid->id & 0x0f000000 ) < ( eisa_id & 0x0f000000 ) ) { printk( "hp100_probe1: newer version of card %s at port 0x%x - unsupported\n", - eid -> name, ioaddr ); + eid->name, ioaddr ); return -ENODEV; } @@ -369,461 +438,1592 @@ uc += hp100_inb( LAN_ADDR + i ); if ( uc != 0xff ) { - printk( "hp100_probe1: bad lan address checksum (card %s at port 0x%x)\n", - eid -> name, ioaddr ); + printk("hp100_probe1: bad lan address checksum (card %s at port 0x%x)\n", + eid->name, ioaddr ); return -EIO; } -#ifndef HP100_IO_MAPPED + /* Determine driver operation mode + * + * Use the variable "hp100_mode" upon insmod or as kernel parameter to + * force driver modes: + * hp100_mode=1 -> default, use busmaster mode if configured. + * hp100_mode=2 -> enable shared memory mode + * hp100_mode=3 -> force use of i/o mapped mode. + * hp100_mode=4 -> same as 1, but re-set the enable bit on the card. + */ + + if(hp100_mode==3) + { + hp100_outw(HP100_MEM_EN|HP100_RESET_LB, OPTION_LSW); + hp100_outw(HP100_IO_EN|HP100_SET_LB, OPTION_LSW); + hp100_outw(HP100_BM_WRITE|HP100_BM_READ|HP100_RESET_HB, OPTION_LSW); + printk("hp100: IO mapped mode forced.\n"); + } + else if(hp100_mode==2) + { + hp100_outw(HP100_MEM_EN|HP100_SET_LB, OPTION_LSW); + hp100_outw(HP100_IO_EN |HP100_SET_LB, OPTION_LSW); + hp100_outw(HP100_BM_WRITE|HP100_BM_READ|HP100_RESET_HB, OPTION_LSW); + printk("hp100: Shared memory mode requested.\n"); + } + else if(hp100_mode==4) + { + if(chip==HP100_CHIPID_LASSEN) + { + hp100_outw(HP100_BM_WRITE| + HP100_BM_READ | HP100_SET_HB, OPTION_LSW); + hp100_outw(HP100_IO_EN | + HP100_MEM_EN | HP100_RESET_LB, OPTION_LSW); + printk("hp100: Busmaster mode requested.\n"); + } + hp100_mode=1; + } + + if(hp100_mode==1) /* default behaviour */ + { + if( (hp100_inw(OPTION_LSW)&HP100_IO_EN) && + (~hp100_inw(OPTION_LSW)&HP100_MEM_EN) && + (~hp100_inw(OPTION_LSW)&(HP100_BM_WRITE|HP100_BM_READ)) + ) + { +#ifdef HP100_DEBUG + printk("hp100: IO_EN bit is set on card.\n"); +#endif + hp100_mode=3; + } + else if( ( chip==HP100_CHIPID_LASSEN ) && + ( (hp100_inw(OPTION_LSW)&(HP100_BM_WRITE|HP100_BM_READ) ) == + (HP100_BM_WRITE|HP100_BM_READ) ) ) + { + printk("hp100: Busmaster mode enabled.\n"); + hp100_outw(HP100_MEM_EN|HP100_IO_EN|HP100_RESET_LB, OPTION_LSW); + } + else + { +#ifdef HP100_DEBUG + printk("hp100: Card not configured for BM or BM not supported with this card. Trying shared memory mode.\n"); +#endif + /* In this case, try shared memory mode */ + hp100_mode=2; + hp100_outw(HP100_MEM_EN|HP100_SET_LB, OPTION_LSW); + /* hp100_outw(HP100_IO_EN|HP100_RESET_LB, OPTION_LSW); */ + } + } + + /* Check for shared memory on the card, eventually remap it */ hp100_page( HW_MAP ); - mem_mapped = ( hp100_inw( OPTION_LSW ) & - ( HP100_MEM_EN | HP100_BM_WRITE | HP100_BM_READ ) ) != 0; + mem_mapped = (( hp100_inw( OPTION_LSW ) & ( HP100_MEM_EN ) ) != 0); mem_ptr_phys = mem_ptr_virt = NULL; - if ( mem_mapped ) + memory_size = (8192<<( (hp100_inb(SRAM)>>5)&0x07)); + + /* For memory mapped or busmaster mode, we want the memory address */ + if ( mem_mapped || (hp100_mode==1)) { - mem_ptr_phys = (u_char *)( hp100_inw( MEM_MAP_LSW ) | - ( hp100_inw( MEM_MAP_MSW ) << 16 ) ); - (u_int)mem_ptr_phys &= ~0x1fff; /* 8k alignment */ + mem_ptr_phys = (u_int *)( hp100_inw( MEM_MAP_LSW ) | + ( hp100_inw( MEM_MAP_MSW ) << 16 ) ); + (u_int)mem_ptr_phys &= ~0x1fff; /* 8k alignment */ + if ( bus == HP100_BUS_ISA && ( (u_long)mem_ptr_phys & ~0xfffff ) != 0 ) { + printk("hp100: Can only use programmed i/o mode.\n"); mem_ptr_phys = NULL; mem_mapped = 0; + hp100_mode=3; /* Use programmed i/o */ } - if ( mem_mapped && bus == HP100_BUS_PCI ) - { - if ( ( mem_ptr_virt = ioremap( (u_long)mem_ptr_phys, 0x2000 ) ) == NULL ) - { - printk( "hp100: ioremap for high PCI memory at 0x%lx failed\n", (u_long)mem_ptr_phys ); - mem_ptr_phys = NULL; - mem_mapped = 0; - } - } - } -#else - mem_mapped = 0; - mem_ptr_phys = mem_ptr_virt = NULL; + + /* We do not need access to shared memory in busmaster mode */ + /* However in slave mode we need to remap high (>1GB) card memory */ + if(hp100_mode!=1) /* = not busmaster */ + { + if ( bus == HP100_BUS_PCI ) + { + /* We try with smaller memory sizes, if ioremap fails */ + for(; memory_size>16383; memory_size=memory_size/2) + { + if((mem_ptr_virt=ioremap((u_long)mem_ptr_phys,memory_size))==NULL) + { +#ifdef HP100_DEBUG + printk( "hp100: ioremap for 0x%x bytes high PCI memory at 0x%lx failed\n", memory_size, (u_long)mem_ptr_phys ); #endif + } + else + { +#ifdef HP100_DEBUG + printk( "hp100: remapped 0x%x bytes high PCI memory at 0x%lx to 0x%lx.\n", memory_size, (u_long)mem_ptr_phys, (u_long)mem_ptr_virt); +#endif + break; + } + } + + if(mem_ptr_virt==NULL) /* all ioremap tries failed */ + { + printk("hp100: Failed to ioremap the PCI card memory. Will have to use i/o mapped mode.\n"); + hp100_mode=3; + memory_size = (8192<<( (hp100_inb(SRAM)>>5)&0x07) ); + } + } + } + + } + + if(hp100_mode==3) /* io mapped forced */ + { + mem_mapped = 0; + mem_ptr_phys = mem_ptr_virt = NULL; + printk("hp100: Using (slow) programmed i/o mode.\n"); + } - if ( ( dev -> priv = kmalloc( sizeof( struct hp100_private ), GFP_KERNEL ) ) == NULL ) + /* Initialise the "private" data structure for this card. */ + if ( (dev->priv=kmalloc(sizeof(struct hp100_private), GFP_KERNEL)) == NULL) return -ENOMEM; - memset( dev -> priv, 0, sizeof( struct hp100_private ) ); + memset( dev->priv, 0, sizeof(struct hp100_private) ); - lp = (struct hp100_private *)dev -> priv; - lp -> id = eid; - lp -> mem_mapped = mem_mapped; - lp -> mem_ptr_phys = mem_ptr_phys; - lp -> mem_ptr_virt = mem_ptr_virt; + lp = (struct hp100_private *)dev->priv; + lp->id = eid; + lp->chip = chip; + lp->mode = hp100_mode; + lp->pci_bus = pci_bus; + lp->bus = bus; + lp->pci_device_fn = pci_device_fn; + lp->priority_tx = hp100_priority_tx; + lp->rx_ratio = hp100_rx_ratio; + lp->mem_ptr_phys = mem_ptr_phys; + lp->mem_ptr_virt = mem_ptr_virt; hp100_page( ID_MAC_ADDR ); - lp -> soft_model = hp100_inb( SOFT_MODEL ); - lp -> mac1_mode = HP100_MAC1MODE3; - lp -> mac2_mode = HP100_MAC2MODE3; + lp->soft_model = hp100_inb( SOFT_MODEL ); + lp->mac1_mode = HP100_MAC1MODE3; + lp->mac2_mode = HP100_MAC2MODE3; + + dev->base_addr = ioaddr; + + lp->memory_size = memory_size; + lp->rx_ratio = hp100_rx_ratio; /* can be conf'd with insmod */ + + /* memory region for programmed i/o */ + request_region( dev->base_addr, HP100_REGION_SIZE, eid->name ); + + dev->open = hp100_open; + dev->stop = hp100_close; + + if (lp->mode==1) /* busmaster */ + dev->hard_start_xmit = hp100_start_xmit_bm; + else + dev->hard_start_xmit = hp100_start_xmit; - dev -> base_addr = ioaddr; + dev->get_stats = hp100_get_stats; + dev->set_multicast_list = &hp100_set_multicast_list; + + /* Ask the card for which IRQ line it is configured */ hp100_page( HW_MAP ); - dev -> irq = hp100_inb( IRQ_CHANNEL ) & HP100_IRQ_MASK; - if ( dev -> irq == 2 ) dev -> irq = 9; - lp -> memory_size = 0x200 << ( ( hp100_inb( SRAM ) & 0xe0 ) >> 5 ); - lp -> rx_ratio = hp100_rx_ratio; - - dev -> open = hp100_open; - dev -> stop = hp100_close; - dev -> hard_start_xmit = hp100_start_xmit; - dev -> get_stats = hp100_get_stats; - dev -> set_multicast_list = &hp100_set_multicast_list; + dev->irq = hp100_inb( IRQ_CHANNEL ) & HP100_IRQMASK; + if ( dev->irq == 2 ) + dev->irq = 9; - request_region( dev -> base_addr, HP100_REGION_SIZE, eid -> name ); + if(lp->mode==1) /* busmaster */ + dev->dma=4; + /* Ask the card for its MAC address and store it for later use. */ hp100_page( ID_MAC_ADDR ); for ( i = uc = 0; i < 6; i++ ) - dev -> dev_addr[ i ] = hp100_inb( LAN_ADDR + i ); + dev->dev_addr[ i ] = hp100_inb( LAN_ADDR + i ); + /* Reset statistics (counters) */ hp100_clear_stats( ioaddr ); ether_setup( dev ); - lp -> lan_type = hp100_sense_lan( dev ); - - printk( "%s: %s at 0x%x, IRQ %d, ", - dev -> name, lp -> id -> name, ioaddr, dev -> irq ); + /* If busmaster mode is wanted, a dma-capable memory area is needed for + * the rx and tx PDLs + * PCI cards can access the whole PC memory. Therefore GFP_DMA is not + * needed for the allocation of the memory area. + */ + + /* TODO: We do not need this with old cards, where PDLs are stored + * in the cards shared memory area. But currently, busmaster has been + * implemented/tested only with the lassen chip anyway... */ + if(lp->mode==1) /* busmaster */ + { + /* Get physically continous memory for TX & RX PDLs */ + if ( (lp->page_vaddr=kmalloc(MAX_RINGSIZE+0x0f,GFP_KERNEL) ) == NULL) + return -ENOMEM; + lp->page_vaddr_algn=((u_int *) ( ((u_int)(lp->page_vaddr)+0x0f) &~0x0f)); + memset(lp->page_vaddr, 0, MAX_RINGSIZE+0x0f); + +#ifdef HP100_DEBUG_BM + printk("hp100: Reserved DMA memory from 0x%x to 0x%x\n", + (u_int)lp->page_vaddr_algn, + (u_int)lp->page_vaddr_algn+MAX_RINGSIZE); +#endif + lp->rxrcommit = lp->txrcommit = 0; + lp->rxrhead = lp->rxrtail = &(lp->rxring[0]); + lp->txrhead = lp->txrtail = &(lp->txring[0]); + } + + /* Initialise the card. */ + /* (I'm not really sure if it's a good idea to do this during probing, but + * like this it's assured that the lan connection type can be sensed + * correctly) + */ + hp100_hwinit( dev ); + + /* Try to find out which kind of LAN the card is connected to. */ + lp->lan_type = hp100_sense_lan( dev ); + + /* Print out a message what about what we think we have probed. */ + printk( "hp100: %s: %s at 0x%x, IRQ %d, ", + dev->name, lp->id->name, ioaddr, dev->irq ); switch ( bus ) { - case HP100_BUS_EISA: printk( "EISA" ); break; - case HP100_BUS_PCI: printk( "PCI" ); break; - default: printk( "ISA" ); break; + case HP100_BUS_EISA: printk( "EISA" ); break; + case HP100_BUS_PCI: printk( "PCI" ); break; + default: printk( "ISA" ); break; } printk( " bus, %dk SRAM (rx/tx %d%%).\n", - lp -> memory_size >> ( 10 - 4 ), lp -> rx_ratio ); - if ( mem_mapped ) + lp->memory_size >> 10, lp->rx_ratio ); + + if ( lp->mode==2 ) /* memory mapped */ { printk( "%s: Memory area at 0x%lx-0x%lx", - dev -> name, (u_long)mem_ptr_phys, (u_long)mem_ptr_phys + 0x1fff ); + dev->name,(u_long)mem_ptr_phys,(u_long)mem_ptr_phys+(u_long)lp->memory_size ); if ( mem_ptr_virt ) - printk( " (virtual base 0x%lx)", (u_long)mem_ptr_virt ); + printk( " (virtual base 0x%lx)", (u_long)mem_ptr_virt ); printk( ".\n" ); + + /* Set for info when doing ifconfig */ + dev->mem_start = (u_long)mem_ptr_phys; + dev->mem_end = (u_long)mem_ptr_phys+(u_long)lp->memory_size; } - printk( "%s: ", dev -> name ); - if ( lp -> lan_type != HP100_LAN_ERR ) + printk( "%s: ", dev->name ); + if ( lp->lan_type != HP100_LAN_ERR ) printk( "Adapter is attached to " ); - switch ( lp -> lan_type ) { - case HP100_LAN_100: - printk( "100Mb/s Voice Grade AnyLAN network.\n" ); - break; - case HP100_LAN_10: - printk( "10Mb/s network.\n" ); - break; - default: - printk( "Warning! Link down.\n" ); + switch ( lp->lan_type ) { + case HP100_LAN_100: + printk( "100Mb/s Voice Grade AnyLAN network.\n" ); + break; + case HP100_LAN_10: + printk( "10Mb/s network.\n" ); + break; + default: + printk( "Warning! Link down.\n" ); } + return 0; +} - hp100_stop_interface( dev ); + +/* This procedure puts the card into a stable init state */ +static void hp100_hwinit( struct device *dev ) +{ + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; - return 0; +#ifdef HP100_DEBUG_B + hp100_outw( 0x4202, TRACE ); + printk("hp100: hwinit\n"); +#endif + + /* Initialise the card. -------------------------------------------- */ + + /* Clear all pending Ints and disable Ints */ + hp100_page( PERFORMANCE ); + hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */ + hp100_outw( 0xffff, IRQ_STATUS ); /* clear all pending ints */ + + hp100_outw( HP100_INT_EN | HP100_RESET_LB, OPTION_LSW ); + hp100_outw( HP100_TRI_INT | HP100_SET_HB, OPTION_LSW ); + + if(lp->mode==1) + { + hp100_BM_shutdown( dev ); /* disables BM, puts cascade in reset */ + wait(); + } + else + { + hp100_outw( HP100_INT_EN | HP100_RESET_LB, OPTION_LSW ); + hp100_cascade_reset( dev, TRUE ); + hp100_page( MAC_CTRL ); + hp100_andb( ~(HP100_RX_EN|HP100_TX_EN), MAC_CFG_1); + } + + /* Initiate EEPROM reload */ + hp100_load_eeprom( dev ); + + wait(); + + /* Go into reset again. */ + hp100_cascade_reset( dev, TRUE ); + + /* Set Option Registers to a safe state */ + hp100_outw( HP100_DEBUG_EN | + HP100_RX_HDR | + HP100_EE_EN | + HP100_BM_WRITE | + HP100_BM_READ | HP100_RESET_HB | + HP100_FAKE_INT | + HP100_INT_EN | + HP100_MEM_EN | + HP100_IO_EN | HP100_RESET_LB, OPTION_LSW); + + hp100_outw( HP100_TRI_INT | + HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW ); + + hp100_outb( HP100_PRIORITY_TX | + HP100_ADV_NXT_PKT | + HP100_TX_CMD | HP100_RESET_LB, OPTION_MSW ); + + /* TODO: Configure MMU for Ram Test. */ + /* TODO: Ram Test. */ + + /* Re-check if adapter is still at same i/o location */ + /* (If the base i/o in eeprom has been changed but the */ + /* registers had not been changed, a reload of the eeprom */ + /* would move the adapter to the address stored in eeprom */ + + /* TODO: Code to implement. */ + + /* Until here it was code from HWdiscover procedure. */ + /* Next comes code from mmuinit procedure of SCO BM driver which is + * called from HWconfigure in the SCO driver. */ + + /* Initialise MMU, eventually switch on Busmaster Mode, initialise + * multicast filter... + */ + hp100_mmuinit( dev ); + + /* We don't turn the interrupts on here - this is done by start_interface. */ + wait(); /* TODO: Do we really need this? */ + + /* Enable Hardware (e.g. unreset) */ + hp100_cascade_reset( dev, FALSE ); + + /* ------- initialisation complete ----------- */ + + /* Finally try to log in the Hub if there may be a VG connection. */ + if( lp->lan_type != HP100_LAN_10 ) + hp100_login_to_vg_hub( dev, FALSE ); /* relogin */ } -/* - * open/close functions + +/* + * mmuinit - Reinitialise Cascade MMU and MAC settings. + * Note: Must already be in reset and leaves card in reset. */ - -static int hp100_open( struct device *dev ) +static void hp100_mmuinit( struct device *dev ) { + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; int i; - int ioaddr = dev -> base_addr; - struct hp100_private *lp = (struct hp100_private *)dev -> priv; - if ( request_irq( dev -> irq, hp100_interrupt, SA_INTERRUPT, lp -> id -> name, NULL ) ) +#ifdef HP100_DEBUG_B + hp100_outw( 0x4203, TRACE ); + printk("hp100: mmuinit\n"); +#endif + +#ifdef HP100_DEBUG + if( 0!=(hp100_inw(OPTION_LSW)&HP100_HW_RST) ) { - printk( "%s: unable to get IRQ %d\n", dev -> name, dev -> irq ); - return -EAGAIN; + printk("hp100: Not in reset when entering mmuinit. Fix me.\n"); + return; } - irq2dev_map[ dev -> irq ] = dev; +#endif - MOD_INC_USE_COUNT; + /* Make sure IRQs are masked off and ack'ed. */ + hp100_page( PERFORMANCE ); + hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */ + hp100_outw( 0xffff, IRQ_STATUS ); /* ack IRQ */ - dev -> tbusy = 0; - dev -> trans_start = jiffies; - dev -> interrupt = 0; - dev -> start = 1; - - lp -> lan_type = hp100_sense_lan( dev ); - lp -> mac1_mode = HP100_MAC1MODE3; - lp -> mac2_mode = HP100_MAC2MODE3; + /* + * Enable Hardware + * - Clear Debug En, Rx Hdr Pipe, EE En, I/O En, Fake Int and Intr En + * - Set Tri-State Int, Bus Master Rd/Wr, and Mem Map Disable + * - Clear Priority, Advance Pkt and Xmit Cmd + */ + + hp100_outw( HP100_DEBUG_EN | + HP100_RX_HDR | + HP100_EE_EN | HP100_RESET_HB | + HP100_IO_EN | + HP100_FAKE_INT | + HP100_INT_EN | HP100_RESET_LB, OPTION_LSW ); + + hp100_outw( HP100_TRI_INT | HP100_SET_HB, OPTION_LSW); + + if(lp->mode==1) /* busmaster */ + { + hp100_outw( HP100_BM_WRITE | + HP100_BM_READ | + HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW ); + } + else if(lp->mode==2) /* memory mapped */ + { + hp100_outw( HP100_BM_WRITE | + HP100_BM_READ | HP100_RESET_HB, OPTION_LSW ); + hp100_outw( HP100_MMAP_DIS | HP100_RESET_HB, OPTION_LSW ); + hp100_outw( HP100_MEM_EN | HP100_SET_LB, OPTION_LSW ); + hp100_outw( HP100_IO_EN | HP100_SET_LB, OPTION_LSW ); + } + else if( lp->mode==3 ) /* i/o mapped mode */ + { + hp100_outw( HP100_MMAP_DIS | HP100_SET_HB | + HP100_IO_EN | HP100_SET_LB, OPTION_LSW ); + } - hp100_page( MAC_CTRL ); - hp100_orw( HP100_LINK_BEAT_DIS | HP100_RESET_LB, LAN_CFG_10 ); + hp100_page( HW_MAP ); + hp100_outb( 0, EARLYRXCFG ); + hp100_outw( 0, EARLYTXCFG ); + + /* + * Enable Bus Master mode + */ + if(lp->mode==1) /* busmaster */ + { + /* Experimental: Set some PCI configuration bits */ + hp100_page( HW_MAP ); + hp100_andb( ~HP100_PDL_USE3, MODECTRL1 ); /* BM engine read maximum */ + hp100_andb( ~HP100_TX_DUALQ, MODECTRL1 ); /* No Queue for Priority TX */ + + /* PCI Bus failures should result in a Misc. Interrupt */ + hp100_orb( HP100_EN_BUS_FAIL, MODECTRL2); + + hp100_outw( HP100_BM_READ | HP100_BM_WRITE | HP100_SET_HB, OPTION_LSW ); + hp100_page( HW_MAP ); + /* Use Burst Mode and switch on PAGE_CK */ + hp100_orb( HP100_BM_BURST_RD | + HP100_BM_BURST_WR, BM); + if((lp->chip==HP100_CHIPID_RAINIER)||(lp->chip==HP100_CHIPID_SHASTA)) + hp100_orb( HP100_BM_PAGE_CK, BM ); + hp100_orb( HP100_BM_MASTER, BM ); + } + else /* not busmaster */ + { + hp100_page(HW_MAP); + hp100_andb(~HP100_BM_MASTER, BM ); + } + + /* + * Divide card memory into regions for Rx, Tx and, if non-ETR chip, PDLs + */ + hp100_page( MMU_CFG ); + if(lp->mode==1) /* only needed for Busmaster */ + { + int xmit_stop, recv_stop; - hp100_stop_interface( dev ); - hp100_load_eeprom( dev ); + if((lp->chip==HP100_CHIPID_RAINIER)||(lp->chip==HP100_CHIPID_SHASTA)) + { + int pdl_stop; + + /* + * Each pdl is 508 bytes long. (63 frags * 4 bytes for address and + * 4 bytes for for header). We will leave NUM_RXPDLS * 508 (rounded + * to the next higher 1k boundary) bytes for the rx-pdl's + * Note: For non-etr chips the transmit stop register must be + * programmed on a 1k boundary, i.e. bits 9:0 must be zero. + */ + pdl_stop = lp->memory_size; + xmit_stop = ( pdl_stop-508*(MAX_RX_PDL)-16 )& ~(0x03ff); + recv_stop = ( xmit_stop * (lp->rx_ratio)/100 ) &~(0x03ff); + hp100_outw( (pdl_stop>>4)-1, PDL_MEM_STOP ); +#ifdef HP100_DEBUG_BM + printk("hp100: PDL_STOP = 0x%x\n", pdl_stop); +#endif + } + else /* ETR chip (Lassen) in busmaster mode */ + { + xmit_stop = ( lp->memory_size ) - 1; + recv_stop = ( ( lp->memory_size * lp->rx_ratio ) / 100 ) & ~(0x03ff); + } - hp100_outw( HP100_MMAP_DIS | HP100_SET_HB | - HP100_IO_EN | HP100_SET_LB, OPTION_LSW ); - hp100_outw( HP100_DEBUG_EN | HP100_RX_HDR | HP100_EE_EN | HP100_RESET_HB | - HP100_FAKE_INT | HP100_RESET_LB, OPTION_LSW ); - hp100_outw( HP100_ADV_NXT_PKT | HP100_TX_CMD | HP100_RESET_LB | - HP100_PRIORITY_TX | ( hp100_priority_tx ? HP100_SET_HB : HP100_RESET_HB ), - OPTION_MSW ); + hp100_outw( xmit_stop>>4 , TX_MEM_STOP ); + hp100_outw( recv_stop>>4 , RX_MEM_STOP ); +#ifdef HP100_DEBUG_BM + printk("hp100: TX_STOP = 0x%x\n",xmit_stop>>4); + printk("hp100: RX_STOP = 0x%x\n",recv_stop>>4); +#endif + } + else /* Slave modes (memory mapped and programmed io) */ + { + hp100_outw( (((lp->memory_size*lp->rx_ratio)/100)>>4), RX_MEM_STOP ); + hp100_outw( ((lp->memory_size - 1 )>>4), TX_MEM_STOP ); +#ifdef HP100_DEBUG + printk("hp100: TX_MEM_STOP: 0x%x\n", hp100_inw(TX_MEM_STOP)); + printk("hp100: RX_MEM_STOP: 0x%x\n", hp100_inw(RX_MEM_STOP)); +#endif + } + /* Write MAC address into page 1 */ hp100_page( MAC_ADDRESS ); for ( i = 0; i < 6; i++ ) - hp100_outb( dev -> dev_addr[ i ], MAC_ADDR + i ); - for ( i = 0; i < 8; i++ ) /* setup multicast filter to receive all */ - hp100_outb( 0xff, HASH_BYTE0 + i ); + hp100_outb( dev->dev_addr[ i ], MAC_ADDR + i ); + + /* Zero the multicast hash registers */ + for ( i = 0; i < 8; i++ ) + hp100_outb( 0x0, HASH_BYTE0 + i ); + + /* Set up MAC defaults */ + hp100_page( MAC_CTRL ); + + /* Go to LAN Page and zero all filter bits */ + /* Zero accept error, accept multicast, accept broadcast and accept */ + /* all directed packet bits */ + hp100_andb( ~(HP100_RX_EN| + HP100_TX_EN| + HP100_ACC_ERRORED| + HP100_ACC_MC| + HP100_ACC_BC| + HP100_ACC_PHY), MAC_CFG_1 ); + + hp100_outb( 0x00, MAC_CFG_2 ); + + /* Zero the frame format bit. This works around a training bug in the */ + /* new hubs. */ + hp100_outb( 0x00, VG_LAN_CFG_2); /* (use 802.3) */ + + if(lp->priority_tx) + hp100_outb( HP100_PRIORITY_TX | HP100_SET_LB, OPTION_MSW ); + else + hp100_outb( HP100_PRIORITY_TX | HP100_RESET_LB, OPTION_MSW ); + + hp100_outb( HP100_ADV_NXT_PKT | + HP100_TX_CMD | HP100_RESET_LB, OPTION_MSW ); + + /* If busmaster, initialize the PDLs */ + if(lp->mode==1) + hp100_init_pdls( dev ); + + /* Go to performance page and initalize isr and imr registers */ hp100_page( PERFORMANCE ); - hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */ - hp100_outw( 0xffff, IRQ_STATUS ); /* ack IRQ */ - hp100_outw( (HP100_RX_PACKET | HP100_RX_ERROR | HP100_SET_HB) | - (HP100_TX_ERROR | HP100_SET_LB ), IRQ_MASK ); - /* and enable few */ - hp100_reset_card(); - hp100_page( MMU_CFG ); - hp100_outw( ( lp -> memory_size * lp -> rx_ratio ) / 100, RX_MEM_STOP ); - hp100_outw( lp -> memory_size - 1, TX_MEM_STOP ); - hp100_unreset_card(); + hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */ + hp100_outw( 0xffff, IRQ_STATUS ); /* ack IRQ */ +} + + +/* + * open/close functions + */ + +static int hp100_open( struct device *dev ) +{ + struct hp100_private *lp = (struct hp100_private *)dev->priv; +#ifdef HP100_DEBUG_B + int ioaddr=dev->base_addr; +#endif + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4204, TRACE ); + printk("hp100: open\n"); +#endif + + /* New: if bus is PCI or EISA, interrupts might be shared interrupts */ + if((lp->bus==HP100_BUS_PCI)||(lp->bus==HP100_BUS_EISA)) + { + if(request_irq(dev->irq,hp100_interrupt,SA_SHIRQ,lp->id->name,dev)) + { + printk( "%s: unable to get IRQ %d\n", dev->name, dev->irq ); + return -EAGAIN; + } + } + else + if(request_irq(dev->irq, hp100_interrupt, SA_INTERRUPT, lp->id->name, NULL)) + { + printk( "%s: unable to get IRQ %d\n", dev->name, dev->irq ); + return -EAGAIN; + } + + irq2dev_map[ dev->irq ] = dev; + + MOD_INC_USE_COUNT; + + dev->tbusy = 0; + dev->trans_start = jiffies; + dev->interrupt = 0; + dev->start = 1; + + lp->lan_type = hp100_sense_lan( dev ); + lp->mac1_mode = HP100_MAC1MODE3; + lp->mac2_mode = HP100_MAC2MODE3; - if ( lp -> lan_type == HP100_LAN_100 ) - lp -> hub_status = hp100_login_to_vg_hub( dev ); + hp100_stop_interface( dev ); + + hp100_hwinit( dev ); - hp100_start_interface( dev ); + hp100_start_interface( dev ); /* sets mac modes, enables interrupts */ return 0; } + +/* The close function is called when the interface is to be brought down */ static int hp100_close( struct device *dev ) { - int ioaddr = dev -> base_addr; - struct hp100_private *lp = (struct hp100_private *)dev -> priv; + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4205, TRACE ); + printk("hp100:close\n"); +#endif hp100_page( PERFORMANCE ); - hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all IRQs */ + hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all IRQs */ hp100_stop_interface( dev ); - if ( lp -> lan_type == HP100_LAN_100 ) /* relogin */ - hp100_login_to_vg_hub( dev ); + if ( lp->lan_type == HP100_LAN_100 ) + lp->hub_status=hp100_login_to_vg_hub( dev, FALSE ); - dev -> tbusy = 1; - dev -> start = 0; + dev->tbusy = 1; + dev->start = 0; - free_irq( dev -> irq, NULL ); - irq2dev_map[ dev -> irq ] = NULL; + if ((lp->bus==HP100_BUS_PCI)||(lp->bus==HP100_BUS_EISA)) + free_irq( dev->irq, dev ); + else + free_irq( dev->irq, NULL ); + irq2dev_map[ dev->irq ] = NULL; MOD_DEC_USE_COUNT; return 0; } + +/* + * Configure the PDL Rx rings and LAN + */ +static void hp100_init_pdls( struct device *dev ) +{ + struct hp100_private *lp = (struct hp100_private *)dev->priv; + hp100_ring_t *ringptr; + u_int *pageptr; + int i; + +#ifdef HP100_DEBUG_B + int ioaddr = dev->base_addr; +#endif + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4206, TRACE ); + printk("hp100: init pdls\n"); +#endif + + if(0==lp->page_vaddr_algn) + printk("hp100: Warning: lp->page_vaddr_algn not initialised!\n"); + else + { + /* pageptr shall point into the DMA accessible memory region */ + /* we use this pointer to status the upper limit of allocated */ + /* memory in the allocated page. */ + /* note: align the pointers to the pci cache line size */ + memset(lp->page_vaddr_algn, 0, MAX_RINGSIZE); /* Zero Rx/Tx ring page */ + pageptr=lp->page_vaddr_algn; + + lp->rxrcommit =0; + ringptr = lp->rxrhead = lp-> rxrtail = &(lp->rxring[0]); + + /* Initialise Rx Ring */ + for (i=MAX_RX_PDL-1; i>=0; i--) + { + lp->rxring[i].next = ringptr; + ringptr=&(lp->rxring[i]); + pageptr+=hp100_init_rxpdl(ringptr, pageptr); + } + + /* Initialise Tx Ring */ + lp->txrcommit = 0; + ringptr = lp->txrhead = lp->txrtail = &(lp->txring[0]); + for (i=MAX_TX_PDL-1; i>=0; i--) + { + lp->txring[i].next = ringptr; + ringptr=&(lp->txring[i]); + pageptr+=hp100_init_txpdl(ringptr, pageptr); + } + } +} + + +/* These functions "format" the entries in the pdl structure */ +/* They return how much memory the fragments need. */ +static int hp100_init_rxpdl( register hp100_ring_t *ringptr, register u32 *pdlptr ) +{ + /* pdlptr is starting adress for this pdl */ + + if( 0!=( ((unsigned)pdlptr) & 0xf) ) + printk("hp100: Init rxpdl: Unaligned pdlptr 0x%x.\n",(unsigned)pdlptr); + + ringptr->pdl = pdlptr+1; + ringptr->pdl_paddr = virt_to_bus(pdlptr+1); + ringptr->skb = (void *) NULL; + + /* + * Write address and length of first PDL Fragment (which is used for + * storing the RX-Header + * We use the 4 bytes _before_ the PDH in the pdl memory area to + * store this information. (PDH is at offset 0x04) + */ + /* Note that pdlptr+1 and not pdlptr is the pointer to the PDH */ + + *(pdlptr+2) =(u_int) virt_to_bus(pdlptr); /* Address Frag 1 */ + *(pdlptr+3) = 4; /* Length Frag 1 */ + + return( ( ((MAX_RX_FRAG*2+2)+3) /4)*4 ); +} + + +static int hp100_init_txpdl( register hp100_ring_t *ringptr, register u32 *pdlptr ) +{ + if( 0!=( ((unsigned)pdlptr) & 0xf) ) + printk("hp100: Init txpdl: Unaligned pdlptr 0x%x.\n",(unsigned) pdlptr); + + ringptr->pdl = pdlptr; /* +1; */ + ringptr->pdl_paddr = virt_to_bus(pdlptr); /* +1 */ + ringptr->skb = (void *) NULL; + + return((((MAX_TX_FRAG*2+2)+3)/4)*4); +} + + /* - * transmit + * hp100_build_rx_pdl allocates an skb_buff of maximum size plus two bytes + * for possible odd word alignment rounding up to next dword and set PDL + * address for fragment#2 + * Returns: 0 if unable to allocate skb_buff + * 1 if successful + */ +int hp100_build_rx_pdl( hp100_ring_t *ringptr, struct device *dev ) +{ +#ifdef HP100_DEBUG_B + int ioaddr = dev->base_addr; +#endif +#ifdef HP100_DEBUG_BM + u_int *p; +#endif + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4207, TRACE ); + printk("hp100: build rx pdl\n"); +#endif + + /* Allocate skb buffer of maximum size */ + /* Note: This depends on the alloc_skb functions allocating more + * space than requested, i.e. aligning to 16bytes */ + + ringptr->skb = dev_alloc_skb( ((MAX_ETHER_SIZE+2+3)/4)*4 ); + + if(NULL!=ringptr->skb) + { + /* + * Reserve 2 bytes at the head of the buffer to land the IP header + * on a long word boundary (According to the Network Driver section + * in the Linux KHG, this should help to increase performance.) + */ + skb_reserve(ringptr->skb, 2); + + ringptr->skb->dev=dev; + ringptr->skb->data=(u_char *)skb_put(ringptr->skb, MAX_ETHER_SIZE ); + + /* ringptr->pdl points to the beginning of the PDL, i.e. the PDH */ + /* Note: 1st Fragment is used for the 4 byte packet status + * (receive header). Its PDL entries are set up by init_rxpdl. So + * here we only have to set up the PDL fragment entries for the data + * part. Those 4 bytes will be stored in the DMA memory region + * directly before the PDL. + */ +#ifdef HP100_DEBUG_BM + printk("hp100: build_rx_pdl: PDH@0x%x, skb->data (len %d) at 0x%x\n", + (u_int) ringptr->pdl, + ((MAX_ETHER_SIZE+2+3)/4)*4, + (unsigned int) ringptr->skb->data); +#endif + + ringptr->pdl[0] = 0x00020000; /* Write PDH */ + ringptr->pdl[3] = ((u_int)virt_to_bus(ringptr->skb->data)); + ringptr->pdl[4] = MAX_ETHER_SIZE; /* Length of Data */ + +#ifdef HP100_DEBUG_BM + for(p=(ringptr->pdl); p<(ringptr->pdl+5); p++) + printk("Adr 0x%.8x = 0x%.8x\n",(u_int) p,(u_int) *p ); +#endif + return(1); + } + /* else: */ + /* alloc_skb failed (no memory) -> still can receive the header + * fragment into PDL memory. make PDL safe by clearing msgptr and + * making the PDL only 1 fragment (i.e. the 4 byte packet status) + */ +#ifdef HP100_DEBUG_BM + printk("hp100: build_rx_pdl: PDH@0x%x, No space for skb.\n", + (u_int) ringptr->pdl); +#endif + + ringptr->pdl[0]=0x00010000; /* PDH: Count=1 Fragment */ + + return(0); +} + + +/* + * hp100_rxfill - attempt to fill the Rx Ring will empty skb's + * + * Makes assumption that skb's are always contiguous memory areas and + * therefore PDLs contain only 2 physical fragments. + * - While the number of Rx PDLs with buffers is less than maximum + * a. Get a maximum packet size skb + * b. Put the physical address of the buffer into the PDL. + * c. Output physical address of PDL to adapter. + */ +static void hp100_rxfill( struct device *dev ) +{ + int ioaddr=dev->base_addr; + + struct hp100_private *lp = (struct hp100_private *)dev->priv; + hp100_ring_t *ringptr; + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4208, TRACE ); + printk("hp100: rxfill\n"); +#endif + + hp100_page( PERFORMANCE ); + + while (lp->rxrcommit < MAX_RX_PDL) + { + /* + ** Attempt to get a buffer and build a Rx PDL. + */ + ringptr = lp->rxrtail; + if (0 == hp100_build_rx_pdl( ringptr, dev )) + { + return; /* None available, return */ + } + + /* Hand this PDL over to the card */ + /* Note: This needs performance page selected! */ +#ifdef HP100_DEBUG_BM + printk("hp100: rxfill: Hand to card: pdl #%d @0x%x phys:0x%x, buffer: 0x%x\n", + lp->rxrcommit, + (u_int)ringptr->pdl, + (u_int)ringptr->pdl_paddr, + (u_int)ringptr->pdl[3]); +#endif + + hp100_outl( (u32)ringptr->pdl_paddr, RX_PDA); + + lp->rxrcommit += 1; + lp->rxrtail = ringptr->next; + } +} + + +/* + * BM_shutdown - shutdown bus mastering and leave chip in reset state + */ + +static void hp100_BM_shutdown( struct device *dev ) +{ + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + unsigned long time; + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4209, TRACE ); + printk("hp100: bm shutdown\n"); +#endif + + hp100_page( PERFORMANCE ); + hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */ + hp100_outw( 0xffff, IRQ_STATUS ); /* Ack all ints */ + + /* Ensure Interrupts are off */ + hp100_outw( HP100_INT_EN | HP100_RESET_LB , OPTION_LSW ); + + /* Disable all MAC activity */ + hp100_page( MAC_CTRL ); + hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 ); /* stop rx/tx */ + + /* If cascade MMU is not already in reset */ + if (0 != (hp100_inw(OPTION_LSW)&HP100_HW_RST) ) + { + /* Wait 1.3ms (10Mb max packet time) to ensure MAC is idle so + * MMU pointers will not be reset out from underneath + */ + hp100_page( MAC_CTRL ); + for(time=0; time<5000; time++) + { + if( (hp100_inb(MAC_CFG_1)&(HP100_TX_IDLE|HP100_RX_IDLE))== + (HP100_TX_IDLE|HP100_RX_IDLE) ) break; + } + + /* Shutdown algorithm depends on the generation of Cascade */ + if( lp->chip==HP100_CHIPID_LASSEN ) + { /* ETR shutdown/reset */ + /* Disable Busmaster mode and wait for bit to go to zero. */ + hp100_page(HW_MAP); + hp100_andb( ~HP100_BM_MASTER, BM ); + /* 100 ms timeout */ + for(time=0; time<32000; time++) + { + if ( 0 == (hp100_inb( BM ) & HP100_BM_MASTER) ) break; + } + } + else + { /* Shasta or Rainier Shutdown/Reset */ + /* To ensure all bus master inloading activity has ceased, + * wait for no Rx PDAs or no Rx packets on card. + */ + hp100_page( PERFORMANCE ); + /* 100 ms timeout */ + for(time=0; time<10000; time++) + { + /* RX_PDL: PDLs not executed. */ + /* RX_PKT_CNT: RX'd packets on card. */ + if ( (hp100_inb( RX_PDL ) == 0) && + (hp100_inb( RX_PKT_CNT ) == 0) ) break; + } + + if(time>=10000) + printk("hp100: BM shutdown error.\n"); + + /* To ensure all bus master outloading activity has ceased, + * wait until the Tx PDA count goes to zero or no more Tx space + * available in the Tx region of the card. + */ + /* 100 ms timeout */ + for(time=0; time<10000; time++) { + if ( (0 == hp100_inb( TX_PKT_CNT )) && + (0 != (hp100_inb( TX_MEM_FREE )&HP100_AUTO_COMPARE))) break; + } + + /* Disable Busmaster mode */ + hp100_page(HW_MAP); + hp100_andb( ~HP100_BM_MASTER, BM ); + } /* end of shutdown procedure for non-etr parts */ + + hp100_cascade_reset( dev, TRUE ); + } + hp100_page( PERFORMANCE ); + hp100_outw( HP100_BM_READ | HP100_BM_WRITE | HP100_RESET_HB, OPTION_LSW ); + /* Busmaster mode should be shut down now. */ +} + + + +/* + * transmit functions */ +/* tx function for busmaster mode */ +static int hp100_start_xmit_bm( struct sk_buff *skb, struct device *dev ) +{ + int i, ok_flag; + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + hp100_ring_t *ringptr; + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4210, TRACE ); + printk("hp100: start_xmit_bm\n"); +#endif + + if ( skb==NULL ) + { + dev_tint( dev ); + return 0; + } + + if ( skb->len <= 0 ) return 0; + + /* Get Tx ring tail pointer */ + if( lp->txrtail->next==lp->txrhead ) + { + /* No memory. */ +#ifdef HP100_DEBUG + printk("hp100: start_xmit_bm: No TX PDL available.\n"); +#endif + /* not waited long enough since last tx? */ + if ( jiffies - dev->trans_start < HZ/10 ) return -EAGAIN; + + if ( lp->lan_type < 0 ) /* no LAN type detected yet? */ + { + hp100_stop_interface( dev ); + if ( ( lp->lan_type = hp100_sense_lan( dev ) ) < 0 ) + { + printk( "%s: no connection found - check wire\n", dev->name ); + hp100_start_interface( dev ); /* 10Mb/s RX pkts maybe handled */ + return -EIO; + } + if ( lp->lan_type == HP100_LAN_100 ) + lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); /* relogin */ + hp100_start_interface( dev ); + } + + if ( lp->lan_type == HP100_LAN_100 && lp->hub_status < 0 ) + /* we have a 100Mb/s adapter but it isn't connected to hub */ + { + printk( "%s: login to 100Mb/s hub retry\n", dev->name ); + hp100_stop_interface( dev ); + lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); + hp100_start_interface( dev ); + } + else + { + hp100_ints_off(); + i = hp100_sense_lan( dev ); + hp100_page( PERFORMANCE ); + hp100_ints_on(); + if ( i == HP100_LAN_ERR ) + printk( "%s: link down detected\n", dev->name ); + else + if ( lp->lan_type != i ) /* cable change! */ + { + /* it's very hard - all network setting must be changed!!! */ + printk( "%s: cable change 10Mb/s <-> 100Mb/s detected\n", dev->name ); + lp->lan_type = i; + hp100_stop_interface( dev ); + if ( lp->lan_type == HP100_LAN_100 ) + lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); + hp100_start_interface( dev ); + } + else + { + printk( "%s: interface reset\n", dev->name ); + hp100_stop_interface( dev ); + hp100_start_interface( dev ); + } + } + + dev->trans_start = jiffies; + return -EAGAIN; + } + + /* + * we have to turn int's off before modifying this, otherwise + * a tx_pdl_cleanup could occur at the same time + */ + cli(); + ringptr=lp->txrtail; + lp->txrtail=ringptr->next; + + /* Check whether packet has minimal packet size */ + ok_flag = skb->len >= HP100_MIN_PACKET_SIZE; + i = ok_flag ? skb->len : HP100_MIN_PACKET_SIZE; + + ringptr->skb=skb; + ringptr->pdl[0]=((1<<16) | i); /* PDH: 1 Fragment & length */ + ringptr->pdl[1]=(u32)virt_to_bus(skb->data); /* 1st Frag: Adr. of data */ + if(lp->chip==HP100_CHIPID_SHASTA) + { + /* TODO:Could someone who has the EISA card please check if this works? */ + ringptr->pdl[2]=i; + } + else /* Lassen */ + { + /* In the PDL, don't use the padded size but the real packet size: */ + ringptr->pdl[2]=skb->len; /* 1st Frag: Length of frag */ + } + + /* Hand this PDL to the card. */ + hp100_outl( ringptr->pdl_paddr, TX_PDA_L ); /* Low Prio. Queue */ + + lp->txrcommit++; + sti(); + + /* Update statistics */ + lp->stats.tx_packets++; +#ifdef LINUX_2_1 + lp->stats.tx_bytes += skb->len; +#endif + dev->trans_start = jiffies; + + return 0; +} + + +/* clean_txring checks if packets have been sent by the card by reading + * the TX_PDL register from the performance page and comparing it to the + * number of commited packets. It then frees the skb's of the packets that + * obviously have been sent to the network. + * + * Needs the PERFORMANCE page selected. + */ +static void hp100_clean_txring( struct device *dev ) +{ + struct hp100_private *lp = (struct hp100_private *)dev->priv; + int ioaddr = dev->base_addr; + int donecount; + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4211, TRACE ); + printk("hp100: clean txring\n"); +#endif + + /* How many PDLs have been transmitted? */ + donecount=(lp->txrcommit)-hp100_inb(TX_PDL); + +#ifdef HP100_DEBUG + if(donecount>MAX_TX_PDL) + printk("hp100: Warning: More PDLs transmitted than commited to card???\n"); +#endif + + for( ; 0!=donecount; donecount-- ) + { +#ifdef HP100_DEBUG_BM + printk("hp100: Free skb: data @0x%.8x txrcommit=0x%x TXPDL=0x%x, done=0x%x\n", + (u_int) lp->txrhead->skb->data, + lp->txrcommit, + hp100_inb(TX_PDL), + donecount); +#endif + dev_kfree_skb( lp->txrhead->skb, FREE_WRITE ); + lp->txrhead->skb=(void *)NULL; + lp->txrhead=lp->txrhead->next; + lp->txrcommit--; + } +} + + +/* tx function for slave modes */ static int hp100_start_xmit( struct sk_buff *skb, struct device *dev ) { int i, ok_flag; - int ioaddr = dev -> base_addr; + int ioaddr = dev->base_addr; u_short val; - struct hp100_private *lp = (struct hp100_private *)dev -> priv; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4212, TRACE ); + printk("hp100: start_xmit\n"); +#endif - if ( lp -> lan_type < 0 ) + if ( lp->lan_type < 0 ) /* no LAN type detected yet? */ { hp100_stop_interface( dev ); - if ( ( lp -> lan_type = hp100_sense_lan( dev ) ) < 0 ) + if ( ( lp->lan_type = hp100_sense_lan( dev ) ) < 0 ) { - printk( "%s: no connection found - check wire\n", dev -> name ); - hp100_start_interface( dev ); /* 10Mb/s RX packets maybe handled */ + printk( "%s: no connection found - check wire\n", dev->name ); + hp100_start_interface( dev ); /* 10Mb/s RX packets maybe handled */ return -EIO; } - if ( lp -> lan_type == HP100_LAN_100 ) - lp -> hub_status = hp100_login_to_vg_hub( dev ); + if ( lp->lan_type == HP100_LAN_100 ) + lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); /* relogin */ hp100_start_interface( dev ); } - if ( ( i = ( hp100_inl( TX_MEM_FREE ) & ~0x7fffffff ) ) < skb -> len + 16 ) + /* If there is not enough free memory on the card... */ + i=hp100_inl(TX_MEM_FREE)&0x7fffffff; + if ( !(((i/2)-539)>(skb->len+16) && (hp100_inb(TX_PKT_CNT)<255)) ) { #ifdef HP100_DEBUG - printk( "hp100_start_xmit: rx free mem = 0x%x\n", i ); + printk( "hp100_start_xmit: tx free mem = 0x%x\n", i ); +#endif + /* not waited long enough since last failed tx try? */ + if ( jiffies - dev->trans_start < HZ/2 ) + { +#ifdef HP100_DEBUG + printk("hp100: trans_start timing problem\n"); #endif - if ( jiffies - dev -> trans_start < 2 * HZ ) return -EAGAIN; - if ( lp -> lan_type == HP100_LAN_100 && lp -> hub_status < 0 ) - /* 100Mb/s adapter isn't connected to hub */ + return -EAGAIN; + } + if ( lp->lan_type == HP100_LAN_100 && lp->hub_status < 0 ) + /* we have a 100Mb/s adapter but it isn't connected to hub */ { - printk( "%s: login to 100Mb/s hub retry\n", dev -> name ); + printk( "%s: login to 100Mb/s hub retry\n", dev->name ); hp100_stop_interface( dev ); - lp -> hub_status = hp100_login_to_vg_hub( dev ); + lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); hp100_start_interface( dev ); } - else + else { hp100_ints_off(); i = hp100_sense_lan( dev ); hp100_page( PERFORMANCE ); hp100_ints_on(); if ( i == HP100_LAN_ERR ) - printk( "%s: link down detected\n", dev -> name ); - else - if ( lp -> lan_type != i ) - { - /* it's very heavy - all network setting must be changed!!! */ - printk( "%s: cable change 10Mb/s <-> 100Mb/s detected\n", dev -> name ); - lp -> lan_type = i; - hp100_stop_interface( dev ); - if ( lp -> lan_type == HP100_LAN_100 ) - lp -> hub_status = hp100_login_to_vg_hub( dev ); - hp100_start_interface( dev ); - } - else - { - printk( "%s: interface reset\n", dev -> name ); - hp100_stop_interface( dev ); - hp100_start_interface( dev ); - } + printk( "%s: link down detected\n", dev->name ); + else + if ( lp->lan_type != i ) /* cable change! */ + { + /* it's very hard - all network setting must be changed!!! */ + printk( "%s: cable change 10Mb/s <-> 100Mb/s detected\n", dev->name ); + lp->lan_type = i; + hp100_stop_interface( dev ); + if ( lp->lan_type == HP100_LAN_100 ) + lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); + hp100_start_interface( dev ); + } + else + { + printk( "%s: interface reset\n", dev->name ); + hp100_stop_interface( dev ); + hp100_start_interface( dev ); + udelay(1000); + } } - dev -> trans_start = jiffies; + dev->trans_start = jiffies; return -EAGAIN; } - for ( i = 0; i < 6000 && ( hp100_inw( OPTION_MSW ) & HP100_TX_CMD ); i++ ) + for ( i=0; i<6000 && ( hp100_inb( OPTION_MSW ) & HP100_TX_CMD ); i++ ) { #ifdef HP100_DEBUG_TX printk( "hp100_start_xmit: busy\n" ); #endif } - + hp100_ints_off(); val = hp100_inw( IRQ_STATUS ); - hp100_outw( val & HP100_TX_COMPLETE, IRQ_STATUS ); + /* Ack / clear the interrupt TX_COMPLETE interrupt - this interrupt is set + * when the current packet being transmitted on the wire is completed. */ + hp100_outw( HP100_TX_COMPLETE, IRQ_STATUS ); #ifdef HP100_DEBUG_TX - printk( "hp100_start_xmit: irq_status = 0x%x, len = %d\n", val, (int)skb -> len ); + printk("hp100_start_xmit: irq_status=0x%.4x, irqmask=0x%.4x, len=%d\n",val,hp100_inw(IRQ_MASK),(int)skb->len ); #endif - ok_flag = skb -> len >= HP100_MIN_PACKET_SIZE; - i = ok_flag ? skb -> len : HP100_MIN_PACKET_SIZE; - hp100_outw( i, DATA32 ); /* length to memory manager */ - hp100_outw( i, FRAGMENT_LEN ); - if ( lp -> mem_mapped ) - { - if ( lp -> mem_ptr_virt ) - { - memcpy( lp -> mem_ptr_virt, skb -> data, skb -> len ); - if ( !ok_flag ) - memset( lp -> mem_ptr_virt, 0, HP100_MIN_PACKET_SIZE - skb -> len ); - } - else - { - memcpy_toio( lp -> mem_ptr_phys, skb -> data, skb -> len ); - if ( !ok_flag ) - memset_io( lp -> mem_ptr_phys, 0, HP100_MIN_PACKET_SIZE - skb -> len ); - } + + ok_flag = skb->len >= HP100_MIN_PACKET_SIZE; + i = ok_flag ? skb->len : HP100_MIN_PACKET_SIZE; + + hp100_outw( i, DATA32 ); /* tell card the total packet length */ + hp100_outw( i, FRAGMENT_LEN ); /* and first/only fragment length */ + + if ( lp->mode==2 ) /* memory mapped */ + { + if ( lp->mem_ptr_virt ) /* high pci memory was remapped */ + { + /* Note: The J2585B needs alignment to 32bits here! */ + memcpy( lp->mem_ptr_virt, skb->data, ( skb->len +3 ) & ~3 ); + if ( !ok_flag ) + memset( lp->mem_ptr_virt, 0, HP100_MIN_PACKET_SIZE - skb->len ); + } + else + { + memcpy_toio( lp->mem_ptr_phys, skb->data, skb->len ); + if ( !ok_flag ) + memset_io( lp->mem_ptr_phys, 0, HP100_MIN_PACKET_SIZE - skb->len ); + } } - else + else /* programmed i/o */ { - outsl( ioaddr + HP100_REG_DATA32, skb -> data, ( skb -> len + 3 ) >> 2 ); + outsl( ioaddr + HP100_REG_DATA32, skb->data, ( skb->len + 3 ) >> 2 ); if ( !ok_flag ) - for ( i = ( skb -> len + 3 ) & ~3; i < HP100_MIN_PACKET_SIZE; i += 4 ) - hp100_outl( 0, DATA32 ); + for ( i = ( skb->len + 3 ) & ~3; i < HP100_MIN_PACKET_SIZE; i += 4 ) + hp100_outl( 0, DATA32 ); } - hp100_outw( HP100_TX_CMD | HP100_SET_LB, OPTION_MSW ); /* send packet */ - lp -> stats.tx_packets++; - dev -> trans_start = jiffies; + + hp100_outb( HP100_TX_CMD | HP100_SET_LB, OPTION_MSW ); /* send packet */ + + lp->stats.tx_packets++; +#ifdef LINUX_2_1 + lp->stats.tx_bytes += skb->len; +#endif + dev->trans_start=jiffies; hp100_ints_on(); - + dev_kfree_skb( skb, FREE_WRITE ); - + #ifdef HP100_DEBUG_TX printk( "hp100_start_xmit: end\n" ); #endif - + return 0; } + /* - * receive - called from interrupt handler + * Receive Function (Non-Busmaster mode) + * Called when an "Receive Packet" interrupt occurs, i.e. the receive + * packet counter is non-zero. + * For non-busmaster, this function does the whole work of transfering + * the packet to the host memory and then up to higher layers via skb + * and netif_rx. */ static void hp100_rx( struct device *dev ) { int packets, pkt_len; - int ioaddr = dev -> base_addr; - struct hp100_private *lp = (struct hp100_private *)dev -> priv; + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; u_int header; struct sk_buff *skb; -#if 0 - if ( lp -> lan_type < 0 ) - { - if ( ( lp -> lan_type = hp100_sense_lan( dev ) ) == HP100_LAN_100 ) - lp -> hub_status = hp100_login_to_vg_hub( dev ); - hp100_page( PERFORMANCE ); - } +#ifdef DEBUG_B + hp100_outw( 0x4213, TRACE ); + printk("hp100: rx\n"); #endif + /* First get indication of received lan packet */ + /* RX_PKT_CND indicates the number of packets which have been fully */ + /* received onto the card but have not been fully transfered of the card */ packets = hp100_inb( RX_PKT_CNT ); -#ifdef HP100_DEBUG +#ifdef HP100_DEBUG_RX if ( packets > 1 ) printk( "hp100_rx: waiting packets = %d\n", packets ); #endif + while ( packets-- > 0 ) { - for ( pkt_len = 0; pkt_len < 6000 && ( hp100_inw( OPTION_MSW ) & HP100_ADV_NXT_PKT ); pkt_len++ ) + /* If ADV_NXT_PKT is still set, we have to wait until the card has */ + /* really advanced to the next packet. */ + for (pkt_len=0; pkt_len<6000 &&(hp100_inb(OPTION_MSW)&HP100_ADV_NXT_PKT); + pkt_len++ ) { -#ifdef HP100_DEBUG_TX +#ifdef HP100_DEBUG_RX printk( "hp100_rx: busy, remaining packets = %d\n", packets ); -#endif +#endif } - if ( lp -> mem_mapped ) + + /* First we get the header, which contains information about the */ + /* actual length of the received packet. */ + if( lp->mode==2 ) /* memory mapped mode */ { - if ( lp -> mem_ptr_virt ) - header = *(__u32 *)lp -> mem_ptr_virt; - else - header = readl( lp -> mem_ptr_phys ); + if ( lp->mem_ptr_virt ) /* if memory was remapped */ + header = *(__u32 *)lp->mem_ptr_virt; + else + header = readl( lp->mem_ptr_phys ); } - else + else /* programmed i/o */ header = hp100_inl( DATA32 ); + pkt_len = header & HP100_PKT_LEN_MASK; + #ifdef HP100_DEBUG_RX - printk( "hp100_rx: new packet - length = %d, errors = 0x%x, dest = 0x%x\n", - header & HP100_PKT_LEN_MASK, ( header >> 16 ) & 0xfff8, ( header >> 16 ) & 7 ); -#endif - /* - * NOTE! This (and the skb_put() below) depends on the skb-functions + printk( "hp100_rx: new packet - length=%d, errors=0x%x, dest=0x%x\n", + header & HP100_PKT_LEN_MASK, (header>>16)&0xfff8, + (header>>16)&7); +#endif + + /* Now we allocate the skb and transfer the data into it. */ + /* NOTE! This (and the skb_put() below) depends on the skb-functions * allocating more than asked (notably, aligning the request up to * the next 16-byte length). */ skb = dev_alloc_skb( pkt_len ); - if ( skb == NULL ) - { + if ( skb == NULL ) /* Not enough memory->drop packet */ + { #ifdef HP100_DEBUG - printk( "hp100_rx: couldn't allocate a sk_buff of size %d\n", pkt_len ); + printk( "hp100_rx: couldn't allocate a sk_buff of size %d\n", pkt_len ); #endif - lp -> stats.rx_dropped++; - } - else - { - u_char *ptr; - - skb -> dev = dev; - ptr = (u_char *)skb_put( skb, pkt_len ); - if ( lp -> mem_mapped ) + lp->stats.rx_dropped++; + } + else /* skb successfully allocated */ + { + u_char *ptr; + + skb->dev = dev; + + /* ptr to start of the sk_buff data area */ + ptr = (u_char *)skb_put( skb, pkt_len ); + + /* Now transfer the data from the card into that area */ + if ( lp->mode==2 ) { - if ( lp -> mem_ptr_virt ) - memcpy( ptr, lp -> mem_ptr_virt, ( pkt_len + 3 ) & ~3 ); - else - memcpy_fromio( ptr, lp -> mem_ptr_phys, ( pkt_len + 3 ) & ~3 ); + if ( lp->mem_ptr_virt ) + memcpy( ptr, lp->mem_ptr_virt, ( pkt_len + 3 ) & ~3 ); + /* Note alignment to 32bit transfers */ + else + memcpy_fromio( ptr, lp->mem_ptr_phys, ( pkt_len + 3 ) & ~3 ); } - else - insl( ioaddr + HP100_REG_DATA32, ptr, ( pkt_len + 3 ) >> 2 ); - skb -> protocol = eth_type_trans( skb, dev ); - netif_rx( skb ); - lp -> stats.rx_packets++; -#ifdef HP100_DEBUG_RX - printk( "rx: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", - ptr[ 0 ], ptr[ 1 ], ptr[ 2 ], ptr[ 3 ], ptr[ 4 ], ptr[ 5 ], - ptr[ 6 ], ptr[ 7 ], ptr[ 8 ], ptr[ 9 ], ptr[ 10 ], ptr[ 11 ] ); + else /* io mapped */ + insl( ioaddr + HP100_REG_DATA32, ptr, ( pkt_len + 3 ) >> 2 ); + + skb->protocol = eth_type_trans( skb, dev ); + + netif_rx( skb ); + lp->stats.rx_packets++; +#ifdef LINUX_2_1 + lp->stats.rx_bytes += skb->len; #endif - } - hp100_outw( HP100_ADV_NXT_PKT | HP100_SET_LB, OPTION_MSW ); + +#ifdef HP100_DEBUG_RX + printk( "rx: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + ptr[ 0 ], ptr[ 1 ], ptr[ 2 ], ptr[ 3 ], ptr[ 4 ], ptr[ 5 ], + ptr[ 6 ], ptr[ 7 ], ptr[ 8 ], ptr[ 9 ], ptr[ 10 ], ptr[ 11 ] ); +#endif + } + + /* Indicate the card that we have got the packet */ + hp100_outb( HP100_ADV_NXT_PKT | HP100_SET_LB, OPTION_MSW ); + switch ( header & 0x00070000 ) { - case (HP100_MULTI_ADDR_HASH<<16): - case (HP100_MULTI_ADDR_NO_HASH<<16): - lp -> stats.multicast++; break; + case (HP100_MULTI_ADDR_HASH<<16): + case (HP100_MULTI_ADDR_NO_HASH<<16): + lp->stats.multicast++; break; } - } + } /* end of while(there are packets) loop */ #ifdef HP100_DEBUG_RX - printk( "hp100_rx: end\n" ); + printk( "hp100_rx: end\n" ); +#endif +} + + +/* + * Receive Function for Busmaster Mode + */ +static void hp100_rx_bm( struct device *dev ) +{ + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + hp100_ring_t *ptr; + u_int header; + int pkt_len; + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4214, TRACE ); + printk("hp100: rx_bm\n"); +#endif + +#ifdef HP100_DEBUG + if(0==lp->rxrcommit) + { + printk("hp100: rx_bm called although no PDLs were committed to adapter?\n"); + return; + } + else + + /* RX_PKT_CNT states how many PDLs are currently formatted and available to + * the cards BM engine */ + if( (hp100_inw(RX_PKT_CNT)&0x00ff) >= lp->rxrcommit) + { + printk("hp100: More packets received than commited? RX_PKT_CNT=0x%x, commit=0x%x\n", hp100_inw(RX_PKT_CNT)&0x00ff, lp->rxrcommit); + return; + } +#endif + + while( (lp->rxrcommit > hp100_inb(RX_PDL)) ) + { + /* + * The packet was received into the pdl pointed to by lp->rxrhead ( + * the oldest pdl in the ring + */ + + /* First we get the header, which contains information about the */ + /* actual length of the received packet. */ + + ptr=lp->rxrhead; + + header = *(ptr->pdl-1); + pkt_len = (header & HP100_PKT_LEN_MASK); + +#ifdef HP100_DEBUG_BM + printk( "hp100: rx_bm: header@0x%x=0x%x length=%d, errors=0x%x, dest=0x%x\n", + (u_int) (ptr->pdl-1),(u_int) header, + pkt_len, + (header>>16)&0xfff8, + (header>>16)&7); + printk( "hp100: RX_PDL_COUNT:0x%x TX_PDL_COUNT:0x%x, RX_PKT_CNT=0x%x PDH=0x%x, Data@0x%x len=0x%x\n", + hp100_inb( RX_PDL ), + hp100_inb( TX_PDL ), + hp100_inb( RX_PKT_CNT ), + (u_int) *(ptr->pdl), + (u_int) *(ptr->pdl+3), + (u_int) *(ptr->pdl+4)); +#endif + + if( (pkt_len>=MIN_ETHER_SIZE) && + (pkt_len<=MAX_ETHER_SIZE) ) + { + if(ptr->skb==NULL) + { + printk("hp100: rx_bm: skb null\n"); + /* can happen if we only allocated room for the pdh due to memory shortage. */ + lp->stats.rx_dropped++; + } + else + { + skb_trim( ptr->skb, pkt_len ); /* Shorten it */ + ptr->skb->protocol = eth_type_trans( ptr->skb, dev ); + + netif_rx( ptr->skb ); /* Up and away... */ + + lp->stats.rx_packets++; +#ifdef LINUX_2_1 + lp->stats.rx_bytes += ptr->skb->len; +#endif + } + + switch ( header & 0x00070000 ) { + case (HP100_MULTI_ADDR_HASH<<16): + case (HP100_MULTI_ADDR_NO_HASH<<16): + lp->stats.multicast++; break; + } + } + else + { +#ifdef HP100_DEBUG + printk("hp100: rx_bm: Received bad packet (length=%d)\n",pkt_len); #endif + if(ptr->skb!=NULL) + dev_kfree_skb( ptr->skb, FREE_READ ); + lp->stats.rx_errors++; + } + + lp->rxrhead=lp->rxrhead->next; + + /* Allocate a new rx PDL (so lp->rxrcommit stays the same) */ + if (0 == hp100_build_rx_pdl( lp->rxrtail, dev )) + { + /* No space for skb, header can still be received. */ +#ifdef HP100_DEBUG + printk("hp100: rx_bm: No space for new PDL.\n"); +#endif + return; + } + else + { /* successfully allocated new PDL - put it in ringlist at tail. */ + hp100_outl((u32)lp->rxrtail->pdl_paddr, RX_PDA); + lp->rxrtail=lp->rxrtail->next; + } + + } } + + /* * statistics */ - -static struct net_device_stats *hp100_get_stats( struct device *dev ) +static hp100_stats_t *hp100_get_stats( struct device *dev ) { - int ioaddr = dev -> base_addr; + int ioaddr = dev->base_addr; + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4215, TRACE ); +#endif hp100_ints_off(); hp100_update_stats( dev ); hp100_ints_on(); - return &((struct hp100_private *)dev -> priv) -> stats; + return &((struct hp100_private *)dev->priv)->stats; } static void hp100_update_stats( struct device *dev ) { - int ioaddr = dev -> base_addr; + int ioaddr = dev->base_addr; u_short val; - struct hp100_private *lp = (struct hp100_private *)dev -> priv; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4216, TRACE ); + printk("hp100: update-stats\n"); +#endif - hp100_page( MAC_CTRL ); /* get all statistics bytes */ + /* Note: Statistics counters clear when read. */ + hp100_page( MAC_CTRL ); val = hp100_inw( DROPPED ) & 0x0fff; - lp -> stats.rx_errors += val; - lp -> stats.rx_over_errors += val; + lp->stats.rx_errors += val; + lp->stats.rx_over_errors += val; val = hp100_inb( CRC ); - lp -> stats.rx_errors += val; - lp -> stats.rx_crc_errors += val; + lp->stats.rx_errors += val; + lp->stats.rx_crc_errors += val; val = hp100_inb( ABORT ); - lp -> stats.tx_errors += val; - lp -> stats.tx_aborted_errors += val; + lp->stats.tx_errors += val; + lp->stats.tx_aborted_errors += val; hp100_page( PERFORMANCE ); } static void hp100_clear_stats( int ioaddr ) { +#ifdef HP100_DEBUG_B + hp100_outw( 0x4217, TRACE ); + printk("hp100: clear_stats\n"); +#endif + cli(); - hp100_page( MAC_CTRL ); /* get all statistics bytes */ + hp100_page( MAC_CTRL ); /* get all statistics bytes */ hp100_inw( DROPPED ); hp100_inb( CRC ); hp100_inb( ABORT ); @@ -831,54 +2031,73 @@ sti(); } + /* * multicast setup */ /* * Set or clear the multicast filter for this adapter. + * TODO: Currently when in multicast mode, card accepts all multicast packets + * for all MC addresses. Should better use the list on the card. */ - + static void hp100_set_multicast_list( struct device *dev) { - int ioaddr = dev -> base_addr; - struct hp100_private *lp = (struct hp100_private *)dev -> priv; + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; -#ifdef HP100_DEBUG_MULTI - printk( "hp100_set_multicast_list: num_addrs = %d\n", dev->mc_count); +#ifdef HP100_DEBUG_B + hp100_outw( 0x4218, TRACE ); + printk("hp100: set_mc_list\n"); #endif + cli(); hp100_ints_off(); hp100_page( MAC_CTRL ); - hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 ); /* stop rx/tx */ + hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 ); /* stop rx/tx */ - if ( dev->flags&IFF_PROMISC) + if ( dev->flags & IFF_PROMISC ) { - lp -> mac2_mode = HP100_MAC2MODE6; /* promiscuous mode, all good */ - lp -> mac1_mode = HP100_MAC1MODE6; /* packets on the net */ + lp->mac2_mode = HP100_MAC2MODE6; /* promiscuous mode = get all good */ + lp->mac1_mode = HP100_MAC1MODE6; /* packets on the net */ } - else - if ( dev->mc_count || dev->flags&IFF_ALLMULTI ) + else if ( dev->mc_count || (dev->flags&IFF_ALLMULTI) ) { - lp -> mac2_mode = HP100_MAC2MODE5; /* multicast mode, packets for me */ - lp -> mac1_mode = HP100_MAC1MODE5; /* broadcasts and all multicasts */ + lp->mac2_mode = HP100_MAC2MODE5; /* multicast mode = get packets for */ + lp->mac1_mode = HP100_MAC1MODE5; /* me, broadcasts and all multicasts */ } - else + else { - lp -> mac2_mode = HP100_MAC2MODE3; /* normal mode, packets for me */ - lp -> mac1_mode = HP100_MAC1MODE3; /* and broadcasts */ + lp->mac2_mode = HP100_MAC2MODE3; /* normal mode = get packets for me */ + lp->mac1_mode = HP100_MAC1MODE3; /* and broadcasts */ } - hp100_outb( lp -> mac2_mode, MAC_CFG_2 ); - hp100_andb( HP100_MAC1MODEMASK, MAC_CFG_1 ); - hp100_orb( lp -> mac1_mode | - HP100_RX_EN | HP100_RX_IDLE | /* enable rx */ - HP100_TX_EN | HP100_TX_IDLE, MAC_CFG_1 ); /* enable tx */ + if ( ( (hp100_inb(MAC_CFG_1) & 0x0f)!=lp->mac1_mode ) || + ( hp100_inb(MAC_CFG_2)!=lp->mac2_mode ) ) { + hp100_outb( lp->mac2_mode, MAC_CFG_2 ); + hp100_andb( HP100_MAC1MODEMASK, MAC_CFG_1 ); /* clear mac1 mode bits */ + hp100_orb( lp->mac1_mode, MAC_CFG_1 ); /* and set the new mode */ + + if(lp->lan_type==HP100_LAN_100) + { +#ifdef HP100_DEBUG + printk("hp100: 100VG MAC settings have changed - relogin.\n"); +#endif + lp->hub_status=hp100_login_to_vg_hub( dev, TRUE ); /* force a relogin to the hub */ + } + } + + hp100_page( MAC_CTRL ); + hp100_orb( HP100_RX_EN | HP100_RX_IDLE | /* enable rx */ + HP100_TX_EN | HP100_TX_IDLE, MAC_CFG_1 ); /* enable tx */ + hp100_page( PERFORMANCE ); hp100_ints_on(); sti(); } + /* * hardware interrupt handling */ @@ -886,249 +2105,747 @@ static void hp100_interrupt( int irq, void *dev_id, struct pt_regs *regs ) { struct device *dev = (struct device *)irq2dev_map[ irq ]; - struct hp100_private *lp; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + int ioaddr; - u_short val; + u_int val; if ( dev == NULL ) return; - ioaddr = dev -> base_addr; - if ( dev -> interrupt ) - printk( "%s: re-entering the interrupt handler\n", dev -> name ); + ioaddr = dev->base_addr; + + if ( dev->interrupt ) + printk( "%s: re-entering the interrupt handler\n", dev->name ); hp100_ints_off(); - dev -> interrupt = 1; - hp100_page( PERFORMANCE ); + dev->interrupt = 1; /* mark that we are inside the handler */ + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4219, TRACE ); +#endif + + /* hp100_page( PERFORMANCE ); */ val = hp100_inw( IRQ_STATUS ); #ifdef HP100_DEBUG_IRQ - printk( "hp100_interrupt: irq_status = 0x%x\n", val ); + printk( "hp100: mode=%x,IRQ_STAT=0x%.4x,RXPKTCNT=0x%.2x RXPDL=0x%.2x TXPKTCNT=0x%.2x TXPDL=0x%.2x\n", + lp->mode, + (u_int)val, + hp100_inb( RX_PKT_CNT ), + hp100_inb( RX_PDL ), + hp100_inb( TX_PKT_CNT ), + hp100_inb( TX_PDL ) + ); #endif - if ( val & HP100_RX_PACKET ) - { - hp100_rx( dev ); - hp100_outw( HP100_RX_PACKET, IRQ_STATUS ); - } - if ( val & (HP100_TX_SPACE_AVAIL | HP100_TX_COMPLETE) ) + + if(val==0) /* might be a shared interrupt */ { - hp100_outw( val & (HP100_TX_SPACE_AVAIL | HP100_TX_COMPLETE), IRQ_STATUS ); + dev->interrupt=0; + hp100_ints_on(); + return; } + /* We're only interested in those interrupts we really enabled. */ + /* val &= hp100_inw( IRQ_MASK ); */ + + /* + * RX_PDL_FILL_COMPL is set whenever a RX_PDL has been executed. A RX_PDL + * is considered executed whenever the RX_PDL data structure is no longer + * needed. + */ + if ( val & HP100_RX_PDL_FILL_COMPL ) + { + if(lp->mode==1) + hp100_rx_bm( dev ); + else + printk("hp100: rx_pdl_fill_compl interrupt although not busmaster?\n"); + } + + /* + * The RX_PACKET interrupt is set, when the receive packet counter is + * non zero. We use this interrupt for receiving in slave mode. In + * busmaster mode, we use it to make sure we did not miss any rx_pdl_fill + * interrupts. If rx_pdl_fill_compl is not set and rx_packet is set, then + * we somehow have missed a rx_pdl_fill_compl interrupt. + */ + + if ( val & HP100_RX_PACKET ) /* Receive Packet Counter is non zero */ + { + if(lp->mode!=1) /* non busmaster */ + hp100_rx( dev ); + else if ( !(val & HP100_RX_PDL_FILL_COMPL )) + { + /* Shouldnt happen - maybe we missed a RX_PDL_FILL Interrupt? */ + hp100_rx_bm( dev ); + } + } + + /* + * Ack. that we have noticed the interrupt and thereby allow next one. + * Note that this is now done after the slave rx function, since first + * acknowledging and then setting ADV_NXT_PKT caused an extra interrupt + * on the J2573. + */ + hp100_outw( val, IRQ_STATUS ); + + /* + * RX_ERROR is set when a packet is dropped due to no memory resources on + * the card or when a RCV_ERR occurs. + * TX_ERROR is set when a TX_ABORT condition occurs in the MAC->exists + * only in the 802.3 MAC and happens when 16 collisions occur during a TX + */ if ( val & ( HP100_TX_ERROR | HP100_RX_ERROR ) ) { - lp = (struct hp100_private *)dev -> priv; - hp100_update_stats( dev ); - hp100_outw( val & (HP100_TX_ERROR | HP100_RX_ERROR), IRQ_STATUS ); - } #ifdef HP100_DEBUG_IRQ - printk( "hp100_interrupt: end\n" ); + printk("hp100: TX/RX Error IRQ\n"); #endif - dev -> interrupt = 0; + hp100_update_stats( dev ); + if(lp->mode==1) + { + hp100_rxfill( dev ); + hp100_clean_txring( dev ); + } + } + + /* + * RX_PDA_ZERO is set when the PDA count goes from non-zero to zero. + */ + if ( (lp->mode==1)&&(val &(HP100_RX_PDA_ZERO)) ) + hp100_rxfill( dev ); + + /* + * HP100_TX_COMPLETE interrupt occurs when packet transmitted on wire + * is completed + */ + if ( (lp->mode==1) && ( val & ( HP100_TX_COMPLETE )) ) + hp100_clean_txring( dev ); + + /* + * MISC_ERROR is set when either the LAN link goes down or a detected + * bus error occurs. + */ + if ( val & HP100_MISC_ERROR ) /* New for J2585B */ + { + printk("hp100: Misc. Error Interrupt - Check cabling.\n"); + if(lp->mode==1) + { + hp100_clean_txring( dev ); + hp100_rxfill( dev ); + } + } + + dev->interrupt = 0; hp100_ints_on(); } + /* * some misc functions */ static void hp100_start_interface( struct device *dev ) { - int ioaddr = dev -> base_addr; - struct hp100_private *lp = (struct hp100_private *)dev -> priv; + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4220, TRACE ); + printk("hp100: hp100_start_interface %s\n",dev->name); +#endif cli(); - hp100_unreset_card(); - hp100_page( MAC_CTRL ); - hp100_outb( lp -> mac2_mode, MAC_CFG_2 ); - hp100_andb( HP100_MAC1MODEMASK, MAC_CFG_1 ); - hp100_orb( lp -> mac1_mode | - HP100_RX_EN | HP100_RX_IDLE | - HP100_TX_EN | HP100_TX_IDLE, MAC_CFG_1 ); + + /* Ensure the adapter does not want to request an interrupt when */ + /* enabling the IRQ line to be active on the bus (i.e. not tri-stated) */ hp100_page( PERFORMANCE ); - hp100_outw( HP100_INT_EN | HP100_SET_LB, OPTION_LSW ); - hp100_outw( HP100_TRI_INT | HP100_RESET_HB, OPTION_LSW ); - if ( lp -> mem_mapped ) + hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */ + hp100_outw( 0xffff, IRQ_STATUS ); /* ack all IRQs */ + hp100_outw( HP100_FAKE_INT|HP100_INT_EN|HP100_RESET_LB, OPTION_LSW); + /* Un Tri-state int. TODO: Check if shared interrupts can be realised? */ + hp100_outw( HP100_TRI_INT | HP100_RESET_HB, OPTION_LSW ); + + if(lp->mode==1) + { + /* Make sure BM bit is set... */ + hp100_page(HW_MAP); + hp100_orb( HP100_BM_MASTER, BM ); + hp100_rxfill( dev ); + } + else if(lp->mode==2) { - /* enable memory mapping */ + /* Enable memory mapping. Note: Don't do this when busmaster. */ hp100_outw( HP100_MMAP_DIS | HP100_RESET_HB, OPTION_LSW ); } - sti(); + + hp100_page(PERFORMANCE); + hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */ + hp100_outw( 0xffff, IRQ_STATUS ); /* ack IRQ */ + + /* enable a few interrupts: */ + if(lp->mode==1) /* busmaster mode */ + { + hp100_outw( HP100_RX_PDL_FILL_COMPL | + HP100_RX_PDA_ZERO | + HP100_RX_ERROR | + /* HP100_RX_PACKET | */ + /* HP100_RX_EARLY_INT | */ HP100_SET_HB | + /* HP100_TX_PDA_ZERO | */ + HP100_TX_COMPLETE | + /* HP100_MISC_ERROR | */ + HP100_TX_ERROR | HP100_SET_LB, IRQ_MASK ); + } + else + { + hp100_outw( HP100_RX_PACKET | + HP100_RX_ERROR | HP100_SET_HB | + HP100_TX_ERROR | HP100_SET_LB , IRQ_MASK ); + } + + /* Enable MAC Tx and RX, set MAC modes, ... */ + /* Note: This function also turns on the interrupts. */ + hp100_set_multicast_list( dev ); } + static void hp100_stop_interface( struct device *dev ) { - int ioaddr = dev -> base_addr; - u_short val; - - hp100_outw( HP100_INT_EN | HP100_RESET_LB | - HP100_TRI_INT | HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW ); - val = hp100_inw( OPTION_LSW ); - hp100_page( HW_MAP ); - hp100_andb( HP100_BM_SLAVE, BM ); - hp100_page( MAC_CTRL ); - hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 ); - if ( !(val & HP100_HW_RST) ) return; - for ( val = 0; val < 6000; val++ ) - if ( ( hp100_inb( MAC_CFG_1 ) & (HP100_TX_IDLE | HP100_RX_IDLE) ) == - (HP100_TX_IDLE | HP100_RX_IDLE) ) - return; - printk( "%s: hp100_stop_interface - timeout\n", dev -> name ); + struct hp100_private *lp = (struct hp100_private *)dev->priv; + int ioaddr = dev->base_addr; + u_int val; + +#ifdef HP100_DEBUG_B + printk("hp100: hp100_stop_interface %s\n",dev->name); + hp100_outw( 0x4221, TRACE ); +#endif + + if (lp->mode==1) + hp100_BM_shutdown( dev ); + else + { + /* Note: MMAP_DIS will be reenabled by start_interface */ + hp100_outw( HP100_INT_EN | HP100_RESET_LB | + HP100_TRI_INT | HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW ); + val = hp100_inw( OPTION_LSW ); + + hp100_page( MAC_CTRL ); + hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 ); + + if ( !(val & HP100_HW_RST) ) return; /* If reset, imm. return ... */ + /* ... else: busy wait until idle */ + for ( val = 0; val < 6000; val++ ) + if ( ( hp100_inb( MAC_CFG_1 ) & (HP100_TX_IDLE | HP100_RX_IDLE) ) == + (HP100_TX_IDLE | HP100_RX_IDLE) ) + { + hp100_page(PERFORMANCE); + return; + } + printk( "%s: hp100_stop_interface - timeout\n", dev->name ); + hp100_page(PERFORMANCE); + } } + static void hp100_load_eeprom( struct device *dev ) { int i; - int ioaddr = dev -> base_addr; + int ioaddr = dev->base_addr; + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4222, TRACE ); +#endif hp100_page( EEPROM_CTRL ); hp100_andw( ~HP100_EEPROM_LOAD, EEPROM_CTRL ); hp100_orw( HP100_EEPROM_LOAD, EEPROM_CTRL ); - for ( i = 0; i < 6000; i++ ) - if ( !( hp100_inw( OPTION_MSW ) & HP100_EE_LOAD ) ) return; - printk( "%s: hp100_load_eeprom - timeout\n", dev -> name ); + for ( i = 0; i < 10000; i++ ) + if ( !( hp100_inb( OPTION_MSW ) & HP100_EE_LOAD ) ) return; + printk( "%s: hp100_load_eeprom - timeout\n", dev->name ); } -/* return values: LAN_10, LAN_100 or LAN_ERR (not connected or hub is down)... */ - + +/* Sense connection status. + * return values: LAN_10 - Connected to 10Mbit/s network + * LAN_100 - Connected to 100Mbit/s network + * LAN_ERR - not connected or 100Mbit/s Hub down + */ static int hp100_sense_lan( struct device *dev ) { - int i; - int ioaddr = dev -> base_addr; + int ioaddr = dev->base_addr; u_short val_VG, val_10; - struct hp100_private *lp = (struct hp100_private *)dev -> priv; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4223, TRACE ); +#endif hp100_page( MAC_CTRL ); - hp100_orw( HP100_VG_RESET, LAN_CFG_VG ); - val_10 = hp100_inw( LAN_CFG_10 ); - val_VG = hp100_inw( LAN_CFG_VG ); -#ifdef HP100_DEBUG_SENSE + /* Enable Auto Selection */ + /* hp100_orb( HP100_VG_RESET|HP100_LINK_CMD|HP100_VG_SEL, VG_LAN_CFG_1 ); */ + /* hp100_orb( HP100_DOT3_MAC,10_LAN_CFG_2); */ + /* hp100_orb( HP100_AUTO_MODE,MAC_CFG_3); */ + /* Now we have to wait a while... */ + /* for(i=0; i<5000; i++) */ + /* { */ + val_10 = hp100_inb( 10_LAN_CFG_1 ); + val_VG = hp100_inb( VG_LAN_CFG_1 ); + /* } */ +#ifdef HP100_DEBUG printk( "hp100_sense_lan: val_VG = 0x%04x, val_10 = 0x%04x\n", val_VG, val_10 ); #endif if ( val_10 & HP100_LINK_BEAT_ST ) return HP100_LAN_10; - if ( lp -> id -> id == 0x02019F022 ) /* HP J27248B doesn't have 100Mb/s interface */ - return HP100_LAN_ERR; - for ( i = 0; i < 2500; i++ ) - { - val_VG = hp100_inw( LAN_CFG_VG ); - if ( val_VG & HP100_LINK_CABLE_ST ) return HP100_LAN_100; - } + if ( (lp->id->id == 0x02019F022) || + (lp->id->id == 0x01042103c) || + (lp->id->id == 0x01040103c) ) + { + hp100_page(PERFORMANCE); + return HP100_LAN_ERR; /* Those cards don't have a 100 Mbit connector */ + } + /* for ( i = 0; i < 2500; i++ ) */ + /* { */ + val_VG = hp100_inb( VG_LAN_CFG_1 ); + hp100_page(PERFORMANCE); + + if ( val_VG & HP100_LINK_CABLE_ST ) /* Can hear the HUBs tone. */ + return HP100_LAN_100; + /* } */ return HP100_LAN_ERR; } + + static int hp100_down_vg_link( struct device *dev ) { - int ioaddr = dev -> base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + int ioaddr = dev->base_addr; unsigned long time; - int i; + long savelan, newlan; + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4224, TRACE ); + printk("hp100: down_vg_link\n"); +#endif hp100_page( MAC_CTRL ); - for ( i = 2500; i > 0; i-- ) - if ( hp100_inw( LAN_CFG_VG ) & HP100_LINK_CABLE_ST ) break; - if ( i <= 0 ) /* not signal - not logout */ + time=jiffies+(HZ/4); + do{ + if ( hp100_inb( VG_LAN_CFG_1 ) & HP100_LINK_CABLE_ST ) break; + } while (time>jiffies); + + if ( jiffies >= time ) /* no signal->no logout */ return 0; - hp100_andw( ~HP100_LINK_CMD, LAN_CFG_VG ); - time = jiffies + 10*HZ/100; - while ( time > jiffies ) - if ( !( hp100_inw( LAN_CFG_VG ) & ( HP100_LINK_UP_ST | - HP100_LINK_CABLE_ST | - HP100_LINK_GOOD_ST ) ) ) - return 0; + + /* Drop the VG Link by clearing the link up cmd and load addr.*/ + + hp100_andb( ~( HP100_LOAD_ADDR| HP100_LINK_CMD), VG_LAN_CFG_1); + hp100_orb( HP100_VG_SEL, VG_LAN_CFG_1); + + /* Conditionally stall for >250ms on Link-Up Status (to go down) */ + time=jiffies+(HZ/2); + do{ + if ( !(hp100_inb( VG_LAN_CFG_1) & HP100_LINK_UP_ST) ) break; + } while(time>jiffies); + #ifdef HP100_DEBUG - printk( "hp100_down_vg_link: timeout\n" ); + if (jiffies>=time) + printk("hp100_down_vg_link: Link does not go down?\n"); #endif - return -EIO; -} -static int hp100_login_to_vg_hub( struct device *dev ) -{ - int i; - int ioaddr = dev -> base_addr; - u_short val; - unsigned long time; + /* To prevent condition where Rev 1 VG MAC and old hubs do not complete */ + /* logout under traffic (even though all the status bits are cleared), */ + /* do this workaround to get the Rev 1 MAC in its idle state */ + if ( lp->chip==HP100_CHIPID_LASSEN ) + { + /* Reset VG MAC to insure it leaves the logoff state even if */ + /* the Hub is still emitting tones */ + hp100_andb(~HP100_VG_RESET, VG_LAN_CFG_1); + udelay(1500); /* wait for >1ms */ + hp100_orb(HP100_VG_RESET, VG_LAN_CFG_1); /* Release Reset */ + udelay(1500); + } + + /* New: For lassen, switch to 10 Mbps mac briefly to clear training ACK */ + /* to get the VG mac to full reset. This is not req.d with later chips */ + /* Note: It will take the between 1 and 2 seconds for the VG mac to be */ + /* selected again! This will be left to the connect hub function to */ + /* perform if desired. */ + if (lp->chip==HP100_CHIPID_LASSEN) + { + /* Have to write to 10 and 100VG control registers simultaneously */ + savelan=newlan=hp100_inl(10_LAN_CFG_1); /* read 10+100 LAN_CFG regs */ + newlan &= ~(HP100_VG_SEL<<16); + newlan |= (HP100_DOT3_MAC)<<8; + hp100_andb( ~HP100_AUTO_MODE, MAC_CFG_3); /* Autosel off */ + hp100_outl(newlan, 10_LAN_CFG_1); + + /* Conditionally stall for 5sec on VG selected. */ + time=jiffies+(HZ*5); + do{ + if( !(hp100_inb(MAC_CFG_4) & HP100_MAC_SEL_ST) ) break; + } while(time>jiffies); - hp100_page( MAC_CTRL ); - hp100_orw( HP100_VG_RESET, LAN_CFG_VG ); - time = jiffies + ( HZ / 2 ); + hp100_orb( HP100_AUTO_MODE, MAC_CFG_3); /* Autosel back on */ + hp100_outl(savelan, 10_LAN_CFG_1); + } + + time=jiffies+(3*HZ); /* Timeout 3s */ do { - if ( hp100_inw( LAN_CFG_VG ) & HP100_LINK_CABLE_ST ) break; - } while ( time > jiffies ); - if ( time <= jiffies ) + if ( (hp100_inb( VG_LAN_CFG_1 )&HP100_LINK_CABLE_ST) == 0) break; + } while (time>jiffies); + + if(time<=jiffies) { #ifdef HP100_DEBUG - printk( "hp100_login_to_vg_hub: timeout for link\n" ); + printk( "hp100_down_vg_link: timeout\n" ); #endif return -EIO; } + + time=jiffies+(2*HZ); /* This seems to take a while.... */ + do {} while (time>jiffies); + + return 0; +} - if ( hp100_down_vg_link( dev ) < 0 ) /* if fail, try reset VG link */ - { - hp100_andw( ~HP100_VG_RESET, LAN_CFG_VG ); - hp100_orw( HP100_VG_RESET, LAN_CFG_VG ); - } - /* bring up link */ - hp100_orw( HP100_LOAD_ADDR | HP100_LINK_CMD, LAN_CFG_VG ); - for ( i = 2500; i > 0; i-- ) - if ( hp100_inw( LAN_CFG_VG ) & HP100_LINK_CABLE_ST ) break; - if ( i <= 0 ) + +static int hp100_login_to_vg_hub( struct device *dev, u_short force_relogin ) +{ + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + u_short val=0; + unsigned long time; + int startst; + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4225, TRACE ); + printk("hp100: login_to_vg_hub\n"); +#endif + + /* Initiate a login sequence iff VG MAC is enabled and either Load Address + * bit is zero or the force relogin flag is set (e.g. due to MAC address or + * promiscuous mode change) + */ + hp100_page( MAC_CTRL ); + startst=hp100_inb( VG_LAN_CFG_1 ); + if((force_relogin==TRUE)||(hp100_inb( MAC_CFG_4 )&HP100_MAC_SEL_ST)) { -#ifdef HP100_DEBUG - printk( "hp100_login_to_vg_hub: timeout for link (bring up)\n" ); +#ifdef HP100_DEBUG_TRAINING + printk("hp100: Start training\n"); #endif - goto down_link; - } - time = jiffies + ( HZ / 2 ); - do { - val = hp100_inw( LAN_CFG_VG ); - if ( ( val & ( HP100_LINK_UP_ST | HP100_LINK_GOOD_ST ) ) == - ( HP100_LINK_UP_ST | HP100_LINK_GOOD_ST ) ) - return 0; /* success */ - } while ( time > jiffies ); - if ( val & HP100_LINK_GOOD_ST ) - printk( "%s: 100Mb cable training failed, check cable.\n", dev -> name ); - else - printk( "%s: 100Mb node not accepted by hub, check frame type or security.\n", dev -> name ); + /* Ensure VG Reset bit is 1 (i.e., do not reset)*/ + hp100_orb( HP100_VG_RESET , VG_LAN_CFG_1 ); + + /* If Lassen AND auto-select-mode AND VG tones were sensed on */ + /* entry then temporarily put them into force 100Mbit mode */ + if((lp->chip==HP100_CHIPID_LASSEN)&&( startst & HP100_LINK_CABLE_ST ) ) + hp100_andb( ~HP100_DOT3_MAC, 10_LAN_CFG_2 ); + + /* Drop the VG link by zeroing Link Up Command and Load Address */ + hp100_andb( ~(HP100_LINK_CMD/* |HP100_LOAD_ADDR */), VG_LAN_CFG_1); + +#ifdef HP100_DEBUG_TRAINING + printk("hp100: Bring down the link\n"); +#endif + + /* Wait for link to drop */ + time = jiffies + (HZ/10); + do { + if (~(hp100_inb( VG_LAN_CFG_1 )& HP100_LINK_UP_ST) ) break; + } while (time>jiffies); + + /* Start an addressed training and optionally request promiscuous port */ + if ( (dev->flags) & IFF_PROMISC ) + { + hp100_orb( HP100_PROM_MODE, VG_LAN_CFG_2); + if(lp->chip==HP100_CHIPID_LASSEN) + hp100_orw( HP100_MACRQ_PROMSC, TRAIN_REQUEST ); + } + else + { + hp100_andb( ~HP100_PROM_MODE, VG_LAN_CFG_2); + /* For ETR parts we need to reset the prom. bit in the training + * register, otherwise promiscious mode won't be disabled. + */ + if(lp->chip==HP100_CHIPID_LASSEN) + { + hp100_andw( ~HP100_MACRQ_PROMSC, TRAIN_REQUEST ); + } + } + + /* With ETR parts, frame format request bits can be set. */ + if(lp->chip==HP100_CHIPID_LASSEN) + hp100_orb( HP100_MACRQ_FRAMEFMT_EITHER, TRAIN_REQUEST); + + hp100_orb( HP100_LINK_CMD|HP100_LOAD_ADDR|HP100_VG_RESET, VG_LAN_CFG_1); + + /* Note: Next wait could be omitted for Hood and earlier chips under */ + /* certain circumstances */ + /* TODO: check if hood/earlier and skip wait. */ + + /* Wait for either short timeout for VG tones or long for login */ + /* Wait for the card hardware to signalise link cable status ok... */ + hp100_page( MAC_CTRL ); + time = jiffies + ( 1*HZ ); /* 1 sec timeout for cable st */ + do { + if ( hp100_inb( VG_LAN_CFG_1 ) & HP100_LINK_CABLE_ST ) break; + } while ( jiffies < time ); + + if ( jiffies >= time ) + { +#ifdef HP100_DEBUG_TRAINING + printk( "hp100: Link cable status not ok? Training aborted.\n" ); +#endif + } + else + { +#ifdef HP100_DEBUG_TRAINING + printk( "hp100: HUB tones detected. Trying to train.\n"); +#endif + + time = jiffies + ( 2*HZ ); /* again a timeout */ + do { + val = hp100_inb( VG_LAN_CFG_1 ); + if ( (val & ( HP100_LINK_UP_ST )) ) + { +#ifdef HP100_DEBUG_TRAINING + printk( "hp100: Passed training.\n"); +#endif + break; + } + } while ( time > jiffies ); + } + + /* If LINK_UP_ST is set, then we are logged into the hub. */ + if ( (jiffies<=time) && (val & HP100_LINK_UP_ST) ) + { +#ifdef HP100_DEBUG_TRAINING + printk( "hp100: Successfully logged into the HUB.\n"); + if(lp->chip==HP100_CHIPID_LASSEN) + { + val = hp100_inw(TRAIN_ALLOW); + printk( "hp100: Card supports 100VG MAC Version \"%s\" ", + (hp100_inw(TRAIN_REQUEST)&HP100_CARD_MACVER) ? "802.12" : "Pre"); + printk( "Driver will use MAC Version \"%s\"\n", + ( val & HP100_HUB_MACVER) ? "802.12" : "Pre" ); + printk( "hp100: Frame format is %s.\n",(val&HP100_MALLOW_FRAMEFMT)?"802.5":"802.3"); + } +#endif + } + else + { + /* If LINK_UP_ST is not set, login was not successful */ + printk("hp100/%s: Problem logging into the HUB.\n",dev->name); + if(lp->chip==HP100_CHIPID_LASSEN) + { + /* Check allowed Register to find out why there is a problem. */ + val = hp100_inw( TRAIN_ALLOW ); /* wont work on non-ETR card */ +#ifdef HP100_DEBUG_TRAINING + printk("hp100: MAC Configuration requested: 0x%04x, HUB allowed: 0x%04x\n", hp100_inw(TRAIN_REQUEST), val); +#endif + if ( val & HP100_MALLOW_ACCDENIED ) + printk("hp100: HUB access denied.\n"); + if ( val & HP100_MALLOW_CONFIGURE ) + printk("hp100: MAC Configuration is incompatible with the Network.\n"); + if ( val & HP100_MALLOW_DUPADDR ) + printk("hp100: Duplicate MAC Address on the Network.\n"); + } + } + + /* If we have put the chip into forced 100 Mbit mode earlier, go back */ + /* to auto-select mode */ + + if( (lp->chip==HP100_CHIPID_LASSEN)&&(startst & HP100_LINK_CABLE_ST) ) + { + hp100_page( MAC_CTRL ); + hp100_orb( HP100_DOT3_MAC, 10_LAN_CFG_2 ); + } + + val=hp100_inb(VG_LAN_CFG_1); -down_link: - hp100_down_vg_link( dev ); - hp100_page( MAC_CTRL ); - hp100_andw( ~( HP100_LOAD_ADDR | HP100_PROM_MODE ), LAN_CFG_VG ); - hp100_orw( HP100_LINK_CMD, LAN_CFG_VG ); + /* Clear the MISC_ERROR Interrupt, which might be generated when doing the relogin */ + hp100_page(PERFORMANCE); + hp100_outw( HP100_MISC_ERROR, IRQ_STATUS); + + if (val&HP100_LINK_UP_ST) + return(0); /* login was ok */ + else + { + printk("hp100: Training failed.\n"); + hp100_down_vg_link( dev ); + return -EIO; + } + } + /* no forced relogin & already link there->no training. */ return -EIO; } + +static void hp100_cascade_reset( struct device *dev, u_short enable ) +{ + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + int i; + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4226, TRACE ); + printk("hp100: cascade_reset\n"); +#endif + + if (enable==TRUE) + { + hp100_outw( HP100_HW_RST | HP100_RESET_LB, OPTION_LSW ); + if(lp->chip==HP100_CHIPID_LASSEN) + { + /* Lassen requires a PCI transmit fifo reset */ + hp100_page( HW_MAP ); + hp100_andb( ~HP100_PCI_RESET, PCICTRL2 ); + hp100_orb( HP100_PCI_RESET, PCICTRL2 ); + /* Wait for min. 300 ns */ + /* we cant use jiffies here, because it may be */ + /* that we have disabled the timer... */ + for (i=0; i<0xffff; i++); + hp100_andb( ~HP100_PCI_RESET, PCICTRL2 ); + hp100_page( PERFORMANCE ); + } + } + else + { /* bring out of reset */ + hp100_outw(HP100_HW_RST|HP100_SET_LB, OPTION_LSW); + for (i=0; i<0xffff; i++ ); + hp100_page(PERFORMANCE); + } +} + +#ifdef HP100_DEBUG +void hp100_RegisterDump( struct device *dev ) +{ + int ioaddr=dev->base_addr; + int Page; + int Register; + + /* Dump common registers */ + printk("hp100: Cascade Register Dump\n"); + printk("hardware id #1: 0x%.2x\n",hp100_inb(HW_ID)); + printk("hardware id #2/paging: 0x%.2x\n",hp100_inb(PAGING)); + printk("option #1: 0x%.4x\n",hp100_inw(OPTION_LSW)); + printk("option #2: 0x%.4x\n",hp100_inw(OPTION_MSW)); + + /* Dump paged registers */ + for (Page = 0; Page < 8; Page++) + { + /* Dump registers */ + printk("page: 0x%.2x\n",Page); + outw( Page, ioaddr+0x02); + for (Register = 0x8; Register < 0x22; Register += 2) + { + /* Display Register contents except data port */ + if (((Register != 0x10) && (Register != 0x12)) || (Page > 0)) + { + printk("0x%.2x = 0x%.4x\n",Register,inw(ioaddr+Register)); + } + } + } + hp100_page(PERFORMANCE); +} +#endif + + + /* * module section */ - + #ifdef MODULE -static int hp100_port = -1; -MODULE_PARM(hp100_port, "i"); +/* Parameters set by insmod */ +int hp100_port[5] = { 0, -1, -1, -1, -1 }; +#ifdef LINUX_2_1 +MODULE_PARM(hp100_port, "1-5i"); +#endif -static char devicename[9] = { 0, }; -static struct device dev_hp100 = { - devicename, /* device name is inserted by linux/drivers/net/net_init.c */ - 0, 0, 0, 0, - 0, 0, - 0, 0, 0, NULL, hp100_probe -}; +#ifdef LINUX_2_1 +char hp100_name[5][IFNAMSIZ] = { "", "", "", "", "" }; +MODULE_PARM(hp100_name, "1-5c" __MODULE_STRING(IFNAMSIZ)); +#else +static char devname[5][IFNAMSIZ] = { "", "", "", "", "" }; +static char *hp100_name[5] = { devname[0], devname[1], + devname[2], devname[3], + devname[4] }; +#endif + +/* List of devices */ +static struct device *hp100_devlist[5] = { NULL, NULL, NULL, NULL, NULL }; + +/* + * Note: if you have more than five 100vg cards in your pc, feel free to + * increase this value + */ + +/* + * Note: to register three eisa or pci devices, use: + * option hp100 hp100_port=0,0,0 + * to register one card at io 0x280 as eth239, use: + * option hp100 hp100_port=0x280 hp100_name=eth239 + */ int init_module( void ) { - if (hp100_port == 0 && !EISA_bus) + int i; + int ret = 0; + + if (hp100_port == 0 && !EISA_bus && !pcibios_present()) printk("HP100: You should not use auto-probing with insmod!\n"); - if ( hp100_port > 0 ) - dev_hp100.base_addr = hp100_port; - if ( register_netdev( &dev_hp100 ) != 0 ) - return -EIO; - return 0; + + /* Loop on all possible base addresses */ + i = -1; + while((hp100_port[++i] != -1) && (i < 5)) + { + /* Create device and set basics args */ + hp100_devlist[i] = kmalloc(sizeof(struct device), GFP_KERNEL); + memset(hp100_devlist[i], 0x00, sizeof(struct device)); + hp100_devlist[i]->name = hp100_name[i]; + hp100_devlist[i]->base_addr = hp100_port[i]; + hp100_devlist[i]->init = &hp100_probe; + + /* Try to create the device */ + if(register_netdev(hp100_devlist[i]) != 0) + { + /* DeAllocate everything */ + /* Note: if dev->priv is mallocated, there is no way to fail */ + kfree_s(hp100_devlist[i], sizeof(struct device)); + hp100_devlist[i] = (struct device *) NULL; + ret = -EIO; + } + } /* Loop over all devices */ + + return ret; } void cleanup_module( void ) { - unregister_netdev( &dev_hp100 ); - release_region( dev_hp100.base_addr, HP100_REGION_SIZE ); - if ( ((struct hp100_private *)dev_hp100.priv) -> mem_ptr_virt ) - iounmap( ((struct hp100_private *)dev_hp100.priv) -> mem_ptr_virt ); - kfree_s( dev_hp100.priv, sizeof( struct hp100_private ) ); - dev_hp100.priv = NULL; + int i; + + /* TODO: Check if all skb's are released/freed. */ + for(i = 0; i < 5; i++) + if(hp100_devlist[i] != (struct device *) NULL) + { + unregister_netdev( hp100_devlist[i] ); + release_region( hp100_devlist[i]->base_addr, HP100_REGION_SIZE ); + if( ((struct hp100_private *)hp100_devlist[i]->priv)->mode==1 ) /* busmaster */ + kfree_s( ((struct hp100_private *)hp100_devlist[i]->priv)->page_vaddr, MAX_RINGSIZE+0x0f); + if ( ((struct hp100_private *)hp100_devlist[i]->priv) -> mem_ptr_virt ) + iounmap( ((struct hp100_private *)hp100_devlist[i]->priv) -> mem_ptr_virt ); + kfree_s( hp100_devlist[i]->priv, sizeof( struct hp100_private ) ); + hp100_devlist[i]->priv = NULL; + kfree_s(hp100_devlist[i], sizeof(struct device)); + hp100_devlist[i] = (struct device *) NULL; + } } -#endif +#endif /* MODULE */ + + + +/* + * Local variables: + * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c hp100.c" + * c-indent-level: 2 + * tab-width: 8 + * End: + */ diff -u --recursive --new-file v2.1.39/linux/drivers/net/hp100.h linux/drivers/net/hp100.h --- v2.1.39/linux/drivers/net/hp100.h Mon May 6 02:26:08 1996 +++ linux/drivers/net/hp100.h Mon May 19 12:49:29 1997 @@ -1,9 +1,10 @@ /* * hp100.h: Hewlett Packard HP10/100VG ANY LAN ethernet driver for Linux. * - * Author: Jaroslav Kysela, + * $Id: hp100.h,v 1.51 1997/04/08 14:26:42 floeff Exp floeff $ * - * Header file... + * Authors: Jaroslav Kysela, + * Siegfried Loeffler * * This driver is based on the 'hpfepkt' crynwr packet driver. * @@ -16,9 +17,10 @@ /**************************************************************************** * Hardware Constants ****************************************************************************/ - -/* - * ATT2MD01 Register Page Constants + +/* + * Page Identifiers + * (Swap Paging Register, PAGING, bits 3:0, Offset 0x02) */ #define HP100_PAGE_PERFORMANCE 0x0 /* Page 0 */ @@ -30,11 +32,8 @@ #define HP100_PAGE_ID_MAC_ADDR 0x6 /* Page 6 */ #define HP100_PAGE_MMU_POINTER 0x7 /* Page 7 */ -/* - * ATT2MD01 Register Addresses - */ -/* Present on all pages */ +/* Registers that are present on all pages */ #define HP100_REG_HW_ID 0x00 /* R: (16) Unique card ID */ #define HP100_REG_TRACE 0x00 /* W: (16) Used for debug output */ @@ -47,14 +46,29 @@ #define HP100_REG_IRQ_STATUS 0x08 /* RW: (16) Which ints are pending */ #define HP100_REG_IRQ_MASK 0x0a /* RW: (16) Select ints to allow */ -#define HP100_REG_FRAGMENT_LEN 0x0c /* RW: (16)12:0 Current fragment len */ +#define HP100_REG_FRAGMENT_LEN 0x0c /* W: (16)12:0 Current fragment len */ +/* Note: For 32 bit systems, fragment len and offset registers are available */ +/* at offset 0x28 and 0x2c, where they can be written as 32bit values. */ #define HP100_REG_OFFSET 0x0e /* RW: (16)12:0 Offset to start read */ #define HP100_REG_DATA32 0x10 /* RW: (32) I/O mode data port */ #define HP100_REG_DATA16 0x12 /* RW: WORDs must be read from here */ #define HP100_REG_TX_MEM_FREE 0x14 /* RD: (32) Amount of free Tx mem */ +#define HP100_REG_TX_PDA_L 0x14 /* W: (32) BM: Ptr to PDL, Low Pri */ +#define HP100_REG_TX_PDA_H 0x1c /* W: (32) BM: Ptr to PDL, High Pri */ #define HP100_REG_RX_PKT_CNT 0x18 /* RD: (8) Rx count of pkts on card */ #define HP100_REG_TX_PKT_CNT 0x19 /* RD: (8) Tx count of pkts on card */ - +#define HP100_REG_RX_PDL 0x1a /* R: (8) BM: # rx pdl not executed */ +#define HP100_REG_TX_PDL 0x1b /* R: (8) BM: # tx pdl not executed */ +#define HP100_REG_RX_PDA 0x18 /* W: (32) BM: Up to 31 addresses */ + /* which point to a PDL */ +#define HP100_REG_SL_EARLY 0x1c /* (32) Enhanced Slave Early Rx */ +#define HP100_REG_STAT_DROPPED 0x20 /* R (12) Dropped Packet Counter */ +#define HP100_REG_STAT_ERRORED 0x22 /* R (8) Errored Packet Counter */ +#define HP100_REG_STAT_ABORT 0x23 /* R (8) Abort Counter/OW Coll. Flag */ +#define HP100_REG_RX_RING 0x24 /* W (32) Slave: RX Ring Pointers */ +#define HP100_REG_32_FRAGMENT_LEN 0x28 /* W (13) Slave: Fragment Length Reg */ +#define HP100_REG_32_OFFSET 0x2c /* W (16) Slave: Offset Register */ + /* Page 1 - MAC Address/Hash Table */ #define HP100_REG_MAC_ADDR 0x08 /* RW: (8) Cards MAC address */ @@ -68,27 +82,46 @@ #define HP100_REG_IRQ_CHANNEL 0x0d /* RW: (8) IRQ and edge/level int */ #define HP100_REG_SRAM 0x0e /* RW: (8) How much RAM on card */ #define HP100_REG_BM 0x0f /* RW: (8) Controls BM functions */ + +/* New on Page 2 for ETR chips: */ +#define HP100_REG_MODECTRL1 0x10 /* RW: (8) Mode Control 1 */ +#define HP100_REG_MODECTRL2 0x11 /* RW: (8) Mode Control 2 */ +#define HP100_REG_PCICTRL1 0x12 /* RW: (8) PCI Cfg 1 */ +#define HP100_REG_PCICTRL2 0x13 /* RW: (8) PCI Cfg 2 */ +#define HP100_REG_PCIBUSMLAT 0x15 /* RW: (8) PCI Bus Master Latency */ +#define HP100_REG_EARLYTXCFG 0x16 /* RW: (16) Early TX Cfg/Cntrl Reg */ +#define HP100_REG_EARLYRXCFG 0x18 /* RW: (8) Early RX Cfg/Cntrl Reg */ +#define HP100_REG_ISAPNPCFG1 0x1a /* RW: (8) ISA PnP Cfg/Cntrl Reg 1 */ +#define HP100_REG_ISAPNPCFG2 0x1b /* RW: (8) ISA PnP Cfg/Cntrl Reg 2 */ /* Page 3 - EEPROM/Boot ROM */ #define HP100_REG_EEPROM_CTRL 0x08 /* RW: (16) Used to load EEPROM */ +#define HP100_REG_BOOTROM_CTRL 0x0a -/* Page 4 - LAN Configuration */ +/* Page 4 - LAN Configuration (MAC_CTRL) */ -#define HP100_REG_LAN_CFG_10 0x08 /* RW: (16) Set 10M XCVR functions */ -#define HP100_REG_LAN_CFG_VG 0x0a /* RW: (16) Set 100M XCVR functions */ +#define HP100_REG_10_LAN_CFG_1 0x08 /* RW: (8) Set 10M XCVR functions */ +#define HP100_REG_10_LAN_CFG_2 0x09 /* RW: (8) 10M XCVR functions */ +#define HP100_REG_VG_LAN_CFG_1 0x0a /* RW: (8) Set 100M XCVR functions */ +#define HP100_REG_VG_LAN_CFG_2 0x0b /* RW: (8) 100M LAN Training cfgregs */ #define HP100_REG_MAC_CFG_1 0x0c /* RW: (8) Types of pkts to accept */ #define HP100_REG_MAC_CFG_2 0x0d /* RW: (8) Misc MAC functions */ -/* The follow clear when read: */ +#define HP100_REG_MAC_CFG_3 0x0e /* RW: (8) Misc MAC functions */ +#define HP100_REG_MAC_CFG_4 0x0f /* R: (8) Misc MAC states */ #define HP100_REG_DROPPED 0x10 /* R: (16),11:0 Pkts cant fit in mem*/ #define HP100_REG_CRC 0x12 /* R: (8) Pkts with CRC */ #define HP100_REG_ABORT 0x13 /* R: (8) Aborted Tx pkts */ - +#define HP100_REG_TRAIN_REQUEST 0x14 /* RW: (16) Endnode MAC register.*/ +#define HP100_REG_TRAIN_ALLOW 0x16 /* R: (16) Hub allowed register */ + /* Page 5 - MMU */ #define HP100_REG_RX_MEM_STOP 0x0c /* RW: (16) End of Rx ring addr */ #define HP100_REG_TX_MEM_STOP 0x0e /* RW: (16) End of Tx ring addr */ - +#define HP100_REG_PDL_MEM_STOP 0x10 /* Not used by 802.12 devices */ +#define HP100_REG_ECB_MEM_STOP 0x14 /* I've no idea what this is */ + /* Page 6 - Card ID/Physical LAN Address */ #define HP100_REG_BOARD_ID 0x08 /* R: (8) EISA/ISA card ID */ @@ -99,32 +132,46 @@ /* Page 7 - MMU Current Pointers */ -#define HP100_REG_RX_MEM_BR 0x08 /* R: (16) Current begin of Rx ring */ -#define HP100_REG_RX_MEM_ER 0x0a /* R: (16) Current end of Rx ring */ -#define HP100_REG_TX_MEM_BR 0x0c /* R: (16) Current begin of Tx ring */ -#define HP100_REG_TX_MEM_ER 0x0e /* R: (16) Current end of Rx ring */ -#define HP100_REG_MEM_DEBUG 0x1a /* RW: (16) Used for memory tests */ - -/* - * HardwareIDReg bits/masks - */ - -#define HP100_HW_ID_0 0x50 /* Hardware ID bytes. */ -#define HP100_HW_ID_1 0x48 -#define HP100_HW_ID_2_REVA 0x50 /* Rev. A ID. NOTE: lower nibble not used */ -#define HP100_HW_ID_3 0x53 - -/* - * OptionLSWReg bits/masks - */ - -#define HP100_DEBUG_EN 0x8000 /* 0:Disable, 1:Enable Debug Dump Pointer */ -#define HP100_RX_HDR 0x4000 /* 0:Disable, 1:Enable putting pkt into */ - /* system memory before Rx interrupt */ -#define HP100_MMAP_DIS 0x2000 /* 0:Enable, 1:Disable memory mapping. */ - /* MMAP_DIS must be 0 and MEM_EN must */ - /* be 1 for memory-mapped mode to be */ - /* enabled */ +#define HP100_REG_PTR_RXSTART 0x08 /* R: (16) Current begin of Rx ring */ +#define HP100_REG_PTR_RXEND 0x0a /* R: (16) Current end of Rx ring */ +#define HP100_REG_PTR_TXSTART 0x0c /* R: (16) Current begin of Tx ring */ +#define HP100_REG_PTR_TXEND 0x0e /* R: (16) Current end of Rx ring */ +#define HP100_REG_PTR_RPDLSTART 0x10 +#define HP100_REG_PTR_RPDLEND 0x12 +#define HP100_REG_PTR_RINGPTRS 0x14 +#define HP100_REG_PTR_MEMDEBUG 0x1a +/* ------------------------------------------------------------------------ */ + + +/* + * Hardware ID Register I (Always available, HW_ID, Offset 0x00) + */ +#define HP100_HW_ID_CASCADE 0x4850 /* Identifies Cascade Chip */ + +/* + * Hardware ID Register 2 & Paging Register + * (Always available, PAGING, Offset 0x02) + * Bits 15:4 are for the Chip ID + */ +#define HP100_CHIPID_MASK 0xFFF0 +#define HP100_CHIPID_SHASTA 0x5350 /* Not 802.12 compliant */ + /* EISA BM/SL, MCA16/32 SL, ISA SL */ +#define HP100_CHIPID_RAINIER 0x5360 /* Not 802.12 compliant EISA BM,*/ + /* PCI SL, MCA16/32 SL, ISA SL */ +#define HP100_CHIPID_LASSEN 0x5370 /* 802.12 compliant PCI BM, PCI SL */ + /* LRF supported */ + +/* + * Option Registers I and II + * (Always available, OPTION_LSW, Offset 0x04-0x05) + */ +#define HP100_DEBUG_EN 0x8000 /* 0:Dis., 1:Enable Debug Dump Ptr. */ +#define HP100_RX_HDR 0x4000 /* 0:Dis., 1:Enable putting pkt into */ + /* system mem. before Rx interrupt */ +#define HP100_MMAP_DIS 0x2000 /* 0:Enable, 1:Disable mem.mapping. */ + /* MMAP_DIS must be 0 and MEM_EN */ + /* must be 1 for memory-mapped */ + /* mode to be enabled */ #define HP100_EE_EN 0x1000 /* 0:Disable,1:Enable EEPROM writing */ #define HP100_BM_WRITE 0x0800 /* 0:Slave, 1:Bus Master for Tx data */ #define HP100_BM_READ 0x0400 /* 0:Slave, 1:Bus Master for Rx data */ @@ -132,121 +179,236 @@ #define HP100_MEM_EN 0x0040 /* Config program set this to */ /* 0:Disable, 1:Enable mem map. */ /* See MMAP_DIS. */ -#define HP100_IO_EN 0x0020 /* 0:Disable, 1:Enable I/O transfers */ -#define HP100_BOOT_EN 0x0010 /* 0:Disable, 1:Enable boot ROM access */ -#define HP100_FAKE_INT 0x0008 /* 0:No int, 1:int */ -#define HP100_INT_EN 0x0004 /* 0:Disable, 1:Enable ints from card */ +#define HP100_IO_EN 0x0020 /* 1:Enable I/O transfers */ +#define HP100_BOOT_EN 0x0010 /* 1:Enable boot ROM access */ +#define HP100_FAKE_INT 0x0008 /* 1:int */ +#define HP100_INT_EN 0x0004 /* 1:Enable ints from card */ #define HP100_HW_RST 0x0002 /* 0:Reset, 1:Out of reset */ + /* NIC reset on 0 to 1 transition */ /* - * OptionMSWReg bits/masks + * Option Register III + * (Always available, OPTION_MSW, Offset 0x06) */ -#define HP100_PRIORITY_TX 0x0080 /* 0:Don't, 1:Do all Tx pkts as priority */ +#define HP100_PRIORITY_TX 0x0080 /* 1:Do all Tx pkts as priority */ #define HP100_EE_LOAD 0x0040 /* 1:EEPROM loading, 0 when done */ -#define HP100_ADV_NXT_PKT 0x0004 /* 1:Advance to next pkt in Rx queue, */ +#define HP100_ADV_NXT_PKT 0x0004 /* 1:Advance to next pkt in Rx queue */ /* h/w will set to 0 when done */ -#define HP100_TX_CMD 0x0002 /* 1:Tell h/w download done, h/w will set */ - /* to 0 when done */ +#define HP100_TX_CMD 0x0002 /* 1:Tell h/w download done, h/w */ + /* will set to 0 when done */ /* - * InterruptStatusReg/InterruptMaskReg bits/masks. These bits will 0 when a 1 - * is written to them. - */ + * Interrupt Status Registers I and II + * (Page PERFORMANCE, IRQ_STATUS, Offset 0x08-0x09) + * Note: With old chips, these Registers will clear when 1 is written to them + * with new chips this depends on setting of CLR_ISMODE + */ +#define HP100_RX_EARLY_INT 0x2000 +#define HP100_RX_PDA_ZERO 0x1000 +#define HP100_RX_PDL_FILL_COMPL 0x0800 #define HP100_RX_PACKET 0x0400 /* 0:No, 1:Yes pkt has been Rx */ #define HP100_RX_ERROR 0x0200 /* 0:No, 1:Yes Rx pkt had error */ +#define HP100_TX_PDA_ZERO 0x0020 /* 1 when PDA count goes to zero */ #define HP100_TX_SPACE_AVAIL 0x0010 /* 0:<8192, 1:>=8192 Tx free bytes */ #define HP100_TX_COMPLETE 0x0008 /* 0:No, 1:Yes a Tx has completed */ +#define HP100_MISC_ERROR 0x0004 /* 0:No, 1:Lan Link down or bus error*/ #define HP100_TX_ERROR 0x0002 /* 0:No, 1:Yes Tx pkt had error */ - + /* - * TxMemoryFreeCountReg bits/masks. + * Xmit Memory Free Count + * (Page PERFORMANCE, TX_MEM_FREE, Offset 0x14) (Read only, 32bit) */ -#define HP100_AUTO_COMPARE 0x8000 /* Says at least 8k is available for Tx. */ - /* NOTE: This mask is for the upper */ - /* word of the register. */ +#define HP100_AUTO_COMPARE 0x80000000 /* Tx Space avail & pkts<255 */ +#define HP100_FREE_SPACE 0x7fffffe0 /* Tx free memory */ /* - * IRQChannelReg bits/masks. + * IRQ Channel + * (Page HW_MAP, IRQ_CHANNEL, Offset 0x0d) */ #define HP100_ZERO_WAIT_EN 0x80 /* 0:No, 1:Yes asserts NOWS signal */ +#define HP100_IRQ_SCRAMBLE 0x40 +#define HP100_BOND_HP 0x20 #define HP100_LEVEL_IRQ 0x10 /* 0:Edge, 1:Level type interrupts. */ - /* Only valid on EISA cards. */ -#define HP100_IRQ_MASK 0x0F /* Isolate the IRQ bits */ + /* (Only valid on EISA cards) */ +#define HP100_IRQMASK 0x0F /* Isolate the IRQ bits */ /* - * SRAMReg bits/masks. + * SRAM Parameters + * (Page HW_MAP, SRAM, Offset 0x0e) */ #define HP100_RAM_SIZE_MASK 0xe0 /* AND to get SRAM size index */ -#define HP100_RAM_SIZE_SHIFT 0x05 /* Shift count to put index in lower bits */ +#define HP100_RAM_SIZE_SHIFT 0x05 /* Shift count(put index in lwr bits)*/ + +/* + * Bus Master Register + * (Page HW_MAP, BM, Offset 0x0f) + */ +#define HP100_BM_BURST_RD 0x01 /* EISA only: 1=Use burst trans. fm system */ + /* memory to chip (tx) */ +#define HP100_BM_BURST_WR 0x02 /* EISA only: 1=Use burst trans. fm system */ + /* memory to chip (rx) */ +#define HP100_BM_MASTER 0x04 /* 0:Slave, 1:BM mode */ +#define HP100_BM_PAGE_CK 0x08 /* This bit should be set whenever in*/ + /* an EISA system */ +#define HP100_BM_PCI_8CLK 0x40 /* ... cycles 8 clocks apart */ + + +/* + * Mode Control Register I + * (Page HW_MAP, MODECTRL1, Offset0x10) + */ +#define HP100_TX_DUALQ 0x10 + /* If set and BM -> dual tx pda queues*/ +#define HP100_ISR_CLRMODE 0x02 /* If set ISR will clear all pending */ + /* interrupts on read (etr only?) */ +#define HP100_EE_NOLOAD 0x04 /* Status whether res will be loaded */ + /* from the eeprom */ +#define HP100_TX_CNT_FLG 0x08 /* Controls Early TX Reg Cnt Field */ +#define HP100_PDL_USE3 0x10 /* If set BM engine will read only */ + /* first three data elements of a PDL */ + /* on the first access. */ +#define HP100_BUSTYPE_MASK 0xe0 /* Three bit bus type info */ + +/* + * Mode Control Register II + * (Page HW_MAP, MODECTRL2, Offset0x11) + */ +#define HP100_EE_MASK 0x0f /* Tell EEPROM circuit not to load */ + /* certain resources */ +#define HP100_DIS_CANCEL 0x20 /* For tx dualq mode operation */ +#define HP100_EN_PDL_WB 0x40 /* 1: Status of PDL completion may be */ + /* written back to system mem */ +#define HP100_EN_BUS_FAIL 0x80 /* Enables bus-fail portion of misc */ + /* interrupt */ + +/* + * PCI Configuration and Control Register I + * (Page HW_MAP, PCICTRL1, Offset 0x12) + */ +#define HP100_LO_MEM 0x01 /* 1: Mapped Mem requested below 1MB */ +#define HP100_NO_MEM 0x02 /* 1: Disables Req for sysmem to PCI */ + /* bios */ +#define HP100_USE_ISA 0x04 /* 1: isa type decodes will occur */ + /* simultaneously with PCI decodes */ +#define HP100_IRQ_HI_MASK 0xf0 /* pgmed by pci bios */ +#define HP100_PCI_IRQ_HI_MASK 0x78 /* Isolate 4 bits for PCI IRQ */ + +/* + * PCI Configuration and Control Register II + * (Page HW_MAP, PCICTRL2, Offset 0x13) + */ +#define HP100_RD_LINE_PDL 0x01 /* 1: PCI command Memory Read Line en */ +#define HP100_RD_TX_DATA_MASK 0x06 /* choose PCI memread cmds for TX */ +#define HP100_MWI 0x08 /* 1: en. PCI memory write invalidate */ +#define HP100_ARB_MODE 0x10 /* Select PCI arbitor type */ +#define HP100_STOP_EN 0x20 /* Enables PCI state machine to issue */ + /* pci stop if cascade not ready */ +#define HP100_IGNORE_PAR 0x40 /* 1: PCI state machine ignores parity*/ +#define HP100_PCI_RESET 0x80 /* 0->1: Reset PCI block */ + +/* + * Early TX Configuration and Control Register + * (Page HW_MAP, EARLYTXCFG, Offset 0x16) + */ +#define HP100_EN_EARLY_TX 0x8000 /* 1=Enable Early TX */ +#define HP100_EN_ADAPTIVE 0x4000 /* 1=Enable adaptive mode */ +#define HP100_EN_TX_UR_IRQ 0x2000 /* reserved, must be 0 */ +#define HP100_EN_LOW_TX 0x1000 /* reserved, must be 0 */ +#define HP100_ET_CNT_MASK 0x0fff /* bits 11..0: ET counters */ /* - * BMReg bits/masks. + * Early RX Configuration and Control Register + * (Page HW_MAP, EARLYRXCFG, Offset 0x18) */ -#define HP100_BM_SLAVE 0x04 /* 0:Slave, 1:BM mode */ +#define HP100_EN_EARLY_RX 0x80 /* 1=Enable Early RX */ +#define HP100_EN_LOW_RX 0x40 /* reserved, must be 0 */ +#define HP100_RX_TRIP_MASK 0x1f /* bits 4..0: threshold at which the + * early rx circuit will start the + * dma of received packet into system + * memory for BM */ /* - * EEPROMControlReg bits/masks. + * Serial Devices Control Register + * (Page EEPROM_CTRL, EEPROM_CTRL, Offset 0x08) */ -#define HP100_EEPROM_LOAD 0x0001 /* 0->1 loads the EEPROM into registers. */ - /* When it goes back to 0, load is */ - /* complete. This should take ~600us. */ +#define HP100_EEPROM_LOAD 0x0001 /* 0->1 loads EEPROM into registers. */ + /* When it goes back to 0, load is */ + /* complete. This should take ~600us.*/ /* - * LANCntrCfg10Reg bits/masks. + * 10MB LAN Control and Configuration Register I + * (Page MAC_CTRL, 10_LAN_CFG_1, Offset 0x08) */ -#define HP100_SQU_ST 0x0100 /* 0:No, 1:Yes collision signal sent */ - /* after Tx. Only used for AUI. */ -#define HP100_MAC10_SEL 0x00c0 /* Get bits to indicate MAC */ -#define HP100_AUI_SEL 0x0020 /* Status of AUI selection */ -#define HP100_LOW_TH 0x0010 /* 0:No, 1:Yes allow better cabling */ -#define HP100_LINK_BEAT_DIS 0x0008 /* 0:Enable, 1:Disable link beat */ -#define HP100_LINK_BEAT_ST 0x0004 /* 0:No, 1:Yes link beat being Rx */ -#define HP100_R_ROL_ST 0x0002 /* 0:No, 1:Yes Rx twisted pair has been */ - /* reversed */ -#define HP100_AUI_ST 0x0001 /* 0:No, 1:Yes use AUI on TP card */ +#define HP100_MAC10_SEL 0xc0 /* Get bits to indicate MAC */ +#define HP100_AUI_SEL 0x20 /* Status of AUI selection */ +#define HP100_LOW_TH 0x10 /* 0:No, 1:Yes allow better cabling */ +#define HP100_LINK_BEAT_DIS 0x08 /* 0:Enable, 1:Disable link beat */ +#define HP100_LINK_BEAT_ST 0x04 /* 0:No, 1:Yes link beat being Rx */ +#define HP100_R_ROL_ST 0x02 /* 0:No, 1:Yes Rx twisted pair has */ + /* been reversed */ +#define HP100_AUI_ST 0x01 /* 0:No, 1:Yes use AUI on TP card */ -/* MAC Selection, use with MAC10_SEL bits */ +/* + * 10 MB LAN Control and Configuration Register II + * (Page MAC_CTRL, 10_LAN_CFG_2, Offset 0x09) + */ +#define HP100_SQU_ST 0x01 /* 0:No, 1:Yes collision signal sent */ + /* after Tx.Only used for AUI. */ +#define HP100_FULLDUP 0x02 /* 1: LXT901 XCVR fullduplx enabled */ +#define HP100_DOT3_MAC 0x04 /* 1: DOT 3 Mac sel. unless Autosel */ + +/* + * MAC Selection, use with MAC10_SEL bits + */ #define HP100_AUTO_SEL_10 0x0 /* Auto select */ #define HP100_XCVR_LXT901_10 0x1 /* LXT901 10BaseT transceiver */ #define HP100_XCVR_7213 0x2 /* 7213 transceiver */ #define HP100_XCVR_82503 0x3 /* 82503 transceiver */ - /* - * LANCntrCfgVGReg bits/masks. + * 100MB LAN Training Register + * (Page MAC_CTRL, VG_LAN_CFG_2, Offset 0x0b) (old, pre 802.12) */ -#define HP100_FRAME_FORMAT 0x0800 /* 0:802.3, 1:802.5 frames */ -#define HP100_BRIDGE 0x0400 /* 0:No, 1:Yes tell hub it's a bridge */ -#define HP100_PROM_MODE 0x0200 /* 0:No, 1:Yes tell hub card is */ - /* promiscuous */ -#define HP100_REPEATER 0x0100 /* 0:No, 1:Yes tell hub MAC wants to be */ - /* a cascaded repeater */ -#define HP100_MAC100_SEL 0x0080 /* 0:No, 1:Yes use 100 Mbit MAC */ -#define HP100_LINK_UP_ST 0x0040 /* 0:No, 1:Yes endnode logged in */ -#define HP100_LINK_CABLE_ST 0x0020 /* 0:No, 1:Yes cable can hear tones from */ - /* hub */ -#define HP100_LOAD_ADDR 0x0010 /* 0->1 card addr will be sent to hub. */ - /* 100ms later the link status bits are */ - /* valid */ -#define HP100_LINK_CMD 0x0008 /* 0->1 link will attempt to log in. */ - /* 100ms later the link status bits are */ - /* valid */ -#define HP100_LINK_GOOD_ST 0x0002 /* 0:No, 1:Yes cable passed training */ -#define HP100_VG_RESET 0x0001 /* 0:Yes, 1:No reset the 100VG MAC */ +#define HP100_FRAME_FORMAT 0x08 /* 0:802.3, 1:802.5 frames */ +#define HP100_BRIDGE 0x04 /* 0:No, 1:Yes tell hub i am a bridge */ +#define HP100_PROM_MODE 0x02 /* 0:No, 1:Yes tell hub card is */ + /* promiscuous */ +#define HP100_REPEATER 0x01 /* 0:No, 1:Yes tell hub MAC wants to */ + /* be a cascaded repeater */ + +/* + * 100MB LAN Control and Configuration Register + * (Page MAC_CTRL, VG_LAN_CFG_1, Offset 0x0a) + */ +#define HP100_VG_SEL 0x80 /* 0:No, 1:Yes use 100 Mbit MAC */ +#define HP100_LINK_UP_ST 0x40 /* 0:No, 1:Yes endnode logged in */ +#define HP100_LINK_CABLE_ST 0x20 /* 0:No, 1:Yes cable can hear tones */ + /* from hub */ +#define HP100_LOAD_ADDR 0x10 /* 0->1 card addr will be sent */ + /* 100ms later the link status */ + /* bits are valid */ +#define HP100_LINK_CMD 0x08 /* 0->1 link will attempt to log in. */ + /* 100ms later the link status */ + /* bits are valid */ +#define HP100_TRN_DONE 0x04 /* NEW ETR-Chips only: Will be reset */ + /* after LinkUp Cmd is given and set */ + /* when training has completed. */ +#define HP100_LINK_GOOD_ST 0x02 /* 0:No, 1:Yes cable passed training */ +#define HP100_VG_RESET 0x01 /* 0:Yes, 1:No reset the 100VG MAC */ /* - * MACConfiguration1Reg bits/masks. + * MAC Configuration Register I + * (Page MAC_CTRL, MAC_CFG_1, Offset 0x0c) */ #define HP100_RX_IDLE 0x80 /* 0:Yes, 1:No currently receiving pkts */ #define HP100_TX_IDLE 0x40 /* 0:Yes, 1:No currently Txing pkts */ -#define HP100_RX_EN 0x20 /* 0:No, 1:Yes allow receiving of pkts */ -#define HP100_TX_EN 0x10 /* 0:No, 1:Yes allow transmitting of pkts */ +#define HP100_RX_EN 0x20 /* 1: allow receiving of pkts */ +#define HP100_TX_EN 0x10 /* 1: allow transmitting of pkts */ #define HP100_ACC_ERRORED 0x08 /* 0:No, 1:Yes allow Rx of errored pkts */ #define HP100_ACC_MC 0x04 /* 0:No, 1:Yes allow Rx of multicast pkts */ #define HP100_ACC_BC 0x02 /* 0:No, 1:Yes allow Rx of broadcast pkts */ -#define HP100_ACC_PHY 0x01 /* 0:No, 1:Yes allow Rx of ALL physical pkts */ - +#define HP100_ACC_PHY 0x01 /* 0:No, 1:Yes allow Rx of ALL phys. pkts */ #define HP100_MAC1MODEMASK 0xf0 /* Hide ACC bits */ #define HP100_MAC1MODE1 0x00 /* Receive nothing, must also disable RX */ #define HP100_MAC1MODE2 0x00 @@ -254,15 +416,14 @@ #define HP100_MAC1MODE4 HP100_MAC1MODE3 | HP100_ACC_MC #define HP100_MAC1MODE5 HP100_MAC1MODE4 /* set mc hash to all ones also */ #define HP100_MAC1MODE6 HP100_MAC1MODE5 | HP100_ACC_PHY /* Promiscuous */ - /* Note MODE6 will receive all GOOD packets on the LAN. This really needs a mode 7 defined to be LAN Analyzer mode, which will receive errored and runt packets, and keep the CRC bytes. */ - -#define HP100_MAC1MODE7 MAC1MODE6 OR ACC_ERRORED +#define HP100_MAC1MODE7 HP100_MAC1MODE6 | HP100_ACC_ERRORED /* - * MACConfiguration2Reg bits/masks. + * MAC Configuration Register II + * (Page MAC_CTRL, MAC_CFG_2, Offset 0x0d) */ #define HP100_TR_MODE 0x80 /* 0:No, 1:Yes support Token Ring formats */ #define HP100_TX_SAME 0x40 /* 0:No, 1:Yes Tx same packet continuous */ @@ -270,9 +431,12 @@ /* transceiver */ #define HP100_LBK_MAC 0x10 /* 0:No, 1:Yes loopback through MAC */ #define HP100_CRC_I 0x08 /* 0:No, 1:Yes inhibit CRC on Tx packets */ +#define HP100_ACCNA 0x04 /* 1: For 802.5: Accept only token ring + * group addr that maches NA mask */ #define HP100_KEEP_CRC 0x02 /* 0:No, 1:Yes keep CRC on Rx packets. */ /* The length will reflect this. */ - +#define HP100_ACCFA 0x01 /* 1: For 802.5: Accept only functional + * addrs that match FA mask (page1) */ #define HP100_MAC2MODEMASK 0x02 #define HP100_MAC2MODE1 0x00 #define HP100_MAC2MODE2 0x00 @@ -283,6 +447,65 @@ #define HP100_MAC2MODE7 KEEP_CRC /* + * MAC Configuration Register III + * (Page MAC_CTRL, MAC_CFG_3, Offset 0x0e) + */ +#define HP100_PACKET_PACE 0x03 /* Packet Pacing: + * 00: No packet pacing + * 01: 8 to 16 uS delay + * 10: 16 to 32 uS delay + * 11: 32 to 64 uS delay + */ +#define HP100_LRF_EN 0x04 /* 1: External LAN Rcv Filter and + * TCP/IP Checksumming enabled. */ +#define HP100_AUTO_MODE 0x10 /* 1: AutoSelect between 10/100 */ + +/* + * MAC Configuration Register IV + * (Page MAC_CTRL, MAC_CFG_4, Offset 0x0f) + */ +#define HP100_MAC_SEL_ST 0x01 /* (R): Status of external VGSEL + * Signal, 1=100VG, 0=10Mbit sel. */ +#define HP100_LINK_FAIL_ST 0x02 /* (R): Status of Link Fail portion + * of the Misc. Interrupt */ + +/* + * 100 MB LAN Training Request/Allowed Registers + * (Page MAC_CTRL, TRAIN_REQUEST and TRAIN_ALLOW, Offset 0x14-0x16)(ETR parts only) + */ +#define HP100_MACRQ_REPEATER 0x0001 /* 1: MAC tells HUB it wants to be + * a cascaded repeater + * 0: ... wants to be a DTE */ +#define HP100_MACRQ_PROMSC 0x0006 /* 2 bits: Promiscious mode + * 00: Rcv only unicast packets + * specifically addr to this + * endnode + * 10: Rcv all pckts fwded by + * the local repeater */ +#define HP100_MACRQ_FRAMEFMT_EITHER 0x0018 /* 11: either format allowed */ +#define HP100_MACRQ_FRAMEFMT_802_3 0x0000 /* 00: 802.3 is requested */ +#define HP100_MACRQ_FRAMEFMT_802_5 0x0010 /* 10: 802.5 format is requested */ +#define HP100_CARD_MACVER 0xe000 /* R: 3 bit Cards 100VG MAC version */ +#define HP100_MALLOW_REPEATER 0x0001 /* If reset, requested access as an + * end node is allowed */ +#define HP100_MALLOW_PROMSC 0x0004 /* 2 bits: Promiscious mode + * 00: Rcv only unicast packets + * specifically addr to this + * endnode + * 10: Rcv all pckts fwded by + * the local repeater */ +#define HP100_MALLOW_FRAMEFMT 0x00e0 /* 2 bits: Frame Format + * 00: 802.3 format will be used + * 10: 802.5 format will be used */ +#define HP100_MALLOW_ACCDENIED 0x0400 /* N bit */ +#define HP100_MALLOW_CONFIGURE 0x0f00 /* C bit */ +#define HP100_MALLOW_DUPADDR 0x1000 /* D bit */ +#define HP100_HUB_MACVER 0xe000 /* R: 3 bit 802.12 MAC/RMAC training */ + /* protocol of repeater */ + +/* ****************************************************************************** */ + +/* * Set/Reset bits */ #define HP100_SET_HB 0x0100 /* 0:Set fields to 0 whose mask is 1 */ @@ -297,20 +520,45 @@ #define HP100_LAN_10 10 /* lan_type value for 10BaseT */ #define HP100_LAN_ERR (-1) /* lan_type value for link down */ -/* - * Receive Header Definition. +#define TRUE 1 +#define FALSE 0 + + +/* + * Bus Master Data Structures ---------------------------------------------- */ -struct hp100_rx_header { - u_short rx_length; /* Pkt length is bits 12:0 */ - u_short rx_status; /* status of the packet */ -}; +#define MAX_RX_PDL 30 /* Card limit = 31 */ +#define MAX_RX_FRAG 2 /* Dont need more... */ +#define MAX_TX_PDL 29 +#define MAX_TX_FRAG 2 /* Limit = 31 */ + +/* Define total PDL area size in bytes (should be 4096) */ +/* This is the size of kernel (dma) memory that will be allocated. */ +#define MAX_RINGSIZE ((MAX_RX_FRAG*8+4+4)*MAX_RX_PDL+(MAX_TX_FRAG*8+4+4)*MAX_TX_PDL)+16 + +/* Ethernet Packet Sizes */ +#define MIN_ETHER_SIZE 60 +#define MAX_ETHER_SIZE 1514 /* Needed for preallocation of */ + /* skb buffer when busmastering */ + +/* Tx or Rx Ring Entry */ +typedef struct hp100_ring { + u_int *pdl; /* Address of PDLs PDH, dword before + * this address is used for rx hdr */ + u_int pdl_paddr; /* Physical address of PDL */ + struct sk_buff *skb; + struct hp100_ring *next; +} hp100_ring_t; + + + +/* Mask for Header Descriptor */ +#define HP100_PKT_LEN_MASK 0x1FFF /* AND with RxLength to get length */ -#define HP100_PKT_LEN_MASK 0x1FFF /* AND with RxLength to get length bits */ /* Receive Packet Status. Note, the error bits are only valid if ACC_ERRORED bit in the MAC Configuration Register 1 is set. */ - #define HP100_RX_PRI 0x8000 /* 0:No, 1:Yes packet is priority */ #define HP100_SDF_ERR 0x4000 /* 0:No, 1:Yes start of frame error */ #define HP100_SKEW_ERR 0x2000 /* 0:No, 1:Yes skew out of range */ @@ -368,7 +616,11 @@ outw( HP100_MMAP_DIS | HP100_RESET_HB, ioaddr + HP100_REG_OPTION_LSW ) #define hp100_mem_map_disable() \ outw( HP100_MMAP_DIS | HP100_SET_HB, ioaddr + HP100_REG_OPTION_LSW ) -#define hp100_reset_card() \ - outw( HP100_HW_RST | HP100_RESET_LB, ioaddr + HP100_REG_OPTION_LSW ) -#define hp100_unreset_card() \ - outw( HP100_HW_RST | HP100_SET_LB, ioaddr + HP100_REG_OPTION_LSW ) + + +/* + * Local variables: + * c-indent-level: 2 + * tab-width: 8 + * End: +*/ diff -u --recursive --new-file v2.1.39/linux/drivers/net/ibmtr.c linux/drivers/net/ibmtr.c --- v2.1.39/linux/drivers/net/ibmtr.c Tue May 13 22:41:09 1997 +++ linux/drivers/net/ibmtr.c Sun May 18 16:38:34 1997 @@ -84,7 +84,7 @@ /* version and credits */ static char *version = "ibmtr.c: v1.3.57 8/ 7/94 Peter De Schrijver and Mark Swanson\n" -" v2.1.29 3/15/97 Paul Norton \n"; +" v2.1.35 5/ 1/97 Paul Norton \n"; static char pcchannelid[] = { 0x05, 0x00, 0x04, 0x09, @@ -1013,14 +1013,15 @@ } /* ARB response */ if (status & SSB_RESP_INT) { /* SSB response */ - + unsigned char retcode; switch (readb(ti->ssb)) { /* SSB command check */ - + case XMIT_DIR_FRAME: case XMIT_UI_FRAME: - if (readb(ti->ssb+2)) /* checks ret_code */ + retcode = readb(ti->ssb+2); + if (retcode && (retcode != 0x22)) /* checks ret_code */ DPRINTK("xmit ret_code: %02X xmit error code: %02X\n", - (int)readb(ti->ssb+2), (int)readb(ti->ssb+6)); + (int)retcode, (int)readb(ti->ssb+6)); else ti->tr_stats.tx_packets++; break; @@ -1321,12 +1322,12 @@ __u32 rbuffer, rbufdata; __u32 llc; unsigned char *data; - unsigned int rbuffer_len, lan_hdr_len, hdr_len; - unsigned int arb_frame_len; + unsigned int rbuffer_len, lan_hdr_len, hdr_len, ip_len, length; struct sk_buff *skb; unsigned int skb_size = 0; int is8022 = 0; unsigned int chksum = 0; + struct iphdr *iph; rbuffer=(ti->sram +ntohs(readw(ti->arb + offsetof(struct arb_rec_req, rec_buf_addr))))+2; @@ -1400,8 +1401,8 @@ } #endif - arb_frame_len=ntohs(readw(ti->arb+offsetof(struct arb_rec_req, frame_len))); - skb_size = arb_frame_len-lan_hdr_len+sizeof(struct trh_hdr); + length = ntohs(readw(ti->arb+offsetof(struct arb_rec_req, frame_len))); + skb_size = length-lan_hdr_len+sizeof(struct trh_hdr); if (is8022) { skb_size += sizeof(struct trllc); } @@ -1429,30 +1430,42 @@ rbuffer_len=ntohs(readw(rbuffer + offsetof(struct rec_buf, buf_len)))-lan_hdr_len; if (is8022) { - /* create whitewashed LLC header in skb buffer (why not the real one?) */ + /* create whitewashed LLC header in sk buffer */ struct trllc *local_llc = (struct trllc *)data; memset(local_llc, 0, sizeof(*local_llc)); local_llc->ethertype = htons(ETH_P_TR_802_2); hdr_len = sizeof(struct trllc); + /* copy the real LLC header to the sk buffer */ + data += hdr_len; + memcpy_fromio(data, rbuffer+offsetof(struct rec_buf, data)+lan_hdr_len,hdr_len); } else { /* Copy the LLC header and the IPv4 header */ hdr_len = sizeof(struct trllc) + sizeof(struct iphdr); memcpy_fromio(data, rbuffer+offsetof(struct rec_buf, data)+lan_hdr_len,hdr_len); + + /* Watch for padded packets and bogons */ + iph=(struct iphdr*)(data+sizeof(struct trllc)); + ip_len = ntohs(iph->tot_len) - sizeof(struct iphdr); + length -= lan_hdr_len + hdr_len; + if ((ip_len <= length) && (ip_len > 7)) + length = ip_len; } data += hdr_len; - lan_hdr_len += hdr_len; rbuffer_len -= hdr_len; - rbufdata = rbuffer + offsetof(struct rec_buf,data) + lan_hdr_len; + rbufdata = rbuffer + offsetof(struct rec_buf,data) + lan_hdr_len + hdr_len; /* Copy the payload... */ for (;;) { if (is8022) memcpy_fromio(data, rbufdata, rbuffer_len); else - chksum = csum_partial_copy(bus_to_virt(rbufdata), data, rbuffer_len, chksum); + chksum = csum_partial_copy(bus_to_virt(rbufdata), data, + length < rbuffer_len ? length : rbuffer_len, + chksum); rbuffer = ntohs(readw(rbuffer)); if (!rbuffer) break; + length -= rbuffer_len; data += rbuffer_len; rbuffer += ti->sram; rbuffer_len = ntohs(readw(rbuffer + offsetof(struct rec_buf, buf_len))); diff -u --recursive --new-file v2.1.39/linux/drivers/pci/pci.c linux/drivers/pci/pci.c --- v2.1.39/linux/drivers/pci/pci.c Tue May 13 22:41:12 1997 +++ linux/drivers/pci/pci.c Sun May 18 16:38:34 1997 @@ -150,6 +150,7 @@ DEVICE( CMD, CMD_646, "646"), DEVICE( VISION, VISION_QD8500, "QD-8500"), DEVICE( VISION, VISION_QD8580, "QD-8580"), + DEVICE( BROOKTREE, BT848, "Brooktree 848"), DEVICE( SIERRA, SIERRA_STB, "STB Horizon 64"), DEVICE( ACC, ACC_2056, "2056"), DEVICE( WINBOND, WINBOND_83769, "W83769F"), @@ -264,6 +265,7 @@ DEVICE( INTEL, INTEL_82437VX, "82437VX Triton II"), DEVICE( INTEL, INTEL_82371AB, "82371AB 430TX PIIX4"), DEVICE( INTEL, INTEL_P6, "Orion P6"), + DEVICE( INTEL, INTEL_P6_2, "82450GX Orion P6"), DEVICE( KTI, KTI_ET32P2, "ET32P2"), DEVICE( ADAPTEC, ADAPTEC_7850, "AIC-7850"), DEVICE( ADAPTEC, ADAPTEC_7855, "AIC-7855"), @@ -518,6 +520,7 @@ case PCI_VENDOR_ID_OLICOM: return "Olicom"; case PCI_VENDOR_ID_CMD: return "CMD"; case PCI_VENDOR_ID_VISION: return "Vision"; + case PCI_VENDOR_ID_BROOKTREE: return "Brooktree"; case PCI_VENDOR_ID_SIERRA: return "Sierra"; case PCI_VENDOR_ID_ACC: return "ACC MICROELECTRONICS"; case PCI_VENDOR_ID_WINBOND: return "Winbond"; diff -u --recursive --new-file v2.1.39/linux/drivers/scsi/53c7xx.c linux/drivers/scsi/53c7xx.c --- v2.1.39/linux/drivers/scsi/53c7xx.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/53c7xx.c Sun May 18 17:10:37 1997 @@ -0,0 +1,6107 @@ +/* + * 53c710 driver. Modified from Drew Eckhardts driver + * for 53c810 by Richard Hirst [richard@sleepie.demon.co.uk] + * Check out PERM_OPTIONS and EXPECTED_CLOCK, which may be defined in the + * relevant machine specific file (eg. mvme166.[ch], amiga7xx.[ch]). + * There are also currently some defines at the top of 53c7xx.scr. + * The chip type is #defined in script_asm.pl, as well as the Makefile. + * Host scsi ID expected to be 7 - see NCR53c7x0_init(). + * + * I have removed the PCI code and some of the 53c8xx specific code - + * simply to make this file smaller and easier to manage. + * + * MVME166 issues: + * Problems trying to read any chip registers in NCR53c7x0_init(), as they + * may never have been set by 166Bug (eg. If kernel has come in over tftp). + */ + +/* + * Adapted for Linux/m68k Amiga platforms for the A4000T/A4091 and + * WarpEngine SCSI controllers. + * By Alan Hourihane + * Thanks to Richard Hirst for making it possible with the MVME additions + */ + +/* + * 53c710 rev 0 doesn't support add with carry. Rev 1 and 2 does. To + * overcome this problem you can define FORCE_DSA_ALIGNMENT, which ensures + * that the DSA address is always xxxxxx00. If disconnection is not allowed, + * then the script only ever tries to add small (< 256) positive offsets to + * DSA, so lack of carry isn't a problem. FORCE_DSA_ALIGNMENT can, of course, + * be defined for all chip revisions at a small cost in memory usage. + */ + +#define FORCE_DSA_ALIGNMENT + +/* + * Selection timer does not always work on the 53c710, depending on the + * timing at the last disconnect, if this is a problem for you, try + * using validids as detailed below. + * + * Options for the NCR7xx driver + * + * nosync:0 - disables synchronous negotiation + * nodisconnect:0 - disables disconnection + * validids:0x?? - Bitmask field that disallows certain ID's. + * - e.g. 0x03 allows ID 0,1 + * - 0x1F allows ID 0,1,2,3,4 + */ + +/* + * PERM_OPTIONS are driver options which will be enabled for all NCR boards + * in the system at driver initialization time. + * + * Don't THINK about touching these in PERM_OPTIONS : + * OPTION_MEMORY_MAPPED + * 680x0 doesn't have an IO map! + * + * OPTION_DEBUG_TEST1 + * Test 1 does bus mastering and interrupt tests, which will help weed + * out brain damaged main boards. + * + * Other PERM_OPTIONS settings are listed below. Note the actual options + * required are set in the relevant file (mvme166.c, amiga7xx.c, etc): + * + * OPTION_NO_ASYNC + * Don't negotiate for asynchronous transfers on the first command + * when OPTION_ALWAYS_SYNCHRONOUS is set. Useful for dain bramaged + * devices which do something bad rather than sending a MESSAGE + * REJECT back to us like they should if they can't cope. + * + * OPTION_SYNCHRONOUS + * Enable support for synchronous transfers. Target negotiated + * synchronous transfers will be responded to. To initiate + * a synchronous transfer request, call + * + * request_synchronous (hostno, target) + * + * from within KGDB. + * + * OPTION_ALWAYS_SYNCHRONOUS + * Negotiate for synchronous transfers with every target after + * driver initialization or a SCSI bus reset. This is a bit dangerous, + * since there are some dain bramaged SCSI devices which will accept + * SDTR messages but keep talking asynchronously. + * + * OPTION_DISCONNECT + * Enable support for disconnect/reconnect. To change the + * default setting on a given host adapter, call + * + * request_disconnect (hostno, allow) + * + * where allow is non-zero to allow, 0 to disallow. + * + * If you really want to run 10MHz FAST SCSI-II transfers, you should + * know that the NCR driver currently ignores parity information. Most + * systems do 5MHz SCSI fine. I've seen a lot that have problems faster + * than 8MHz. To play it safe, we only request 5MHz transfers. + * + * If you'd rather get 10MHz transfers, edit sdtr_message and change + * the fourth byte from 50 to 25. + */ + +#include + +/* + * Sponsored by + * iX Multiuser Multitasking Magazine + * Hannover, Germany + * hm@ix.de + * + * Copyright 1993, 1994, 1995 Drew Eckhardt + * Visionary Computing + * (Unix and Linux consulting and custom programming) + * drew@PoohSticks.ORG + * +1 (303) 786-7975 + * + * TolerANT and SCSI SCRIPTS are registered trademarks of NCR Corporation. + * + * For more information, please consult + * + * NCR53C810 + * SCSI I/O Processor + * Programmer's Guide + * + * NCR 53C810 + * PCI-SCSI I/O Processor + * Data Manual + * + * NCR 53C810/53C820 + * PCI-SCSI I/O Processor Design In Guide + * + * For literature on Symbios Logic Inc. formerly NCR, SCSI, + * and Communication products please call (800) 334-5454 or + * (719) 536-3300. + * + * PCI BIOS Specification Revision + * PCI Local Bus Specification + * PCI System Design Guide + * + * PCI Special Interest Group + * M/S HF3-15A + * 5200 N.E. Elam Young Parkway + * Hillsboro, Oregon 97124-6497 + * +1 (503) 696-2000 + * +1 (800) 433-5177 + */ + +/* + * Design issues : + * The cumulative latency needed to propagate a read/write request + * through the file system, buffer cache, driver stacks, SCSI host, and + * SCSI device is ultimately the limiting factor in throughput once we + * have a sufficiently fast host adapter. + * + * So, to maximize performance we want to keep the ratio of latency to data + * transfer time to a minimum by + * 1. Minimizing the total number of commands sent (typical command latency + * including drive and bus mastering host overhead is as high as 4.5ms) + * to transfer a given amount of data. + * + * This is accomplished by placing no arbitrary limit on the number + * of scatter/gather buffers supported, since we can transfer 1K + * per scatter/gather buffer without Eric's cluster patches, + * 4K with. + * + * 2. Minimizing the number of fatal interrupts serviced, since + * fatal interrupts halt the SCSI I/O processor. Basically, + * this means offloading the practical maximum amount of processing + * to the SCSI chip. + * + * On the NCR53c810/820/720, this is accomplished by using + * interrupt-on-the-fly signals when commands complete, + * and only handling fatal errors and SDTR / WDTR messages + * in the host code. + * + * On the NCR53c710, interrupts are generated as on the NCR53c8x0, + * only the lack of a interrupt-on-the-fly facility complicates + * things. Also, SCSI ID registers and commands are + * bit fielded rather than binary encoded. + * + * On the NCR53c700 and NCR53c700-66, operations that are done via + * indirect, table mode on the more advanced chips must be + * replaced by calls through a jump table which + * acts as a surrogate for the DSA. Unfortunately, this + * will mean that we must service an interrupt for each + * disconnect/reconnect. + * + * 3. Eliminating latency by pipelining operations at the different levels. + * + * This driver allows a configurable number of commands to be enqueued + * for each target/lun combination (experimentally, I have discovered + * that two seems to work best) and will ultimately allow for + * SCSI-II tagged queuing. + * + * + * Architecture : + * This driver is built around a Linux queue of commands waiting to + * be executed, and a shared Linux/NCR array of commands to start. Commands + * are transfered to the array by the run_process_issue_queue() function + * which is called whenever a command completes. + * + * As commands are completed, the interrupt routine is triggered, + * looks for commands in the linked list of completed commands with + * valid status, removes these commands from a list of running commands, + * calls the done routine, and flags their target/luns as not busy. + * + * Due to limitations in the intelligence of the NCR chips, certain + * concessions are made. In many cases, it is easier to dynamically + * generate/fix-up code rather than calculate on the NCR at run time. + * So, code is generated or fixed up for + * + * - Handling data transfers, using a variable number of MOVE instructions + * interspersed with CALL MSG_IN, WHEN MSGIN instructions. + * + * The DATAIN and DATAOUT routines are separate, so that an incorrect + * direction can be trapped, and space isn't wasted. + * + * It may turn out that we're better off using some sort + * of table indirect instruction in a loop with a variable + * sized table on the NCR53c710 and newer chips. + * + * - Checking for reselection (NCR53c710 and better) + * + * - Handling the details of SCSI context switches (NCR53c710 and better), + * such as reprogramming appropriate synchronous parameters, + * removing the dsa structure from the NCR's queue of outstanding + * commands, etc. + * + */ + +#ifdef MODULE +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_AMIGA +#include +#include +#include +#include + +#define BIG_ENDIAN +#define NO_IO_SPACE +#endif + +#ifdef CONFIG_MVME166 +#include + +#define BIG_ENDIAN +#define NO_IO_SPACE +#endif + +#include "scsi.h" +#include "hosts.h" +#include "53c7xx.h" +#include "constants.h" +#include "sd.h" +#include +#include + +#ifdef NO_IO_SPACE +/* + * The following make the definitions in 53c7xx.h (write8, etc) smaller, + * we don't have seperate i/o space anyway. + */ +#undef inb +#undef outb +#define inb(x) 1 +#define inw(x) 1 +#define inl(x) 1 +#define outb(x,y) 1 +#define outw(x,y) 1 +#define outl(x,y) 1 +#endif + +static int check_address (unsigned long addr, int size); +static void dump_events (struct Scsi_Host *host, int count); +static Scsi_Cmnd * return_outstanding_commands (struct Scsi_Host *host, + int free, int issue); +static void hard_reset (struct Scsi_Host *host); +static void ncr_scsi_reset (struct Scsi_Host *host); +static void print_lots (struct Scsi_Host *host); +static void set_synchronous (struct Scsi_Host *host, int target, int sxfer, + int scntl3, int now_connected); +static int datapath_residual (struct Scsi_Host *host); +static const char * sbcl_to_phase (int sbcl); +static void print_progress (Scsi_Cmnd *cmd); +static void print_queues (struct Scsi_Host *host); +static void process_issue_queue (unsigned long flags); +static int shutdown (struct Scsi_Host *host); +static void abnormal_finished (struct NCR53c7x0_cmd *cmd, int result); +static int disable (struct Scsi_Host *host); +static int NCR53c7xx_run_tests (struct Scsi_Host *host); +void NCR53c7x0_intr(int irq, void *dev_id, struct pt_regs * regs); +static int ncr_halt (struct Scsi_Host *host); +static void intr_phase_mismatch (struct Scsi_Host *host, struct NCR53c7x0_cmd + *cmd); +static void intr_dma (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd); +static void print_dsa (struct Scsi_Host *host, u32 *dsa, + const char *prefix); +static int print_insn (struct Scsi_Host *host, const u32 *insn, + const char *prefix, int kernel); + +static void NCR53c7xx_dsa_fixup (struct NCR53c7x0_cmd *cmd); +static void NCR53c7x0_init_fixup (struct Scsi_Host *host); +static int NCR53c7x0_dstat_sir_intr (struct Scsi_Host *host, struct + NCR53c7x0_cmd *cmd); +static void NCR53c7x0_soft_reset (struct Scsi_Host *host); + +/* Size of event list (per host adapter) */ +static int track_events = 0; +static struct Scsi_Host *first_host = NULL; /* Head of list of NCR boards */ +static Scsi_Host_Template *the_template = NULL; + +/* NCR53c710 script handling code */ + +#include "53c7xx_d.h" +#ifdef A_int_debug_sync +#define DEBUG_SYNC_INTR A_int_debug_sync +#endif +int NCR53c7xx_script_len = sizeof (SCRIPT); +int NCR53c7xx_dsa_len = A_dsa_end + Ent_dsa_zero - Ent_dsa_code_template; +#ifdef FORCE_DSA_ALIGNMENT +int CmdPageStart = (0 - Ent_dsa_zero - sizeof(struct NCR53c7x0_cmd)) & 0xff; +#endif +int flushsize; + +static char *setup_strings[] = + {"","","","","","","",""}; + +#define MAX_SETUP_STRINGS (sizeof(setup_strings) / sizeof(char *)) +#define SETUP_BUFFER_SIZE 200 +static char setup_buffer[SETUP_BUFFER_SIZE]; +static char setup_used[MAX_SETUP_STRINGS]; + +void ncr53c7xx_setup (char *str, int *ints) +{ + int i; + char *p1, *p2; + + p1 = setup_buffer; + *p1 = '\0'; + if (str) + strncpy(p1, str, SETUP_BUFFER_SIZE - strlen(setup_buffer)); + setup_buffer[SETUP_BUFFER_SIZE - 1] = '\0'; + p1 = setup_buffer; + i = 0; + while (*p1 && (i < MAX_SETUP_STRINGS)) { + p2 = strchr(p1, ','); + if (p2) { + *p2 = '\0'; + if (p1 != p2) + setup_strings[i] = p1; + p1 = p2 + 1; + i++; + } + else { + setup_strings[i] = p1; + break; + } + } + for (i=0; i= '0') && (*cp <= '9')) { + *val = simple_strtoul(cp,NULL,0); + } + return ++x; +} + + + +/* + * KNOWN BUGS : + * - There is some sort of conflict when the PPP driver is compiled with + * support for 16 channels? + * + * - On systems which predate the 1.3.x initialization order change, + * the NCR driver will cause Cannot get free page messages to appear. + * These are harmless, but I don't know of an easy way to avoid them. + * + * - With OPTION_DISCONNECT, on two systems under unknown circumstances, + * we get a PHASE MISMATCH with DSA set to zero (suggests that we + * are occurring somewhere in the reselection code) where + * DSP=some value DCMD|DBC=same value. + * + * Closer inspection suggests that we may be trying to execute + * some portion of the DSA? + * scsi0 : handling residual transfer (+ 0 bytes from DMA FIFO) + * scsi0 : handling residual transfer (+ 0 bytes from DMA FIFO) + * scsi0 : no current command : unexpected phase MSGIN. + * DSP=0x1c46cc, DCMD|DBC=0x1c46ac, DSA=0x0 + * DSPS=0x0, TEMP=0x1c3e70, DMODE=0x80 + * scsi0 : DSP-> + * 001c46cc : 0x001c46cc 0x00000000 + * 001c46d4 : 0x001c5ea0 0x000011f8 + * + * Changed the print code in the phase_mismatch handler so + * that we call print_lots to try to diagnose this. + * + */ + +/* + * Possible future direction of architecture for max performance : + * + * We're using a single start array for the NCR chip. This is + * sub-optimal, because we cannot add a command which would conflict with + * an executing command to this start queue, and therefore must insert the + * next command for a given I/T/L combination after the first has completed; + * incurring our interrupt latency between SCSI commands. + * + * To allow further pipelining of the NCR and host CPU operation, we want + * to set things up so that immediately on termination of a command destined + * for a given LUN, we get that LUN busy again. + * + * To do this, we need to add a 32 bit pointer to which is jumped to + * on completion of a command. If no new command is available, this + * would point to the usual DSA issue queue select routine. + * + * If one were, it would point to a per-NCR53c7x0_cmd select routine + * which starts execution immediately, inserting the command at the head + * of the start queue if the NCR chip is selected or reselected. + * + * We would change so that we keep a list of outstanding commands + * for each unit, rather than a single running_list. We'd insert + * a new command into the right running list; if the NCR didn't + * have something running for that yet, we'd put it in the + * start queue as well. Some magic needs to happen to handle the + * race condition between the first command terminating before the + * new one is written. + * + * Potential for profiling : + * Call do_gettimeofday(struct timeval *tv) to get 800ns resolution. + */ + + +/* + * TODO : + * 1. To support WIDE transfers, not much needs to happen. We + * should do CHMOVE instructions instead of MOVEs when + * we have scatter/gather segments of uneven length. When + * we do this, we need to handle the case where we disconnect + * between segments. + * + * 2. Currently, when Icky things happen we do a FATAL(). Instead, + * we want to do an integrity check on the parts of the NCR hostdata + * structure which were initialized at boot time; FATAL() if that + * fails, and otherwise try to recover. Keep track of how many + * times this has happened within a single SCSI command; if it + * gets excessive, then FATAL(). + * + * 3. Parity checking is currently disabled, and a few things should + * happen here now that we support synchronous SCSI transfers : + * 1. On soft-reset, we shoould set the EPC (Enable Parity Checking) + * and AAP (Assert SATN/ on parity error) bits in SCNTL0. + * + * 2. We should enable the parity interrupt in the SIEN0 register. + * + * 3. intr_phase_mismatch() needs to believe that message out is + * always an "acceptable" phase to have a mismatch in. If + * the old phase was MSG_IN, we should send a MESSAGE PARITY + * error. If the old phase was something else, we should send + * a INITIATOR_DETECTED_ERROR message. Note that this could + * cause a RESTORE POINTERS message; so we should handle that + * correctly first. Instead, we should probably do an + * initiator_abort. + * + * 4. MPEE bit of CTEST4 should be set so we get interrupted if + * we detect an error. + * + * + * 5. The initial code has been tested on the NCR53c810. I don't + * have access to NCR53c700, 700-66 (Forex boards), NCR53c710 + * (NCR Pentium systems), NCR53c720, NCR53c820, or NCR53c825 boards to + * finish development on those platforms. + * + * NCR53c820/825/720 - need to add wide transfer support, including WDTR + * negotiation, programming of wide transfer capabilities + * on reselection and table indirect selection. + * + * NCR53c710 - need to add fatal interrupt or GEN code for + * command completion signaling. Need to modify all + * SDID, SCID, etc. registers, and table indirect select code + * since these use bit fielded (ie 1<NOP_insn) ? + /* + * If the IF TRUE bit is set, it's a JUMP instruction. The + * operand is a bus pointer to the dsa_begin routine for this DSA. The + * dsa field of the NCR53c7x0_cmd structure starts with the + * DSA code template. By converting to a virtual address, + * subtracting the code template size, and offset of the + * dsa field, we end up with a pointer to the start of the + * structure (alternatively, we could use the + * dsa_cmnd field, an anachronism from when we weren't + * sure what the relationship between the NCR structures + * and host structures were going to be. + */ + (struct NCR53c7x0_cmd *) ((char *) bus_to_virt (issue[1]) - + (hostdata->E_dsa_code_begin - hostdata->E_dsa_code_template) - + offsetof(struct NCR53c7x0_cmd, dsa)) + /* If the IF TRUE bit is not set, it's a NOP */ + : NULL; +} + + +/* + * FIXME: we should junk these, in favor of synchronous_want and + * wide_want in the NCR53c7x0_hostdata structure. + */ + +/* Template for "preferred" synchronous transfer parameters. */ + +static const unsigned char sdtr_message[] = { +#ifdef CONFIG_SCSI_NCR53C7xx_FAST + EXTENDED_MESSAGE, 3 /* length */, EXTENDED_SDTR, 25 /* *4ns */, 8 /* off */ +#else + EXTENDED_MESSAGE, 3 /* length */, EXTENDED_SDTR, 50 /* *4ns */, 8 /* off */ +#endif +}; + +/* Template to request asynchronous transfers */ + +static const unsigned char async_message[] = { + EXTENDED_MESSAGE, 3 /* length */, EXTENDED_SDTR, 0, 0 /* asynchronous */ +}; + +/* Template for "preferred" WIDE transfer parameters */ + +static const unsigned char wdtr_message[] = { + EXTENDED_MESSAGE, 2 /* length */, EXTENDED_WDTR, 1 /* 2^1 bytes */ +}; + +/* + * Function : struct Scsi_Host *find_host (int host) + * + * Purpose : KGDB support function which translates a host number + * to a host structure. + * + * Inputs : host - number of SCSI host + * + * Returns : NULL on failure, pointer to host structure on success. + */ + +static struct Scsi_Host * +find_host (int host) { + struct Scsi_Host *h; + for (h = first_host; h && h->host_no != host; h = h->next); + if (!h) { + printk (KERN_ALERT "scsi%d not found\n", host); + return NULL; + } else if (h->hostt != the_template) { + printk (KERN_ALERT "scsi%d is not a NCR board\n", host); + return NULL; + } + return h; +} + +/* + * Function : request_synchronous (int host, int target) + * + * Purpose : KGDB interface which will allow us to negotiate for + * synchronous transfers. This ill be replaced with a more + * integrated function; perhaps a new entry in the scsi_host + * structure, accessible via an ioctl() or perhaps /proc/scsi. + * + * Inputs : host - number of SCSI host; target - number of target. + * + * Returns : 0 when negotiation has been setup for next SCSI command, + * -1 on failure. + */ + +static int +request_synchronous (int host, int target) { + struct Scsi_Host *h; + struct NCR53c7x0_hostdata *hostdata; + unsigned long flags; + if (target < 0) { + printk (KERN_ALERT "target %d is bogus\n", target); + return -1; + } + if (!(h = find_host (host))) + return -1; + else if (h->this_id == target) { + printk (KERN_ALERT "target %d is host ID\n", target); + return -1; + } + else if (target > h->max_id) { + printk (KERN_ALERT "target %d exceeds maximum of %d\n", target, + h->max_id); + return -1; + } + hostdata = (struct NCR53c7x0_hostdata *)h->hostdata; + + save_flags(flags); + cli(); + if (hostdata->initiate_sdtr & (1 << target)) { + restore_flags(flags); + printk (KERN_ALERT "target %d already doing SDTR\n", target); + return -1; + } + hostdata->initiate_sdtr |= (1 << target); + restore_flags(flags); + return 0; +} + +/* + * Function : request_disconnect (int host, int on_or_off) + * + * Purpose : KGDB support function, tells us to allow or disallow + * disconnections. + * + * Inputs : host - number of SCSI host; on_or_off - non-zero to allow, + * zero to disallow. + * + * Returns : 0 on success, * -1 on failure. + */ + +static int +request_disconnect (int host, int on_or_off) { + struct Scsi_Host *h; + struct NCR53c7x0_hostdata *hostdata; + if (!(h = find_host (host))) + return -1; + hostdata = (struct NCR53c7x0_hostdata *) h->hostdata; + if (on_or_off) + hostdata->options |= OPTION_DISCONNECT; + else + hostdata->options &= ~OPTION_DISCONNECT; + return 0; +} + +/* + * Function : static void NCR53c7x0_driver_init (struct Scsi_Host *host) + * + * Purpose : Initialize internal structures, as required on startup, or + * after a SCSI bus reset. + * + * Inputs : host - pointer to this host adapter's structure + */ + +static void +NCR53c7x0_driver_init (struct Scsi_Host *host) { + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + int i, j; + u32 *ncrcurrent; + +flush_cache_all(); +cache_push(virt_to_bus(hostdata->script), flushsize); +cache_clear(virt_to_bus(hostdata->script), flushsize); + + for (i = 0; i < 16; ++i) { + hostdata->request_sense[i] = 0; + for (j = 0; j < 8; ++j) + hostdata->busy[i][j] = 0; + set_synchronous (host, i, /* sxfer */ 0, hostdata->saved_scntl3, 0); + } + hostdata->issue_queue = NULL; + hostdata->running_list = hostdata->finished_queue = + hostdata->ncrcurrent = NULL; + for (i = 0, ncrcurrent = (u32 *) hostdata->schedule; + i < host->can_queue; ++i, ncrcurrent += 2) { + ncrcurrent[0] = hostdata->NOP_insn; + ncrcurrent[1] = 0xdeadbeef; + } + ncrcurrent[0] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24) | DBC_TCI_TRUE; + ncrcurrent[1] = (u32) virt_to_bus (hostdata->script) + + hostdata->E_wait_reselect; + hostdata->reconnect_dsa_head = 0; + hostdata->addr_reconnect_dsa_head = (u32) + virt_to_bus((void *) &(hostdata->reconnect_dsa_head)); + hostdata->expecting_iid = 0; + hostdata->expecting_sto = 0; + if (hostdata->options & OPTION_ALWAYS_SYNCHRONOUS) + hostdata->initiate_sdtr = 0xffff; + else + hostdata->initiate_sdtr = 0; + hostdata->talked_to = 0; + hostdata->idle = 1; +flush_cache_all(); +cache_push(virt_to_bus(hostdata->script), flushsize); +cache_clear(virt_to_bus(hostdata->script), flushsize); +} + +/* + * Function : static int clock_to_ccf_710 (int clock) + * + * Purpose : Return the clock conversion factor for a given SCSI clock. + * + * Inputs : clock - SCSI clock expressed in Hz. + * + * Returns : ccf on success, -1 on failure. + */ + +static int +clock_to_ccf_710 (int clock) { + if (clock <= 16666666) + return -1; + if (clock <= 25000000) + return 2; /* Divide by 1.0 */ + else if (clock <= 37500000) + return 1; /* Divide by 1.5 */ + else if (clock <= 50000000) + return 0; /* Divide by 2.0 */ + else if (clock <= 66000000) + return 3; /* Divide by 3.0 */ + else + return -1; +} + +/* + * Function : static int NCR53c7x0_init (struct Scsi_Host *host) + * + * Purpose : initialize the internal structures for a given SCSI host + * + * Inputs : host - pointer to this host adapter's structure + * + * Preconditions : when this function is called, the chip_type + * field of the hostdata structure MUST have been set. + * + * Returns : 0 on success, -1 on failure. + */ + +int +NCR53c7x0_init (struct Scsi_Host *host) { + NCR53c7x0_local_declare(); + int i, ccf; + unsigned char revision; + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + struct Scsi_Host *search; + /* + * There are some things which we need to know about in order to provide + * a semblance of support. Print 'em if they aren't what we expect, + * otherwise don't add to the noise. + * + * -1 means we don't know what to expect. + */ + int val, flags; + char buf[32]; + int expected_id = -1; + int expected_clock = -1; + int uninitialized = 0; +#ifdef NO_IO_SPACE + int expected_mapping = OPTION_MEMORY_MAPPED; +#else + int expected_mapping = OPTION_IO_MAPPED; +#endif + for (i=0;i<7;i++) + hostdata->valid_ids[i] = 1; /* Default all ID's to scan */ + + /* Parse commandline flags */ + if (check_setup_strings("nosync",&flags,&val,buf)) + { + hostdata->options |= OPTION_NO_ASYNC; + hostdata->options &= ~(OPTION_SYNCHRONOUS | OPTION_ALWAYS_SYNCHRONOUS); + } + + if (check_setup_strings("nodisconnect",&flags,&val,buf)) + hostdata->options &= ~OPTION_DISCONNECT; + + if (check_setup_strings("validids",&flags,&val,buf)) + { + for (i=0;i<7;i++) + hostdata->valid_ids[i] = val & (1<chip) { + case 710: + hostdata->dstat_sir_intr = NCR53c7x0_dstat_sir_intr; + hostdata->init_save_regs = NULL; + hostdata->dsa_fixup = NCR53c7xx_dsa_fixup; + hostdata->init_fixup = NCR53c7x0_init_fixup; + hostdata->soft_reset = NCR53c7x0_soft_reset; + hostdata->run_tests = NCR53c7xx_run_tests; + expected_clock = hostdata->scsi_clock = 50000000; + expected_id = 7; + break; + default: + printk ("scsi%d : chip type of %d is not supported yet, detaching.\n", + host->host_no, hostdata->chip); + scsi_unregister (host); + return -1; + } + + /* Assign constants accessed by NCR */ + hostdata->NCR53c7xx_zero = 0; + hostdata->NCR53c7xx_msg_reject = MESSAGE_REJECT; + hostdata->NCR53c7xx_msg_abort = ABORT; + hostdata->NCR53c7xx_msg_nop = NOP; + hostdata->NOP_insn = (DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24; + + if (expected_mapping == -1 || + (hostdata->options & (OPTION_MEMORY_MAPPED)) != + (expected_mapping & OPTION_MEMORY_MAPPED)) + printk ("scsi%d : using %s mapped access\n", host->host_no, + (hostdata->options & OPTION_MEMORY_MAPPED) ? "memory" : + "io"); + + hostdata->dmode = (hostdata->chip == 700 || hostdata->chip == 70066) ? + DMODE_REG_00 : DMODE_REG_10; + hostdata->istat = ((hostdata->chip / 100) == 8) ? + ISTAT_REG_800 : ISTAT_REG_700; + +/* Only the ISTAT register is readable when the NCR is running, so make + sure it's halted. */ + ncr_halt(host); + +/* + * XXX - the NCR53c700 uses bitfielded registers for SCID, SDID, etc, + * as does the 710 with one bit per SCSI ID. Conversely, the NCR + * uses a normal, 3 bit binary representation of these values. + * + * Get the rest of the NCR documentation, and FIND OUT where the change + * was. + */ + +#if 0 + /* May not be able to do this - chip my not have been set up yet */ + tmp = hostdata->this_id_mask = NCR53c7x0_read8(SCID_REG); + for (host->this_id = 0; tmp != 1; tmp >>=1, ++host->this_id); +#else + host->this_id = 7; +#endif + +/* + * Note : we should never encounter a board setup for ID0. So, + * if we see ID0, assume that it was uninitialized and set it + * to the industry standard 7. + */ + if (!host->this_id) { + printk("scsi%d : initiator ID was %d, changing to 7\n", + host->host_no, host->this_id); + host->this_id = 7; + hostdata->this_id_mask = 1 << 7; + uninitialized = 1; + }; + + if (expected_id == -1 || host->this_id != expected_id) + printk("scsi%d : using initiator ID %d\n", host->host_no, + host->this_id); + + /* + * Save important registers to allow a soft reset. + */ + + /* + * CTEST7 controls cache snooping, burst mode, and support for + * external differential drivers. This isn't currently used - the + * default value may not be optimal anyway. + * Even worse, it may never have been set up since reset. + */ + hostdata->saved_ctest7 = NCR53c7x0_read8(CTEST7_REG) & CTEST7_SAVE; + revision = (NCR53c7x0_read8(CTEST8_REG) & 0xF0) >> 4; + switch (revision) { + case 1: + revision = 0; + break; + case 2: + revision = 1; + break; + case 4: + revision = 2; + break; + case 8: + revision = 3; + break; + default: + revision = 255; + break; + } + printk("scsi%d: Revision 0x%x\n",host->host_no,revision); + + /* + * On NCR53c700 series chips, DCNTL controls the SCSI clock divisor, + * on 800 series chips, it allows for a totem-pole IRQ driver. + * NOTE saved_dcntl currently overwritten in init function. + * The value read here may be garbage anyway, MVME166 board at least + * does not initialise chip if kernel arrived via tftp. + */ + + hostdata->saved_dcntl = NCR53c7x0_read8(DCNTL_REG); + + /* + * DMODE controls DMA burst length, and on 700 series chips, + * 286 mode and bus width + * NOTE: On MVME166, chip may have been reset, so this could be a + * power-on/reset default value. + */ + hostdata->saved_dmode = NCR53c7x0_read8(hostdata->dmode); + + /* + * Now that burst length and enabled/disabled status is known, + * clue the user in on it. + */ + + ccf = clock_to_ccf_710 (expected_clock); + + for (i = 0; i < 16; ++i) + hostdata->cmd_allocated[i] = 0; + + if (hostdata->init_save_regs) + hostdata->init_save_regs (host); + if (hostdata->init_fixup) + hostdata->init_fixup (host); + + if (!the_template) { + the_template = host->hostt; + first_host = host; + } + + /* + * Linux SCSI drivers have always been plagued with initialization + * problems - some didn't work with the BIOS disabled since they expected + * initialization from it, some didn't work when the networking code + * was enabled and registers got scrambled, etc. + * + * To avoid problems like this, in the future, we will do a soft + * reset on the SCSI chip, taking it back to a sane state. + */ + + hostdata->soft_reset (host); + +#if 1 + hostdata->debug_count_limit = -1; +#else + hostdata->debug_count_limit = 1; +#endif + hostdata->intrs = -1; + hostdata->resets = -1; + memcpy ((void *) hostdata->synchronous_want, (void *) sdtr_message, + sizeof (hostdata->synchronous_want)); + + NCR53c7x0_driver_init (host); + + /* + * Set up an interrupt handler if we aren't already sharing an IRQ + * with another board. + */ + +#ifdef CONFIG_MVME166 + if (request_irq(IRQ_MVME166_SCSI, NCR53c7x0_intr, 0, "SCSI-script", NULL)) + panic ("Couldn't get SCSI IRQ"); +#ifdef MVME166_INTFLY + else if (request_irq(IRQ_MVME166_FLY, NCR53c7x0_intr, 0, "SCSI-intfly", NULL)) + panic ("Couldn't get INT_FLY IRQ"); +#endif +#else + for (search = first_host; search && !(search->hostt == the_template && + search->irq == host->irq && search != host); search=search->next); + + if (!search) { +#ifdef CONFIG_AMIGA + if (request_irq(IRQ_AMIGA_PORTS, NCR53c7x0_intr, 0, "53c7xx", NCR53c7x0_intr)) { +#else + if (request_irq(host->irq, NCR53c7x0_intr, SA_INTERRUPT, "53c7xx", NULL)) { +#endif + printk("scsi%d : IRQ%d not free, detaching\n" + " You have either a configuration problem, or a\n" + " broken BIOS. You may wish to manually assign\n" + " an interrupt to the NCR board rather than using\n" + " an automatic setting.\n", + host->host_no, host->irq); + scsi_unregister (host); + return -1; + } + } else { + printk("scsi%d : using interrupt handler previously installed for scsi%d\n", + host->host_no, search->host_no); + } +#endif + + if ((hostdata->run_tests && hostdata->run_tests(host) == -1) || + (hostdata->options & OPTION_DEBUG_TESTS_ONLY)) { + /* XXX Should disable interrupts, etc. here */ + scsi_unregister (host); + return -1; + } else { + if (host->io_port) { + host->n_io_port = 128; + request_region (host->io_port, host->n_io_port, "ncr53c7xx"); + } + } + + if (NCR53c7x0_read8 (SBCL_REG) & SBCL_BSY) { + printk ("scsi%d : bus wedge, doing SCSI reset\n", host->host_no); + hard_reset (host); + } + return 0; +} + +/* + * Function : static int normal_init(Scsi_Host_Template *tpnt, int board, + * int chip, u32 base, int io_port, int irq, int dma, int pcivalid, + * unsigned char pci_bus, unsigned char pci_device_fn, + * long long options); + * + * Purpose : initializes a NCR53c7,8x0 based on base addresses, + * IRQ, and DMA channel. + * + * Useful where a new NCR chip is backwards compatible with + * a supported chip, but the DEVICE ID has changed so it + * doesn't show up when the autoprobe does a pcibios_find_device. + * + * Inputs : tpnt - Template for this SCSI adapter, board - board level + * product, chip - 710 + * + * Returns : 0 on success, -1 on failure. + * + */ + +int +ncr53c7xx_init (Scsi_Host_Template *tpnt, int board, int chip, + u32 base, int io_port, int irq, int dma, long long options, int clock) +{ + struct Scsi_Host *instance; + struct NCR53c7x0_hostdata *hostdata; + char chip_str[80]; + int script_len = 0, dsa_len = 0, size = 0, max_cmd_size = 0, + schedule_size = 0, ok = 0; + void *tmp; + + switch (chip) { + case 710: + schedule_size = (tpnt->can_queue + 1) * 8 /* JUMP instruction size */; + script_len = NCR53c7xx_script_len; + dsa_len = NCR53c7xx_dsa_len; + options |= OPTION_INTFLY; + sprintf (chip_str, "NCR53c%d", chip); + break; + default: + printk("scsi-ncr53c7xx : unsupported SCSI chip %d\n", chip); + return -1; + } + + printk("scsi-ncr53c7xx : %s at memory 0x%x, io 0x%x, irq %d", + chip_str, (unsigned) base, io_port, irq); + if (dma == DMA_NONE) + printk("\n"); + else + printk(", dma %d\n", dma); + + if (options & OPTION_DEBUG_PROBE_ONLY) { + printk ("scsi-ncr53c7xx : probe only enabled, aborting initialization\n"); + return -1; + } + + max_cmd_size = sizeof(struct NCR53c7x0_cmd) + dsa_len + + /* Size of dynamic part of command structure : */ + 2 * /* Worst case : we don't know if we need DATA IN or DATA out */ + ( 2 * /* Current instructions per scatter/gather segment */ + tpnt->sg_tablesize + + 3 /* Current startup / termination required per phase */ + ) * + 8 /* Each instruction is eight bytes */; + + /* Allocate fixed part of hostdata, dynamic part to hold appropriate + SCSI SCRIPT(tm) plus a single, maximum-sized NCR53c7x0_cmd structure. + + We need a NCR53c7x0_cmd structure for scan_scsis() when we are + not loaded as a module, and when we're loaded as a module, we + can't use a non-dynamically allocated structure because modules + are vmalloc()'d, which can allow structures to cross page + boundaries and breaks our physical/virtual address assumptions + for DMA. + + So, we stick it past the end of our hostdata structure. + + ASSUMPTION : + Regardless of how many simultaneous SCSI commands we allow, + the probe code only executes a _single_ instruction at a time, + so we only need one here, and don't need to allocate NCR53c7x0_cmd + structures for each target until we are no longer in scan_scsis + and kmalloc() has become functional (memory_init() happens + after all device driver initialization). + */ + + size = sizeof(struct NCR53c7x0_hostdata) + script_len + + /* Note that alignment will be guaranteed, since we put the command + allocated at probe time after the fixed-up SCSI script, which + consists of 32 bit words, aligned on a 32 bit boundary. But + on a 64bit machine we need 8 byte alignment for hostdata->free, so + we add in another 4 bytes to take care of potential misalignment + */ + (sizeof(void *) - sizeof(u32)) + max_cmd_size + schedule_size; + +#ifdef FORCE_DSA_ALIGNMENT + /* + * 53c710 rev.0 doesn't have an add-with-carry instruction. + * Ensure we allocate enough memory to force DSA alignment. + */ + size += 256; +#endif + flushsize = size; + instance = scsi_register (tpnt, size); + if (!instance) + return -1; + + /* FIXME : if we ever support an ISA NCR53c7xx based board, we + need to check if the chip is running in a 16 bit mode, and if so + unregister it if it is past the 16M (0x1000000) mark */ + + hostdata = (struct NCR53c7x0_hostdata *) + instance->hostdata; + hostdata->size = size; + hostdata->script_count = script_len / sizeof(u32); + hostdata = (struct NCR53c7x0_hostdata *) instance->hostdata; + hostdata->board = board; + hostdata->chip = chip; + + /* + * Being memory mapped is more desirable, since + * + * - Memory accesses may be faster. + * + * - The destination and source address spaces are the same for + * all instructions, meaning we don't have to twiddle dmode or + * any other registers. + * + * So, we try for memory mapped, and if we don't get it, + * we go for port mapped, and that failing we tell the user + * it can't work. + */ + + if (base) { + instance->base = (unsigned char *) (unsigned long) base; + /* Check for forced I/O mapping */ + if (!(options & OPTION_IO_MAPPED)) { + options |= OPTION_MEMORY_MAPPED; + ok = 1; + } + } else { + options &= ~OPTION_MEMORY_MAPPED; + } + + if (io_port) { + instance->io_port = io_port; + options |= OPTION_IO_MAPPED; + ok = 1; + } else { + options &= ~OPTION_IO_MAPPED; + } + + if (!ok) { + printk ("scsi%d : not initializing, no I/O or memory mapping known \n", + instance->host_no); + scsi_unregister (instance); + return -1; + } + instance->irq = irq; + instance->dma_channel = dma; + + hostdata->options = options; + hostdata->dsa_len = dsa_len; + hostdata->max_cmd_size = max_cmd_size; + hostdata->num_cmds = 1; + /* Initialize single command */ + tmp = (hostdata->script + hostdata->script_count); +#ifdef FORCE_DSA_ALIGNMENT + { + void *t = ROUNDUP(tmp, void *); + if (((u32)t & 0xff) > CmdPageStart) + t = (void *)((u32)t + 255); + t = (void *)(((u32)t & ~0xff) + CmdPageStart); + hostdata->free = t; + printk ("scsi: Registered size increased by 256 to %d\n", size); + printk ("scsi: CmdPageStart = 0x%02x\n", CmdPageStart); + printk ("scsi: tmp = 0x%08x, hostdata->free set to 0x%08x\n", + (u32)tmp, (u32)t); + } +#else + hostdata->free = ROUNDUP(tmp, void *); +#endif + hostdata->free->real = tmp; + hostdata->free->size = max_cmd_size; + hostdata->free->free = NULL; + hostdata->free->next = NULL; + hostdata->extra_allocate = 0; + + /* Allocate command start code space */ + hostdata->schedule = (chip == 700 || chip == 70066) ? + NULL : (u32 *) ((char *)hostdata->free + max_cmd_size); + +/* + * For diagnostic purposes, we don't really care how fast things blaze. + * For profiling, we want to access the 800ns resolution system clock, + * using a 'C' call on the host processor. + * + * Therefore, there's no need for the NCR chip to directly manipulate + * this data, and we should put it wherever is most convenient for + * Linux. + */ + if (track_events) + hostdata->events = (struct NCR53c7x0_event *) (track_events ? + vmalloc (sizeof (struct NCR53c7x0_event) * track_events) : NULL); + else + hostdata->events = NULL; + + if (hostdata->events) { + memset ((void *) hostdata->events, 0, sizeof(struct NCR53c7x0_event) * + track_events); + hostdata->event_size = track_events; + hostdata->event_index = 0; + } else + hostdata->event_size = 0; + + return NCR53c7x0_init(instance); +} + + +/* + * Function : static void NCR53c7x0_init_fixup (struct Scsi_Host *host) + * + * Purpose : copy and fixup the SCSI SCRIPTS(tm) code for this device. + * + * Inputs : host - pointer to this host adapter's structure + * + */ + +static void +NCR53c7x0_init_fixup (struct Scsi_Host *host) { + NCR53c7x0_local_declare(); + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + unsigned char tmp; + int i, ncr_to_memory, memory_to_ncr; + u32 base; + NCR53c7x0_local_setup(host); + + + /* XXX - NOTE : this code MUST be made endian aware */ + /* Copy code into buffer that was allocated at detection time. */ + memcpy ((void *) hostdata->script, (void *) SCRIPT, + sizeof(SCRIPT)); + /* Fixup labels */ + for (i = 0; i < PATCHES; ++i) + hostdata->script[LABELPATCHES[i]] += + virt_to_bus(hostdata->script); + /* Fixup addresses of constants that used to be EXTERNAL */ + + patch_abs_32 (hostdata->script, 0, NCR53c7xx_msg_abort, + virt_to_bus(&(hostdata->NCR53c7xx_msg_abort))); + patch_abs_32 (hostdata->script, 0, NCR53c7xx_msg_reject, + virt_to_bus(&(hostdata->NCR53c7xx_msg_reject))); + patch_abs_32 (hostdata->script, 0, NCR53c7xx_zero, + virt_to_bus(&(hostdata->NCR53c7xx_zero))); + patch_abs_32 (hostdata->script, 0, NCR53c7xx_sink, + virt_to_bus(&(hostdata->NCR53c7xx_sink))); + patch_abs_32 (hostdata->script, 0, NOP_insn, + virt_to_bus(&(hostdata->NOP_insn))); + patch_abs_32 (hostdata->script, 0, schedule, + virt_to_bus((void *) hostdata->schedule)); + + /* Fixup references to external variables: */ + for (i = 0; i < EXTERNAL_PATCHES_LEN; ++i) + hostdata->script[EXTERNAL_PATCHES[i].offset] += + virt_to_bus(EXTERNAL_PATCHES[i].address); + + /* + * Fixup absolutes set at boot-time. + * + * All non-code absolute variables suffixed with "dsa_" and "int_" + * are constants, and need no fixup provided the assembler has done + * it for us (I don't know what the "real" NCR assembler does in + * this case, my assembler does the right magic). + */ + + patch_abs_rwri_data (hostdata->script, 0, dsa_save_data_pointer, + Ent_dsa_code_save_data_pointer - Ent_dsa_zero); + patch_abs_rwri_data (hostdata->script, 0, dsa_restore_pointers, + Ent_dsa_code_restore_pointers - Ent_dsa_zero); + patch_abs_rwri_data (hostdata->script, 0, dsa_check_reselect, + Ent_dsa_code_check_reselect - Ent_dsa_zero); + + /* + * Just for the hell of it, preserve the settings of + * Burst Length and Enable Read Line bits from the DMODE + * register. Make sure SCRIPTS start automagically. + */ + +#if defined(CONFIG_MVME166) + /* We know better what we want than 166Bug does! */ + tmp = DMODE_10_BL_8 | DMODE_10_FC2; +#else + tmp = NCR53c7x0_read8(DMODE_REG_10); + tmp &= (DMODE_BL_MASK | DMODE_10_FC2 | DMODE_10_FC1 | DMODE_710_PD | + DMODE_710_UO); +#endif + + if (!(hostdata->options & OPTION_MEMORY_MAPPED)) { + base = (u32) host->io_port; + memory_to_ncr = tmp|DMODE_800_DIOM; + ncr_to_memory = tmp|DMODE_800_SIOM; + } else { + base = virt_to_bus(host->base); + memory_to_ncr = ncr_to_memory = tmp; + } + + /* SCRATCHB_REG_10 == SCRATCHA_REG_800, as it happens */ + patch_abs_32 (hostdata->script, 0, addr_scratch, base + SCRATCHA_REG_800); + patch_abs_32 (hostdata->script, 0, addr_temp, base + TEMP_REG); + patch_abs_32 (hostdata->script, 0, addr_dsa, base + DSA_REG); + + /* + * I needed some variables in the script to be accessible to + * both the NCR chip and the host processor. For these variables, + * I made the arbitrary decision to store them directly in the + * hostdata structure rather than in the RELATIVE area of the + * SCRIPTS. + */ + + + patch_abs_rwri_data (hostdata->script, 0, dmode_memory_to_memory, tmp); + patch_abs_rwri_data (hostdata->script, 0, dmode_memory_to_ncr, memory_to_ncr); + patch_abs_rwri_data (hostdata->script, 0, dmode_ncr_to_memory, ncr_to_memory); + + patch_abs_32 (hostdata->script, 0, msg_buf, + virt_to_bus((void *)&(hostdata->msg_buf))); + patch_abs_32 (hostdata->script, 0, reconnect_dsa_head, + virt_to_bus((void *)&(hostdata->reconnect_dsa_head))); + patch_abs_32 (hostdata->script, 0, addr_reconnect_dsa_head, + virt_to_bus((void *)&(hostdata->addr_reconnect_dsa_head))); + patch_abs_32 (hostdata->script, 0, reselected_identify, + virt_to_bus((void *)&(hostdata->reselected_identify))); +/* reselected_tag is currently unused */ +#if 0 + patch_abs_32 (hostdata->script, 0, reselected_tag, + virt_to_bus((void *)&(hostdata->reselected_tag))); +#endif + + patch_abs_32 (hostdata->script, 0, test_dest, + virt_to_bus((void*)&hostdata->test_dest)); + patch_abs_32 (hostdata->script, 0, test_src, + virt_to_bus(&hostdata->test_source)); + patch_abs_32 (hostdata->script, 0, saved_dsa, + virt_to_bus(&hostdata->saved2_dsa)); + patch_abs_32 (hostdata->script, 0, emulfly, + virt_to_bus(&hostdata->emulated_intfly)); + + patch_abs_rwri_data (hostdata->script, 0, dsa_check_reselect, + (unsigned char)(Ent_dsa_code_check_reselect - Ent_dsa_zero)); + +/* These are for event logging; the ncr_event enum contains the + actual interrupt numbers. */ +#ifdef A_int_EVENT_SELECT + patch_abs_32 (hostdata->script, 0, int_EVENT_SELECT, (u32) EVENT_SELECT); +#endif +#ifdef A_int_EVENT_DISCONNECT + patch_abs_32 (hostdata->script, 0, int_EVENT_DISCONNECT, (u32) EVENT_DISCONNECT); +#endif +#ifdef A_int_EVENT_RESELECT + patch_abs_32 (hostdata->script, 0, int_EVENT_RESELECT, (u32) EVENT_RESELECT); +#endif +#ifdef A_int_EVENT_COMPLETE + patch_abs_32 (hostdata->script, 0, int_EVENT_COMPLETE, (u32) EVENT_COMPLETE); +#endif +#ifdef A_int_EVENT_IDLE + patch_abs_32 (hostdata->script, 0, int_EVENT_IDLE, (u32) EVENT_IDLE); +#endif +#ifdef A_int_EVENT_SELECT_FAILED + patch_abs_32 (hostdata->script, 0, int_EVENT_SELECT_FAILED, + (u32) EVENT_SELECT_FAILED); +#endif +#ifdef A_int_EVENT_BEFORE_SELECT + patch_abs_32 (hostdata->script, 0, int_EVENT_BEFORE_SELECT, + (u32) EVENT_BEFORE_SELECT); +#endif +#ifdef A_int_EVENT_RESELECT_FAILED + patch_abs_32 (hostdata->script, 0, int_EVENT_RESELECT_FAILED, + (u32) EVENT_RESELECT_FAILED); +#endif + + /* + * Make sure the NCR and Linux code agree on the location of + * certain fields. + */ + + hostdata->E_accept_message = Ent_accept_message; + hostdata->E_command_complete = Ent_command_complete; + hostdata->E_cmdout_cmdout = Ent_cmdout_cmdout; + hostdata->E_data_transfer = Ent_data_transfer; + hostdata->E_debug_break = Ent_debug_break; + hostdata->E_dsa_code_template = Ent_dsa_code_template; + hostdata->E_dsa_code_template_end = Ent_dsa_code_template_end; + hostdata->E_end_data_transfer = Ent_end_data_transfer; + hostdata->E_initiator_abort = Ent_initiator_abort; + hostdata->E_msg_in = Ent_msg_in; + hostdata->E_other_transfer = Ent_other_transfer; + hostdata->E_other_in = Ent_other_in; + hostdata->E_other_out = Ent_other_out; + hostdata->E_reject_message = Ent_reject_message; + hostdata->E_respond_message = Ent_respond_message; + hostdata->E_select = Ent_select; + hostdata->E_select_msgout = Ent_select_msgout; + hostdata->E_target_abort = Ent_target_abort; +#ifdef Ent_test_0 + hostdata->E_test_0 = Ent_test_0; +#endif + hostdata->E_test_1 = Ent_test_1; + hostdata->E_test_2 = Ent_test_2; +#ifdef Ent_test_3 + hostdata->E_test_3 = Ent_test_3; +#endif + hostdata->E_wait_reselect = Ent_wait_reselect; + hostdata->E_dsa_code_begin = Ent_dsa_code_begin; + + hostdata->dsa_cmdout = A_dsa_cmdout; + hostdata->dsa_cmnd = A_dsa_cmnd; + hostdata->dsa_datain = A_dsa_datain; + hostdata->dsa_dataout = A_dsa_dataout; + hostdata->dsa_end = A_dsa_end; + hostdata->dsa_msgin = A_dsa_msgin; + hostdata->dsa_msgout = A_dsa_msgout; + hostdata->dsa_msgout_other = A_dsa_msgout_other; + hostdata->dsa_next = A_dsa_next; + hostdata->dsa_select = A_dsa_select; + hostdata->dsa_start = Ent_dsa_code_template - Ent_dsa_zero; + hostdata->dsa_status = A_dsa_status; + hostdata->dsa_jump_dest = Ent_dsa_code_fix_jump - Ent_dsa_zero + + 8 /* destination operand */; + + /* sanity check */ + if (A_dsa_fields_start != Ent_dsa_code_template_end - + Ent_dsa_zero) + printk("scsi%d : NCR dsa_fields start is %d not %d\n", + host->host_no, A_dsa_fields_start, Ent_dsa_code_template_end - + Ent_dsa_zero); + + printk("scsi%d : NCR code relocated to 0x%lx (virt 0x%p)\n", host->host_no, + virt_to_bus(hostdata->script), hostdata->script); +flush_cache_all(); +cache_push(virt_to_bus(hostdata->script), flushsize); +cache_clear(virt_to_bus(hostdata->script), flushsize); +} + +/* + * Function : static int NCR53c7xx_run_tests (struct Scsi_Host *host) + * + * Purpose : run various verification tests on the NCR chip, + * including interrupt generation, and proper bus mastering + * operation. + * + * Inputs : host - a properly initialized Scsi_Host structure + * + * Preconditions : the NCR chip must be in a halted state. + * + * Returns : 0 if all tests were successful, -1 on error. + * + */ + +static int +NCR53c7xx_run_tests (struct Scsi_Host *host) { + NCR53c7x0_local_declare(); + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + unsigned long timeout; + u32 start; + int failed, i; + unsigned long flags; + NCR53c7x0_local_setup(host); + + /* The NCR chip _must_ be idle to run the test scripts */ + + save_flags(flags); + cli(); + if (!hostdata->idle) { + printk ("scsi%d : chip not idle, aborting tests\n", host->host_no); + restore_flags(flags); + return -1; + } + + /* + * Check for functional interrupts, this could work as an + * autoprobe routine. + */ + + if ((hostdata->options & OPTION_DEBUG_TEST1) && + hostdata->state != STATE_DISABLED) { + hostdata->idle = 0; + hostdata->test_running = 1; + hostdata->test_completed = -1; + hostdata->test_dest = 0; + hostdata->test_source = 0xdeadbeef; + start = virt_to_bus (hostdata->script) + hostdata->E_test_1; + hostdata->state = STATE_RUNNING; + printk ("scsi%d : test 1", host->host_no); + flush_cache_all(); + cache_push(virt_to_bus(hostdata->script), flushsize); + cache_clear(virt_to_bus(hostdata->script), flushsize); + NCR53c7x0_write32 (DSP_REG, start); + if (hostdata->options & OPTION_DEBUG_TRACE) + NCR53c7x0_write8 (DCNTL_REG, hostdata->saved_dcntl | DCNTL_SSM | + DCNTL_STD); + printk (" started\n"); + sti(); + + /* + * This is currently a .5 second timeout, since (in theory) no slow + * board will take that long. In practice, we've seen one + * pentium which occassionally fails with this, but works with + * 10 times as much? + */ + + timeout = jiffies + 5 * HZ / 10; + while ((hostdata->test_completed == -1) && jiffies < timeout) + barrier(); + + failed = 1; + if (hostdata->test_completed == -1) + printk ("scsi%d : driver test 1 timed out%s\n",host->host_no , + (hostdata->test_dest == 0xdeadbeef) ? + " due to lost interrupt.\n" + " Please verify that the correct IRQ is being used for your board,\n" + : ""); + else if (hostdata->test_completed != 1) + printk ("scsi%d : test 1 bad interrupt value (%d)\n", + host->host_no, hostdata->test_completed); + else + failed = (hostdata->test_dest != 0xdeadbeef); + + if (hostdata->test_dest != 0xdeadbeef) { + printk ("scsi%d : driver test 1 read 0x%x instead of 0xdeadbeef indicating a\n" + " probable cache invalidation problem. Please configure caching\n" + " as write-through or disabled\n", + host->host_no, hostdata->test_dest); + } + + if (failed) { + printk ("scsi%d : DSP = 0x%p (script at 0x%p, start at 0x%x)\n", + host->host_no, bus_to_virt(NCR53c7x0_read32(DSP_REG)), + hostdata->script, start); + printk ("scsi%d : DSPS = 0x%x\n", host->host_no, + NCR53c7x0_read32(DSPS_REG)); + restore_flags(flags); + return -1; + } + hostdata->test_running = 0; + } + + if ((hostdata->options & OPTION_DEBUG_TEST2) && + hostdata->state != STATE_DISABLED) { + u32 dsa[48]; + unsigned char identify = IDENTIFY(0, 0); + unsigned char cmd[6]; + unsigned char data[36]; + unsigned char status = 0xff; + unsigned char msg = 0xff; + + cmd[0] = INQUIRY; + cmd[1] = cmd[2] = cmd[3] = cmd[5] = 0; + cmd[4] = sizeof(data); + + dsa[2] = 1; + dsa[3] = virt_to_bus(&identify); + dsa[4] = 6; + dsa[5] = virt_to_bus(&cmd); + dsa[6] = sizeof(data); + dsa[7] = virt_to_bus(&data); + dsa[8] = 1; + dsa[9] = virt_to_bus(&status); + dsa[10] = 1; + dsa[11] = virt_to_bus(&msg); + + for (i = 0; i < 6; ++i) { +#ifdef VALID_IDS + if (!hostdata->valid_ids[i]) + continue; +#endif + cli(); + if (!hostdata->idle) { + printk ("scsi%d : chip not idle, aborting tests\n", host->host_no); + restore_flags(flags); + return -1; + } + + /* 710: bit mapped scsi ID, async */ + dsa[0] = (1 << i) << 16; + hostdata->idle = 0; + hostdata->test_running = 2; + hostdata->test_completed = -1; + start = virt_to_bus(hostdata->script) + hostdata->E_test_2; + hostdata->state = STATE_RUNNING; + flush_cache_all(); + cache_clear(virt_to_bus(hostdata->script), flushsize); + NCR53c7x0_write32 (DSA_REG, virt_to_bus(dsa)); + NCR53c7x0_write32 (DSP_REG, start); + if (hostdata->options & OPTION_DEBUG_TRACE) + NCR53c7x0_write8 (DCNTL_REG, hostdata->saved_dcntl | + DCNTL_SSM | DCNTL_STD); + sti(); + + timeout = jiffies + 5 * HZ; /* arbitrary */ + while ((hostdata->test_completed == -1) && jiffies < timeout) + barrier(); + + NCR53c7x0_write32 (DSA_REG, 0); + + if (hostdata->test_completed == 2) { + data[35] = 0; + printk ("scsi%d : test 2 INQUIRY to target %d, lun 0 : %s\n", + host->host_no, i, data + 8); + printk ("scsi%d : status ", host->host_no); + print_status (status); + printk ("\nscsi%d : message ", host->host_no); + print_msg (&msg); + printk ("\n"); + } else if (hostdata->test_completed == 3) { + printk("scsi%d : test 2 no connection with target %d\n", + host->host_no, i); + if (!hostdata->idle) { + printk("scsi%d : not idle\n", host->host_no); + restore_flags(flags); + return -1; + } + } else if (hostdata->test_completed == -1) { + printk ("scsi%d : test 2 timed out\n", host->host_no); + restore_flags(flags); + return -1; + } + hostdata->test_running = 0; + } + } + + restore_flags(flags); + return 0; +} + +/* + * Function : static void NCR53c7xx_dsa_fixup (struct NCR53c7x0_cmd *cmd) + * + * Purpose : copy the NCR53c8xx dsa structure into cmd's dsa buffer, + * performing all necessary relocation. + * + * Inputs : cmd, a NCR53c7x0_cmd structure with a dsa area large + * enough to hold the NCR53c8xx dsa. + */ + +static void +NCR53c7xx_dsa_fixup (struct NCR53c7x0_cmd *cmd) { + Scsi_Cmnd *c = cmd->cmd; + struct Scsi_Host *host = c->host; + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + int i; + + memcpy (cmd->dsa, hostdata->script + (hostdata->E_dsa_code_template / 4), + hostdata->E_dsa_code_template_end - hostdata->E_dsa_code_template); + + /* + * Note : within the NCR 'C' code, dsa points to the _start_ + * of the DSA structure, and _not_ the offset of dsa_zero within + * that structure used to facilitate shorter signed offsets + * for the 8 bit ALU. + * + * The implications of this are that + * + * - 32 bit A_dsa_* absolute values require an additional + * dsa_zero added to their value to be correct, since they are + * relative to dsa_zero which is in essentially a separate + * space from the code symbols. + * + * - All other symbols require no special treatment. + */ + + patch_abs_tci_data (cmd->dsa, Ent_dsa_code_template / sizeof(u32), + dsa_temp_lun, c->lun); + patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32), + dsa_temp_addr_next, virt_to_bus(&cmd->dsa_next_addr)); + patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32), + dsa_temp_next, virt_to_bus(cmd->dsa) + Ent_dsa_zero - + Ent_dsa_code_template + A_dsa_next); + patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32), + dsa_temp_sync, virt_to_bus((void *)hostdata->sync[c->target].script)); + patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32), + dsa_sscf_710, virt_to_bus((void *)&hostdata->sync[c->target].sscf_710)); + patch_abs_tci_data (cmd->dsa, Ent_dsa_code_template / sizeof(u32), + dsa_temp_target, 1 << c->target); + /* XXX - new pointer stuff */ + patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32), + dsa_temp_addr_saved_pointer, virt_to_bus(&cmd->saved_data_pointer)); + patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32), + dsa_temp_addr_saved_residual, virt_to_bus(&cmd->saved_residual)); + patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32), + dsa_temp_addr_residual, virt_to_bus(&cmd->residual)); + + /* XXX - new start stuff */ + + patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32), + dsa_temp_addr_dsa_value, virt_to_bus(&cmd->dsa_addr)); + +flush_cache_all(); +cache_push(virt_to_bus(hostdata->script), flushsize); +cache_clear(virt_to_bus(hostdata->script), flushsize); +} + +/* + * Function : run_process_issue_queue (void) + * + * Purpose : insure that the coroutine is running and will process our + * request. process_issue_queue_running is checked/set here (in an + * inline function) rather than in process_issue_queue itself to reduce + * the chances of stack overflow. + * + */ + +static volatile int process_issue_queue_running = 0; + +static __inline__ void +run_process_issue_queue(void) { + unsigned long flags; + save_flags (flags); + cli(); + if (!process_issue_queue_running) { + process_issue_queue_running = 1; + process_issue_queue(flags); + /* + * process_issue_queue_running is cleared in process_issue_queue + * once it can't do more work, and process_issue_queue exits with + * interrupts disabled. + */ + } + restore_flags (flags); +} + +/* + * Function : static void abnormal_finished (struct NCR53c7x0_cmd *cmd, int + * result) + * + * Purpose : mark SCSI command as finished, OR'ing the host portion + * of the result word into the result field of the corresponding + * Scsi_Cmnd structure, and removing it from the internal queues. + * + * Inputs : cmd - command, result - entire result field + * + * Preconditions : the NCR chip should be in a halted state when + * abnormal_finished is run, since it modifies structures which + * the NCR expects to have exclusive access to. + */ + +static void +abnormal_finished (struct NCR53c7x0_cmd *cmd, int result) { + Scsi_Cmnd *c = cmd->cmd; + struct Scsi_Host *host = c->host; + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + unsigned long flags; + int left, found; + volatile struct NCR53c7x0_cmd * linux_search; + volatile struct NCR53c7x0_cmd * volatile *linux_prev; + volatile u32 *ncr_prev, *ncrcurrent, ncr_search; + +#if 0 + printk ("scsi%d: abnormal finished\n", host->host_no); +#endif + + save_flags(flags); + cli(); + found = 0; + /* + * Traverse the NCR issue array until we find a match or run out + * of instructions. Instructions in the NCR issue array are + * either JUMP or NOP instructions, which are 2 words in length. + */ + + + for (found = 0, left = host->can_queue, ncrcurrent = hostdata->schedule; + left > 0; --left, ncrcurrent += 2) + { + if (issue_to_cmd (host, hostdata, (u32 *) ncrcurrent) == cmd) + { + ncrcurrent[0] = hostdata->NOP_insn; + ncrcurrent[1] = 0xdeadbeef; + ++found; + break; + } + } + + /* + * Traverse the NCR reconnect list of DSA structures until we find + * a pointer to this dsa or have found too many command structures. + * We let prev point at the next field of the previous element or + * head of the list, so we don't do anything different for removing + * the head element. + */ + + for (left = host->can_queue, + ncr_search = hostdata->reconnect_dsa_head, + ncr_prev = &hostdata->reconnect_dsa_head; + left >= 0 && ncr_search && + ((char*)bus_to_virt(ncr_search) + hostdata->dsa_start) + != (char *) cmd->dsa; + ncr_prev = (u32*) ((char*)bus_to_virt(ncr_search) + + hostdata->dsa_next), ncr_search = *ncr_prev, --left); + + if (left < 0) + printk("scsi%d: loop detected in ncr reconncect list\n", + host->host_no); + else if (ncr_search) + if (found) + printk("scsi%d: scsi %ld in ncr issue array and reconnect lists\n", + host->host_no, c->pid); + else { + volatile u32 * next = (u32 *) + ((char *)bus_to_virt(ncr_search) + hostdata->dsa_next); + *ncr_prev = *next; +/* If we're at the tail end of the issue queue, update that pointer too. */ + found = 1; + } + + /* + * Traverse the host running list until we find this command or discover + * we have too many elements, pointing linux_prev at the next field of the + * linux_previous element or head of the list, search at this element. + */ + + for (left = host->can_queue, linux_search = hostdata->running_list, + linux_prev = &hostdata->running_list; + left >= 0 && linux_search && linux_search != cmd; + linux_prev = &(linux_search->next), + linux_search = linux_search->next, --left); + + if (left < 0) + printk ("scsi%d: loop detected in host running list for scsi pid %ld\n", + host->host_no, c->pid); + else if (linux_search) { + *linux_prev = linux_search->next; + --hostdata->busy[c->target][c->lun]; + } + + /* Return the NCR command structure to the free list */ + cmd->next = hostdata->free; + hostdata->free = cmd; + c->host_scribble = NULL; + + /* And return */ + c->result = result; + c->scsi_done(c); + + restore_flags(flags); + run_process_issue_queue(); +} + +/* + * Function : static void intr_break (struct Scsi_Host *host, + * struct NCR53c7x0_cmd *cmd) + * + * Purpose : Handler for breakpoint interrupts from a SCSI script + * + * Inputs : host - pointer to this host adapter's structure, + * cmd - pointer to the command (if any) dsa was pointing + * to. + * + */ + +static void +intr_break (struct Scsi_Host *host, struct + NCR53c7x0_cmd *cmd) { + NCR53c7x0_local_declare(); + struct NCR53c7x0_break *bp; +#if 0 + Scsi_Cmnd *c = cmd ? cmd->cmd : NULL; +#endif + u32 *dsp; + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + unsigned long flags; + NCR53c7x0_local_setup(host); + + /* + * Find the break point corresponding to this address, and + * dump the appropriate debugging information to standard + * output. + */ + save_flags(flags); + cli(); + flush_cache_all(); + cache_push(virt_to_bus(hostdata->script), flushsize); + dsp = (u32 *) bus_to_virt(NCR53c7x0_read32(DSP_REG)); + for (bp = hostdata->breakpoints; bp && bp->address != dsp; + bp = bp->next); + if (!bp) + panic("scsi%d : break point interrupt from %p with no breakpoint!", + host->host_no, dsp); + + /* + * Configure the NCR chip for manual start mode, so that we can + * point the DSP register at the instruction that follows the + * INT int_debug_break instruction. + */ + + NCR53c7x0_write8 (hostdata->dmode, + NCR53c7x0_read8(hostdata->dmode)|DMODE_MAN); + + /* + * And update the DSP register, using the size of the old + * instruction in bytes. + */ + + restore_flags(flags); +} +/* + * Function : static void print_synchronous (const char *prefix, + * const unsigned char *msg) + * + * Purpose : print a pretty, user and machine parsable representation + * of a SDTR message, including the "real" parameters, data + * clock so we can tell transfer rate at a glance. + * + * Inputs ; prefix - text to prepend, msg - SDTR message (5 bytes) + */ + +static void +print_synchronous (const char *prefix, const unsigned char *msg) { + if (msg[4]) { + int Hz = 1000000000 / (msg[3] * 4); + int integer = Hz / 1000000; + int fraction = (Hz - (integer * 1000000)) / 10000; + printk ("%speriod %dns offset %d %d.%02dMHz %s SCSI%s\n", + prefix, (int) msg[3] * 4, (int) msg[4], integer, fraction, + (((msg[3] * 4) < 200) ? "FAST" : "synchronous"), + (((msg[3] * 4) < 200) ? "-II" : "")); + } else + printk ("%sasynchronous SCSI\n", prefix); +} + +/* + * Function : static void set_synchronous (struct Scsi_Host *host, + * int target, int sxfer, int scntl3, int now_connected) + * + * Purpose : reprogram transfers between the selected SCSI initiator and + * target with the given register values; in the indirect + * select operand, reselection script, and chip registers. + * + * Inputs : host - NCR53c7,8xx SCSI host, target - number SCSI target id, + * sxfer and scntl3 - NCR registers. now_connected - if non-zero, + * we should reprogram the registers now too. + * + * NOTE: For 53c710, scntl3 is actually used for SCF bits from + * SBCL, as we don't have a SCNTL3. + */ + +static void +set_synchronous (struct Scsi_Host *host, int target, int sxfer, int scntl3, + int now_connected) { + NCR53c7x0_local_declare(); + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + u32 *script; + NCR53c7x0_local_setup(host); + + /* These are eight bit registers */ + sxfer &= 0xff; + scntl3 &= 0xff; + + hostdata->sync[target].sxfer_sanity = sxfer; + hostdata->sync[target].scntl3_sanity = scntl3; + +/* + * HARD CODED : synchronous script is EIGHT words long. This + * must agree with 53c7.8xx.h + */ + + if ((hostdata->chip != 700) && (hostdata->chip != 70066)) { + hostdata->sync[target].select_indirect = (1 << target) << 16 | + (sxfer << 8); + hostdata->sync[target].sscf_710 = scntl3; + + script = (u32 *) hostdata->sync[target].script; + + /* XXX - add NCR53c7x0 code to reprogram SCF bits if we want to */ + script[0] = ((DCMD_TYPE_RWRI | DCMD_RWRI_OPC_MODIFY | + DCMD_RWRI_OP_MOVE) << 24) | + (SBCL_REG << 16) | (scntl3 << 8); + script[1] = 0; + script += 2; + + script[0] = ((DCMD_TYPE_RWRI | DCMD_RWRI_OPC_MODIFY | + DCMD_RWRI_OP_MOVE) << 24) | + (SXFER_REG << 16) | (sxfer << 8); + script[1] = 0; + script += 2; + +#ifdef DEBUG_SYNC_INTR + if (hostdata->options & OPTION_DEBUG_DISCONNECT) { + script[0] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_INT) << 24) | DBC_TCI_TRUE; + script[1] = DEBUG_SYNC_INTR; + script += 2; + } +#endif + + script[0] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_RETURN) << 24) | DBC_TCI_TRUE; + script[1] = 0; + script += 2; + } + + if (hostdata->options & OPTION_DEBUG_SYNCHRONOUS) + printk ("scsi%d : target %d sync parameters are sxfer=0x%x, scntl3=0x%x\n", + host->host_no, target, sxfer, scntl3); + + if (now_connected) { + NCR53c7x0_write8(SBCL_REG, scntl3); + NCR53c7x0_write8(SXFER_REG, sxfer); + } +} + + +/* + * Function : static int asynchronous (struct Scsi_Host *host, int target) + * + * Purpose : reprogram between the selected SCSI Host adapter and target + * (assumed to be currently connected) for asynchronous transfers. + * + * Inputs : host - SCSI host structure, target - numeric target ID. + * + * Preconditions : the NCR chip should be in one of the halted states + */ + +static void +asynchronous (struct Scsi_Host *host, int target) { + NCR53c7x0_local_declare(); + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + NCR53c7x0_local_setup(host); + set_synchronous (host, target, /* no offset */ 0, hostdata->saved_scntl3, + 1); + printk ("scsi%d : setting target %d to asynchronous SCSI\n", + host->host_no, target); +} + +/* + * XXX - do we want to go out of our way (ie, add extra code to selection + * in the NCR53c710/NCR53c720 script) to reprogram the synchronous + * conversion bits, or can we be content in just setting the + * sxfer bits? I chose to do so [richard@sleepie.demon.co.uk] + */ + +/* Table for NCR53c8xx synchronous values */ + +/* This table is also correct for 710, allowing that scf=4 is equivalent + * of SSCF=0 (ie use DCNTL, divide by 3) for a 50.01-66.00MHz clock. + * For any other clock values, we cannot use entries with SCF values of + * 4. I guess that for a 66MHz clock, the slowest it will set is 2MHz, + * and for a 50MHz clock, the slowest will be 2.27Mhz. Should check + * that a device doesn't try and negotiate sync below these limits! + */ + +static const struct { + int div; /* Total clock divisor * 10 */ + unsigned char scf; /* */ + unsigned char tp; /* 4 + tp = xferp divisor */ +} syncs[] = { +/* div scf tp div scf tp div scf tp */ + { 40, 1, 0}, { 50, 1, 1}, { 60, 1, 2}, + { 70, 1, 3}, { 75, 2, 1}, { 80, 1, 4}, + { 90, 1, 5}, { 100, 1, 6}, { 105, 2, 3}, + { 110, 1, 7}, { 120, 2, 4}, { 135, 2, 5}, + { 140, 3, 3}, { 150, 2, 6}, { 160, 3, 4}, + { 165, 2, 7}, { 180, 3, 5}, { 200, 3, 6}, + { 210, 4, 3}, { 220, 3, 7}, { 240, 4, 4}, + { 270, 4, 5}, { 300, 4, 6}, { 330, 4, 7} +}; + +/* + * Function : static void synchronous (struct Scsi_Host *host, int target, + * char *msg) + * + * Purpose : reprogram transfers between the selected SCSI initiator and + * target for synchronous SCSI transfers such that the synchronous + * offset is less than that requested and period at least as long + * as that requested. Also modify *msg such that it contains + * an appropriate response. + * + * Inputs : host - NCR53c7,8xx SCSI host, target - number SCSI target id, + * msg - synchronous transfer request. + */ + + +static void +synchronous (struct Scsi_Host *host, int target, char *msg) { + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + int desire, divisor, i, limit; + unsigned char scntl3, sxfer; +/* The diagnostic message fits on one line, even with max. width integers */ + char buf[80]; + +/* Desired transfer clock in Hz */ + desire = 1000000000L / (msg[3] * 4); +/* Scale the available SCSI clock by 10 so we get tenths */ + divisor = (hostdata->scsi_clock * 10) / desire; + +/* NCR chips can handle at most an offset of 8 */ + if (msg[4] > 8) + msg[4] = 8; + + if (hostdata->options & OPTION_DEBUG_SDTR) + printk("scsi%d : optimal synchronous divisor of %d.%01d\n", + host->host_no, divisor / 10, divisor % 10); + + limit = (sizeof(syncs) / sizeof(syncs[0]) -1); + for (i = 0; (i < limit) && (divisor > syncs[i].div); ++i); + + if (hostdata->options & OPTION_DEBUG_SDTR) + printk("scsi%d : selected synchronous divisor of %d.%01d\n", + host->host_no, syncs[i].div / 10, syncs[i].div % 10); + + msg[3] = ((1000000000L / hostdata->scsi_clock) * syncs[i].div / 10 / 4); + + if (hostdata->options & OPTION_DEBUG_SDTR) + printk("scsi%d : selected synchronous period of %dns\n", host->host_no, + msg[3] * 4); + + scntl3 = syncs[i].scf; + sxfer = (msg[4] << SXFER_MO_SHIFT) | (syncs[i].tp << 4); + if (hostdata->options & OPTION_DEBUG_SDTR) + printk ("scsi%d : sxfer=0x%x scntl3=0x%x\n", + host->host_no, (int) sxfer, (int) scntl3); + set_synchronous (host, target, sxfer, scntl3, 1); + sprintf (buf, "scsi%d : setting target %d to ", host->host_no, target); + print_synchronous (buf, msg); +} + +/* + * Function : static int NCR53c7x0_dstat_sir_intr (struct Scsi_Host *host, + * struct NCR53c7x0_cmd *cmd) + * + * Purpose : Handler for INT generated instructions for the + * NCR53c810/820 SCSI SCRIPT + * + * Inputs : host - pointer to this host adapter's structure, + * cmd - pointer to the command (if any) dsa was pointing + * to. + * + */ + +static int +NCR53c7x0_dstat_sir_intr (struct Scsi_Host *host, struct + NCR53c7x0_cmd *cmd) { + NCR53c7x0_local_declare(); + int print; + Scsi_Cmnd *c = cmd ? cmd->cmd : NULL; + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + u32 dsps,*dsp; /* Argument of the INT instruction */ + +flush_cache_all(); +cache_push(virt_to_bus(hostdata->script), flushsize); +cache_clear(virt_to_bus(hostdata->script), flushsize); + + NCR53c7x0_local_setup(host); + dsps = NCR53c7x0_read32(DSPS_REG); + dsp = (u32 *) bus_to_virt(NCR53c7x0_read32(DSP_REG)); + + if (hostdata->options & OPTION_DEBUG_INTR) + printk ("scsi%d : DSPS = 0x%x\n", host->host_no, dsps); + + switch (dsps) { + case A_int_msg_1: + print = 1; + switch (hostdata->msg_buf[0]) { + /* + * Unless we've initiated synchronous negotiation, I don't + * think that this should happen. + */ + case MESSAGE_REJECT: + hostdata->dsp = hostdata->script + hostdata->E_accept_message / + sizeof(u32); + hostdata->dsp_changed = 1; + if (cmd && (cmd->flags & CMD_FLAG_SDTR)) { + printk ("scsi%d : target %d rejected SDTR\n", host->host_no, + c->target); + cmd->flags &= ~CMD_FLAG_SDTR; + asynchronous (host, c->target); + print = 0; + } + break; + case INITIATE_RECOVERY: + printk ("scsi%d : extended contingent allegiance not supported yet, rejecting\n", + host->host_no); + /* Fall through to default */ + hostdata->dsp = hostdata->script + hostdata->E_reject_message / + sizeof(u32); + hostdata->dsp_changed = 1; + break; + default: + printk ("scsi%d : unsupported message, rejecting\n", + host->host_no); + hostdata->dsp = hostdata->script + hostdata->E_reject_message / + sizeof(u32); + hostdata->dsp_changed = 1; + } + if (print) { + printk ("scsi%d : received message", host->host_no); + if (c) + printk (" from target %d lun %d ", c->target, c->lun); + print_msg ((unsigned char *) hostdata->msg_buf); + printk("\n"); + } + + return SPECIFIC_INT_NOTHING; + + + case A_int_msg_sdtr: +/* + * At this point, hostdata->msg_buf contains + * 0 EXTENDED MESSAGE + * 1 length + * 2 SDTR + * 3 period * 4ns + * 4 offset + */ + + if (cmd) { + char buf[80]; + sprintf (buf, "scsi%d : target %d %s ", host->host_no, c->target, + (cmd->flags & CMD_FLAG_SDTR) ? "accepting" : "requesting"); + print_synchronous (buf, (unsigned char *) hostdata->msg_buf); + + /* + * Initiator initiated, won't happen unless synchronous + * transfers are enabled. If we get a SDTR message in + * response to our SDTR, we should program our parameters + * such that + * offset <= requested offset + * period >= requested period + */ + if (cmd->flags & CMD_FLAG_SDTR) { + cmd->flags &= ~CMD_FLAG_SDTR; + if (hostdata->msg_buf[4]) + synchronous (host, c->target, (unsigned char *) + hostdata->msg_buf); + else + asynchronous (host, c->target); + hostdata->dsp = hostdata->script + hostdata->E_accept_message / + sizeof(u32); + hostdata->dsp_changed = 1; + return SPECIFIC_INT_NOTHING; + } else { + if (hostdata->options & OPTION_SYNCHRONOUS) { + cmd->flags |= CMD_FLAG_DID_SDTR; + synchronous (host, c->target, (unsigned char *) + hostdata->msg_buf); + } else { + hostdata->msg_buf[4] = 0; /* 0 offset = async */ + asynchronous (host, c->target); + } + patch_dsa_32 (cmd->dsa, dsa_msgout_other, 0, 5); + patch_dsa_32 (cmd->dsa, dsa_msgout_other, 1, (u32) + virt_to_bus ((void *)&hostdata->msg_buf)); + hostdata->dsp = hostdata->script + + hostdata->E_respond_message / sizeof(u32); + hostdata->dsp_changed = 1; + } + return SPECIFIC_INT_NOTHING; + } + /* Fall through to abort if we couldn't find a cmd, and + therefore a dsa structure to twiddle */ + case A_int_msg_wdtr: + hostdata->dsp = hostdata->script + hostdata->E_reject_message / + sizeof(u32); + hostdata->dsp_changed = 1; + return SPECIFIC_INT_NOTHING; + case A_int_err_unexpected_phase: + if (hostdata->options & OPTION_DEBUG_INTR) + printk ("scsi%d : unexpected phase\n", host->host_no); + return SPECIFIC_INT_ABORT; + case A_int_err_selected: + if ((hostdata->chip / 100) == 8) + printk ("scsi%d : selected by target %d\n", host->host_no, + (int) NCR53c7x0_read8(SDID_REG_800) &7); + else + printk ("scsi%d : selected by target LCRC=0x%02x\n", host->host_no, + (int) NCR53c7x0_read8(LCRC_REG_10)); + hostdata->dsp = hostdata->script + hostdata->E_target_abort / + sizeof(u32); + hostdata->dsp_changed = 1; + return SPECIFIC_INT_NOTHING; + case A_int_err_unexpected_reselect: + if ((hostdata->chip / 100) == 8) + printk ("scsi%d : unexpected reselect by target %d lun %d\n", + host->host_no, (int) NCR53c7x0_read8(SDID_REG_800) & 7, + hostdata->reselected_identify & 7); + else + printk ("scsi%d : unexpected reselect LCRC=0x%02x\n", host->host_no, + (int) NCR53c7x0_read8(LCRC_REG_10)); + hostdata->dsp = hostdata->script + hostdata->E_initiator_abort / + sizeof(u32); + hostdata->dsp_changed = 1; + return SPECIFIC_INT_NOTHING; +/* + * Since contingent allegiance conditions are cleared by the next + * command issued to a target, we must issue a REQUEST SENSE + * command after receiving a CHECK CONDITION status, before + * another command is issued. + * + * Since this NCR53c7x0_cmd will be freed after use, we don't + * care if we step on the various fields, so modify a few things. + */ + case A_int_err_check_condition: +#if 0 + if (hostdata->options & OPTION_DEBUG_INTR) +#endif + printk ("scsi%d : CHECK CONDITION\n", host->host_no); + if (!c) { + printk("scsi%d : CHECK CONDITION with no SCSI command\n", + host->host_no); + return SPECIFIC_INT_PANIC; + } + + /* + * FIXME : this uses the normal one-byte selection message. + * We may want to renegotiate for synchronous & WIDE transfers + * since these could be the crux of our problem. + * + hostdata->NOP_insn* FIXME : once SCSI-II tagged queuing is implemented, we'll + * have to set this up so that the rest of the DSA + * agrees with this being an untagged queue'd command. + */ + + patch_dsa_32 (cmd->dsa, dsa_msgout, 0, 1); + + /* + * Modify the table indirect for COMMAND OUT phase, since + * Request Sense is a six byte command. + */ + + patch_dsa_32 (cmd->dsa, dsa_cmdout, 0, 6); + + c->cmnd[0] = REQUEST_SENSE; + c->cmnd[1] &= 0xe0; /* Zero all but LUN */ + c->cmnd[2] = 0; + c->cmnd[3] = 0; + c->cmnd[4] = sizeof(c->sense_buffer); + c->cmnd[5] = 0; + + /* + * Disable dataout phase, and program datain to transfer to the + * sense buffer, and add a jump to other_transfer after the + * command so overflow/underrun conditions are detected. + */ + + patch_dsa_32 (cmd->dsa, dsa_dataout, 0, + virt_to_bus(hostdata->script) + hostdata->E_other_transfer); + patch_dsa_32 (cmd->dsa, dsa_datain, 0, + virt_to_bus(cmd->data_transfer_start)); + cmd->data_transfer_start[0] = (((DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I | + DCMD_BMI_IO)) << 24) | sizeof(c->sense_buffer); + cmd->data_transfer_start[1] = (u32) virt_to_bus(c->sense_buffer); + + cmd->data_transfer_start[2] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP) + << 24) | DBC_TCI_TRUE; + cmd->data_transfer_start[3] = (u32) virt_to_bus(hostdata->script) + + hostdata->E_other_transfer; + + /* + * Currently, this command is flagged as completed, ie + * it has valid status and message data. Reflag it as + * incomplete. Q - need to do something so that original + * status, etc are used. + */ + + cmd->cmd->result = 0xffff; + + /* + * Restart command as a REQUEST SENSE. + */ + hostdata->dsp = (u32 *) hostdata->script + hostdata->E_select / + sizeof(u32); + hostdata->dsp_changed = 1; + return SPECIFIC_INT_NOTHING; + case A_int_debug_break: + return SPECIFIC_INT_BREAK; + case A_int_norm_aborted: + hostdata->dsp = (u32 *) hostdata->schedule; + hostdata->dsp_changed = 1; + if (cmd) + abnormal_finished (cmd, DID_ERROR << 16); + return SPECIFIC_INT_NOTHING; + case A_int_norm_emulateintfly: + /* I'm not sure this is the right ! thing to do, but it works + * with the A4000T when copyback is disabled, and also the + * WarpEngine with copyback enabled, so it looks as though + * it does work to some extent. + * + * RGH: I don't really like it - You get an interrupt which + * calls NCR53c7x0_intr(), which calls this function (via + * intr_dma()), which calls NCR53c7x0_intr()..... + * Anyway lets see how it goes for now. + */ + hostdata->emulated_intfly = 1; + NCR53c7x0_intr(host->irq, NULL, NULL); + return SPECIFIC_INT_NOTHING; + case A_int_test_1: + case A_int_test_2: + hostdata->idle = 1; + hostdata->test_completed = (dsps - A_int_test_1) / 0x00010000 + 1; + if (hostdata->options & OPTION_DEBUG_INTR) + printk("scsi%d : test%d complete\n", host->host_no, + hostdata->test_completed); + return SPECIFIC_INT_NOTHING; +#ifdef A_int_debug_reselected_ok + case A_int_debug_reselected_ok: + if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR| + OPTION_DEBUG_DISCONNECT)) { + /* + * Note - this dsa is not based on location relative to + * the command structure, but to location relative to the + * DSA register + */ + u32 *dsa; + dsa = (u32 *) bus_to_virt (NCR53c7x0_read32(DSA_REG)); + + printk("scsi%d : reselected_ok (DSA = 0x%x (virt 0x%p)\n", + host->host_no, NCR53c7x0_read32(DSA_REG), dsa); + printk("scsi%d : resume address is 0x%x (virt 0x%p)\n", + host->host_no, cmd->saved_data_pointer, + bus_to_virt(cmd->saved_data_pointer)); + print_insn (host, hostdata->script + Ent_reselected_ok / + sizeof(u32), "", 1); + if ((hostdata->chip / 100) == 8) + printk ("scsi%d : sxfer=0x%x, scntl3=0x%x\n", + host->host_no, NCR53c7x0_read8(SXFER_REG), + NCR53c7x0_read8(SCNTL3_REG_800)); + else + printk ("scsi%d : sxfer=0x%x, cannot read SBCL\n", + host->host_no, NCR53c7x0_read8(SXFER_REG)); + if (c) { + print_insn (host, (u32 *) + hostdata->sync[c->target].script, "", 1); + print_insn (host, (u32 *) + hostdata->sync[c->target].script + 2, "", 1); + } + } + return SPECIFIC_INT_RESTART; +#endif +#ifdef A_int_debug_reselect_check + case A_int_debug_reselect_check: + if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) { + u32 *dsa; +#if 0 + u32 *code; +#endif + /* + * Note - this dsa is not based on location relative to + * the command structure, but to location relative to the + * DSA register + */ + dsa = bus_to_virt (NCR53c7x0_read32(DSA_REG)); + printk("scsi%d : reselected_check_next (DSA = 0x%lx (virt 0x%p))\n", + host->host_no, virt_to_bus(dsa), dsa); + if (dsa) { + printk("scsi%d : resume address is 0x%x (virt 0x%p)\n", + host->host_no, cmd->saved_data_pointer, + bus_to_virt (cmd->saved_data_pointer)); +#if 0 + printk("scsi%d : template code :\n", host->host_no); + for (code = dsa + (Ent_dsa_code_check_reselect - Ent_dsa_zero) + / sizeof(u32); code < (dsa + Ent_dsa_zero / sizeof(u32)); + code += print_insn (host, code, "", 1)); +#endif + } + print_insn (host, hostdata->script + Ent_reselected_ok / + sizeof(u32), "", 1); + } + return SPECIFIC_INT_RESTART; +#endif +#ifdef A_int_debug_dsa_schedule + case A_int_debug_dsa_schedule: + if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) { + u32 *dsa; + /* + * Note - this dsa is not based on location relative to + * the command structure, but to location relative to the + * DSA register + */ + dsa = (u32 *) bus_to_virt (NCR53c7x0_read32(DSA_REG)); + printk("scsi%d : dsa_schedule (old DSA = 0x%lx (virt 0x%p))\n", + host->host_no, virt_to_bus(dsa), dsa); + if (dsa) + printk("scsi%d : resume address is 0x%x (virt 0x%p)\n" + " (temp was 0x%x (virt 0x%p))\n", + host->host_no, cmd->saved_data_pointer, + bus_to_virt (cmd->saved_data_pointer), + NCR53c7x0_read32 (TEMP_REG), + bus_to_virt (NCR53c7x0_read32(TEMP_REG))); + } + return SPECIFIC_INT_RESTART; +#endif +#ifdef A_int_debug_scheduled + case A_int_debug_scheduled: + if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) { + printk("scsi%d : new I/O 0x%x (virt 0x%p) scheduled\n", + host->host_no, NCR53c7x0_read32(DSA_REG), + bus_to_virt(NCR53c7x0_read32(DSA_REG))); + } + return SPECIFIC_INT_RESTART; +#endif +#ifdef A_int_debug_idle + case A_int_debug_idle: + if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) { + printk("scsi%d : idle\n", host->host_no); + } + return SPECIFIC_INT_RESTART; +#endif +#ifdef A_int_debug_cmd + case A_int_debug_cmd: + if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) { + printk("scsi%d : command sent\n"); + } + return SPECIFIC_INT_RESTART; +#endif +#ifdef A_int_debug_dsa_loaded + case A_int_debug_dsa_loaded: + if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) { + printk("scsi%d : DSA loaded with 0x%x (virt 0x%p)\n", host->host_no, + NCR53c7x0_read32(DSA_REG), + bus_to_virt(NCR53c7x0_read32(DSA_REG))); + } + return SPECIFIC_INT_RESTART; +#endif +#ifdef A_int_debug_reselected + case A_int_debug_reselected: + if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR| + OPTION_DEBUG_DISCONNECT)) { + if ((hostdata->chip / 100) == 8) + printk("scsi%d : reselected by target %d lun %d\n", + host->host_no, (int) NCR53c7x0_read8(SDID_REG_800) & ~0x80, + (int) hostdata->reselected_identify & 7); + else + printk("scsi%d : reselected by LCRC=0x%02x lun %d\n", + host->host_no, (int) NCR53c7x0_read8(LCRC_REG_10), + (int) hostdata->reselected_identify & 7); + print_queues(host); + } + return SPECIFIC_INT_RESTART; +#endif +#ifdef A_int_debug_disconnect_msg + case A_int_debug_disconnect_msg: + if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) { + if (c) + printk("scsi%d : target %d lun %d disconnecting\n", + host->host_no, c->target, c->lun); + else + printk("scsi%d : unknown target disconnecting\n", + host->host_no); + } + return SPECIFIC_INT_RESTART; +#endif +#ifdef A_int_debug_disconnected + case A_int_debug_disconnected: + if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR| + OPTION_DEBUG_DISCONNECT)) { + printk ("scsi%d : disconnected, new queues are\n", + host->host_no); + print_queues(host); +#if 0 + /* Not valid on ncr53c710! */ + printk ("scsi%d : sxfer=0x%x, scntl3=0x%x\n", + host->host_no, NCR53c7x0_read8(SXFER_REG), + NCR53c7x0_read8(SCNTL3_REG_800)); +#endif + if (c) { + print_insn (host, (u32 *) + hostdata->sync[c->target].script, "", 1); + print_insn (host, (u32 *) + hostdata->sync[c->target].script + 2, "", 1); + } + } + return SPECIFIC_INT_RESTART; +#endif +#ifdef A_int_debug_panic + case A_int_debug_panic: + printk("scsi%d : int_debug_panic received\n", host->host_no); + print_lots (host); + return SPECIFIC_INT_PANIC; +#endif +#ifdef A_int_debug_saved + case A_int_debug_saved: + if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR| + OPTION_DEBUG_DISCONNECT)) { + printk ("scsi%d : saved data pointer 0x%x (virt 0x%p)\n", + host->host_no, cmd->saved_data_pointer, + bus_to_virt (cmd->saved_data_pointer)); + print_progress (c); + } + return SPECIFIC_INT_RESTART; +#endif +#ifdef A_int_debug_restored + case A_int_debug_restored: + if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR| + OPTION_DEBUG_DISCONNECT)) { + if (cmd) { + int size; + printk ("scsi%d : restored data pointer 0x%x (virt 0x%p)\n", + host->host_no, cmd->saved_data_pointer, bus_to_virt ( + cmd->saved_data_pointer)); + size = print_insn (host, (u32 *) + bus_to_virt(cmd->saved_data_pointer), "", 1); + size = print_insn (host, (u32 *) + bus_to_virt(cmd->saved_data_pointer) + size, "", 1); + print_progress (c); + } +#if 0 + printk ("scsi%d : datapath residual %d\n", + host->host_no, datapath_residual (host)) ; +#endif + } + return SPECIFIC_INT_RESTART; +#endif +#ifdef A_int_debug_sync + case A_int_debug_sync: + if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR| + OPTION_DEBUG_DISCONNECT|OPTION_DEBUG_SDTR)) { + unsigned char sxfer = NCR53c7x0_read8 (SXFER_REG), scntl3; + if ((hostdata->chip / 100) == 8) { + scntl3 = NCR53c7x0_read8 (SCNTL3_REG_800); + if (c) { + if (sxfer != hostdata->sync[c->target].sxfer_sanity || + scntl3 != hostdata->sync[c->target].scntl3_sanity) { + printk ("scsi%d : sync sanity check failed sxfer=0x%x, scntl3=0x%x", + host->host_no, sxfer, scntl3); + NCR53c7x0_write8 (SXFER_REG, sxfer); + NCR53c7x0_write8 (SCNTL3_REG_800, scntl3); + } + } else + printk ("scsi%d : unknown command sxfer=0x%x, scntl3=0x%x\n", + host->host_no, (int) sxfer, (int) scntl3); + } else { + if (c) { + if (sxfer != hostdata->sync[c->target].sxfer_sanity) { + printk ("scsi%d : sync sanity check failed sxfer=0x%x", + host->host_no, sxfer); + NCR53c7x0_write8 (SXFER_REG, sxfer); + NCR53c7x0_write8 (SBCL_REG, + hostdata->sync[c->target].sscf_710); + } + } else + printk ("scsi%d : unknown command sxfer=0x%x\n", + host->host_no, (int) sxfer); + } + } + return SPECIFIC_INT_RESTART; +#endif +#ifdef A_int_debug_datain + case A_int_debug_datain: + if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR| + OPTION_DEBUG_DISCONNECT|OPTION_DEBUG_SDTR)) { + int size; + if ((hostdata->chip / 100) == 8) + printk ("scsi%d : In do_datain (%s) sxfer=0x%x, scntl3=0x%x\n" + " datapath residual=%d\n", + host->host_no, sbcl_to_phase (NCR53c7x0_read8 (SBCL_REG)), + (int) NCR53c7x0_read8(SXFER_REG), + (int) NCR53c7x0_read8(SCNTL3_REG_800), + datapath_residual (host)) ; + else + printk ("scsi%d : In do_datain (%s) sxfer=0x%x\n" + " datapath residual=%d\n", + host->host_no, sbcl_to_phase (NCR53c7x0_read8 (SBCL_REG)), + (int) NCR53c7x0_read8(SXFER_REG), + datapath_residual (host)) ; + print_insn (host, dsp, "", 1); + size = print_insn (host, (u32 *) bus_to_virt(dsp[1]), "", 1); + print_insn (host, (u32 *) bus_to_virt(dsp[1]) + size, "", 1); + } + return SPECIFIC_INT_RESTART; +#endif +#ifdef A_int_debug_check_dsa + case A_int_debug_check_dsa: + if (NCR53c7x0_read8 (SCNTL1_REG) & SCNTL1_CON) { + int sdid; + int tmp; + char *where; + if (hostdata->chip / 100 == 8) + sdid = NCR53c7x0_read8 (SDID_REG_800) & 15; + else { + tmp = NCR53c7x0_read8 (SDID_REG_700); + if (!tmp) + panic ("SDID_REG_700 = 0"); + tmp >>= 1; + sdid = 0; + while (tmp) { + tmp >>= 1; + sdid++; + } + } + where = dsp - NCR53c7x0_insn_size(NCR53c7x0_read8 + (DCMD_REG)) == hostdata->script + + Ent_select_check_dsa / sizeof(u32) ? + "selection" : "reselection"; + if (c && sdid != c->target) { + printk ("scsi%d : SDID target %d != DSA target %d at %s\n", + host->host_no, sdid, c->target, where); + print_lots(host); + dump_events (host, 20); + return SPECIFIC_INT_PANIC; + } + } + return SPECIFIC_INT_RESTART; +#endif + default: + if ((dsps & 0xff000000) == 0x03000000) { + printk ("scsi%d : misc debug interrupt 0x%x\n", + host->host_no, dsps); + return SPECIFIC_INT_RESTART; + } else if ((dsps & 0xff000000) == 0x05000000) { + if (hostdata->events) { + struct NCR53c7x0_event *event; + ++hostdata->event_index; + if (hostdata->event_index >= hostdata->event_size) + hostdata->event_index = 0; + event = (struct NCR53c7x0_event *) hostdata->events + + hostdata->event_index; + event->event = (enum ncr_event) dsps; + event->dsa = bus_to_virt(NCR53c7x0_read32(DSA_REG)); + if (NCR53c7x0_read8 (SCNTL1_REG) & SCNTL1_CON) { + if (hostdata->chip / 100 == 8) + event->target = NCR53c7x0_read8(SSID_REG_800); + else { + unsigned char tmp, sdid; + tmp = NCR53c7x0_read8 (SDID_REG_700); + if (!tmp) + panic ("SDID_REG_700 = 0"); + tmp >>= 1; + sdid = 0; + while (tmp) { + tmp >>= 1; + sdid++; + } + event->target = sdid; + } + } + else + event->target = 255; + + if (event->event == EVENT_RESELECT) + event->lun = hostdata->reselected_identify & 0xf; + else if (c) + event->lun = c->lun; + else + event->lun = 255; + do_gettimeofday(&(event->time)); + if (c) { + event->pid = c->pid; + memcpy ((void *) event->cmnd, (void *) c->cmnd, + sizeof (event->cmnd)); + } else { + event->pid = -1; + } + } + return SPECIFIC_INT_RESTART; + } + + printk ("scsi%d : unknown user interrupt 0x%x\n", + host->host_no, (unsigned) dsps); + return SPECIFIC_INT_PANIC; + } +flush_cache_all(); +cache_push(virt_to_bus(hostdata->script), flushsize); +cache_clear(virt_to_bus(hostdata->script), flushsize); +} + +/* + * XXX - the stock NCR assembler won't output the scriptu.h file, + * which undefine's all #define'd CPP symbols from the script.h + * file, which will create problems if you use multiple scripts + * with the same symbol names. + * + * If you insist on using NCR's assembler, you could generate + * scriptu.h from script.h using something like + * + * grep #define script.h | \ + * sed 's/#define[ ][ ]*\([_a-zA-Z][_a-zA-Z0-9]*\).*$/#undefine \1/' \ + * > scriptu.h + */ + +#include "53c7xx_u.h" + +/* XXX - add alternate script handling code here */ + + +/* + * Function : static void NCR537xx_soft_reset (struct Scsi_Host *host) + * + * Purpose : perform a soft reset of the NCR53c7xx chip + * + * Inputs : host - pointer to this host adapter's structure + * + * Preconditions : NCR53c7x0_init must have been called for this + * host. + * + */ + +static void +NCR53c7x0_soft_reset (struct Scsi_Host *host) { + NCR53c7x0_local_declare(); + unsigned long flags; +#ifdef CONFIG_MVME166 + volatile unsigned long v; +#endif + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + NCR53c7x0_local_setup(host); + + save_flags(flags); + cli(); + + /* Disable scsi chip and s/w level 7 ints */ + +#ifdef CONFIG_MVME166 + v = *(volatile unsigned long *)0xfff4006c; + v &= ~0x8000; + *(volatile unsigned long *)0xfff4006c = v; + v = *(volatile unsigned long *)0xfff4202c; + v &= ~0x10; + *(volatile unsigned long *)0xfff4202c = v; +#else + /* Anything specific for your hardware? */ +#endif + + /* + * Do a soft reset of the chip so that everything is + * reinitialized to the power-on state. + * + * Basically follow the procedure outlined in the NCR53c700 + * data manual under Chapter Six, How to Use, Steps Necessary to + * Start SCRIPTS, with the exception of actually starting the + * script and setting up the synchronous transfer gunk. + */ + + /* Should we reset the scsi bus here??????????????????? */ + + NCR53c7x0_write8(ISTAT_REG_700, ISTAT_10_SRST); + NCR53c7x0_write8(ISTAT_REG_700, 0); + + /* + * saved_dcntl is set up in NCR53c7x0_init() before it is overwritten + * here. We should have some better way of working out the CF bit + * setting.. + */ + + hostdata->saved_dcntl = DCNTL_10_EA|DCNTL_10_COM; + if (hostdata->scsi_clock > 50000000) + hostdata->saved_dcntl |= DCNTL_700_CF_3; + else + if (hostdata->scsi_clock > 37500000) + hostdata->saved_dcntl |= DCNTL_700_CF_2; +#if 0 + else + /* Any clocks less than 37.5MHz? */ +#endif + + if (hostdata->options & OPTION_DEBUG_TRACE) + NCR53c7x0_write8(DCNTL_REG, hostdata->saved_dcntl | DCNTL_SSM); + else + NCR53c7x0_write8(DCNTL_REG, hostdata->saved_dcntl); +#if 0 + /* Following disables snooping - run with caches disabled at first */ + NCR53c7x0_write8(CTEST7_REG, CTEST7_10_TT1|CTEST7_STD); +#else + /* Setup CTEST7 for SC1=0, SC0=1 - sink/source data without invalidating + * cache lines. */ + NCR53c7x0_write8(CTEST7_REG, CTEST7_10_TT1|CTEST7_STD|CTEST7_10_SC0); +#endif + /* Actually burst of eight, according to my 53c710 databook */ + NCR53c7x0_write8(hostdata->dmode, DMODE_10_BL_8 | DMODE_10_FC2); + NCR53c7x0_write8(SCID_REG, 1 << host->this_id); + NCR53c7x0_write8(SBCL_REG, 0); + NCR53c7x0_write8(SCNTL1_REG, SCNTL1_ESR_700); + NCR53c7x0_write8(SCNTL0_REG, ((hostdata->options & OPTION_PARITY) ? + SCNTL0_EPC : 0) | SCNTL0_EPG_700 | SCNTL0_ARB1 | SCNTL0_ARB2); + + /* + * Enable all interrupts, except parity which we only want when + * the user requests it. + */ + + NCR53c7x0_write8(DIEN_REG, DIEN_700_BF | + DIEN_ABRT | DIEN_SSI | DIEN_SIR | DIEN_700_OPC); + + NCR53c7x0_write8(SIEN_REG_700, ((hostdata->options & OPTION_PARITY) ? + SIEN_PAR : 0) | SIEN_700_STO | SIEN_RST | SIEN_UDC | + SIEN_SGE | SIEN_MA); + +#ifdef CONFIG_MVME166 + /* Enable scsi chip and s/w level 7 ints */ + + v = *(volatile unsigned long *)0xfff40080; + v = (v & ~(0xf << 28)) | (4 << 28); + *(volatile unsigned long *)0xfff40080 = v; + v = *(volatile unsigned long *)0xfff4006c; + v |= 0x8000; + *(volatile unsigned long *)0xfff4006c = v; + v = *(volatile unsigned long *)0xfff4202c; + v = (v & ~0xff) | 0x10 | 4; + *(volatile unsigned long *)0xfff4202c = v; +#else + /* Anything needed for your hardware */ +#endif + restore_flags(flags); +} + + +/* + * Function static struct NCR53c7x0_cmd *allocate_cmd (Scsi_Cmnd *cmd) + * + * Purpose : Return the first free NCR53c7x0_cmd structure (which are + * reused in a LIFO manner to minimize cache thrashing). + * + * Side effects : If we haven't yet scheduled allocation of NCR53c7x0_cmd + * structures for this device, do so. Attempt to complete all scheduled + * allocations using kmalloc(), putting NCR53c7x0_cmd structures on + * the free list. Teach programmers not to drink and hack. + * + * Inputs : cmd - SCSI command + * + * Returns : NCR53c7x0_cmd structure allocated on behalf of cmd; + * NULL on failure. + */ + +static struct NCR53c7x0_cmd * +allocate_cmd (Scsi_Cmnd *cmd) { + struct Scsi_Host *host = cmd->host; + struct NCR53c7x0_hostdata *hostdata = + (struct NCR53c7x0_hostdata *) host->hostdata; + void *real; /* Real address */ + int size; /* Size of *tmp */ + struct NCR53c7x0_cmd *tmp; + unsigned long flags; + + if (hostdata->options & OPTION_DEBUG_ALLOCATION) + printk ("scsi%d : num_cmds = %d, can_queue = %d\n" + " target = %d, lun = %d, %s\n", + host->host_no, hostdata->num_cmds, host->can_queue, + cmd->target, cmd->lun, (hostdata->cmd_allocated[cmd->target] & + (1 << cmd->lun)) ? "already allocated" : "not allocated"); + +/* + * If we have not yet reserved commands for this I_T_L nexus, and + * the device exists (as indicated by permanent Scsi_Cmnd structures + * being allocated under 1.3.x, or being outside of scan_scsis in + * 1.2.x), do so now. + */ + if (!(hostdata->cmd_allocated[cmd->target] & (1 << cmd->lun)) && + cmd->device && cmd->device->has_cmdblocks) { + if ((hostdata->extra_allocate + hostdata->num_cmds) < host->can_queue) + hostdata->extra_allocate += host->cmd_per_lun; + hostdata->cmd_allocated[cmd->target] |= (1 << cmd->lun); + } + + for (; hostdata->extra_allocate > 0 ; --hostdata->extra_allocate, + ++hostdata->num_cmds) { + /* historically, kmalloc has returned unaligned addresses; pad so we + have enough room to ROUNDUP */ + size = hostdata->max_cmd_size + sizeof (void *); +#ifdef FORCE_DSA_ALIGNMENT + /* + * 53c710 rev.0 doesn't have an add-with-carry instruction. + * Ensure we allocate enough memory to force alignment. + */ + size += 256; +#endif +/* FIXME: for ISA bus '7xx chips, we need to or GFP_DMA in here */ + real = kmalloc (size, GFP_ATOMIC); + if (!real) { + if (hostdata->options & OPTION_DEBUG_ALLOCATION) + printk ("scsi%d : kmalloc(%d) failed\n", + host->host_no, size); + break; + } + tmp = ROUNDUP(real, void *); +#ifdef FORCE_DSA_ALIGNMENT + { + if (((u32)tmp & 0xff) > CmdPageStart) + tmp = (struct NCR53c7x0_cmd *)((u32)tmp + 255); + tmp = (struct NCR53c7x0_cmd *)(((u32)tmp & ~0xff) + CmdPageStart); +#ifdef DEBUG + printk ("scsi: size = %d, real = 0x%08x, tmp set to 0x%08x\n", + size, (u32)real, (u32)tmp); +#endif + } +#endif + tmp->real = real; + tmp->size = size; + tmp->free = ((void (*)(void *, int)) kfree); + save_flags (flags); + cli(); + tmp->next = hostdata->free; + hostdata->free = tmp; + restore_flags (flags); + } + save_flags(flags); + cli(); + tmp = (struct NCR53c7x0_cmd *) hostdata->free; + if (tmp) { + hostdata->free = tmp->next; + } + restore_flags(flags); + if (!tmp) + printk ("scsi%d : can't allocate command for target %d lun %d\n", + host->host_no, cmd->target, cmd->lun); + return tmp; +} + +/* + * Function static struct NCR53c7x0_cmd *create_cmd (Scsi_Cmnd *cmd) + * + * + * Purpose : allocate a NCR53c7x0_cmd structure, initialize it based on the + * Scsi_Cmnd structure passed in cmd, including dsa and Linux field + * initialization, and dsa code relocation. + * + * Inputs : cmd - SCSI command + * + * Returns : NCR53c7x0_cmd structure corresponding to cmd, + * NULL on failure. + */ +static struct NCR53c7x0_cmd * +create_cmd (Scsi_Cmnd *cmd) { + NCR53c7x0_local_declare(); + struct Scsi_Host *host = cmd->host; + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + struct NCR53c7x0_cmd *tmp; /* NCR53c7x0_cmd structure for this command */ + int datain, /* Number of instructions per phase */ + dataout; + int data_transfer_instructions, /* Count of dynamic instructions */ + i; /* Counter */ + u32 *cmd_datain, /* Address of datain/dataout code */ + *cmd_dataout; /* Incremented as we assemble */ +#ifdef notyet + unsigned char *msgptr; /* Current byte in select message */ + int msglen; /* Length of whole select message */ +#endif + unsigned long flags; + u32 exp_select_indirect; /* Used in sanity check */ + NCR53c7x0_local_setup(cmd->host); + + if (!(tmp = allocate_cmd (cmd))) + return NULL; + + + /* + * Decide whether we need to generate commands for DATA IN, + * DATA OUT, neither, or both based on the SCSI command + */ + + switch (cmd->cmnd[0]) { + /* These commands do DATA IN */ + case INQUIRY: + case MODE_SENSE: + case READ_6: + case READ_10: + case READ_CAPACITY: + case REQUEST_SENSE: + datain = 2 * (cmd->use_sg ? cmd->use_sg : 1) + 3; + dataout = 0; + break; + /* These commands do DATA OUT */ + case MODE_SELECT: + case WRITE_6: + case WRITE_10: + case START_STOP: /* also SCAN, which may do DATA OUT */ +#if 0 + printk("scsi%d : command is ", host->host_no); + print_command(cmd->cmnd); +#endif +#if 0 + printk ("scsi%d : %d scatter/gather segments\n", host->host_no, + cmd->use_sg); +#endif + datain = 0; + dataout = 2 * (cmd->use_sg ? cmd->use_sg : 1) + 3; +#if 0 + hostdata->options |= OPTION_DEBUG_INTR; +#endif + break; + /* + * These commands do no data transfer, we should force an + * interrupt if a data phase is attempted on them. + */ + case TEST_UNIT_READY: + datain = dataout = 0; + break; + /* + * We don't know about these commands, so generate code to handle + * both DATA IN and DATA OUT phases. + */ + default: + datain = dataout = 2 * (cmd->use_sg ? cmd->use_sg : 1) + 3; + } + + /* + * New code : so that active pointers work correctly regardless + * of where the saved data pointer is at, we want to immediately + * enter the dynamic code after selection, and on a non-data + * phase perform a CALL to the non-data phase handler, with + * returns back to this address. + * + * If a phase mismatch is encountered in the middle of a + * Block MOVE instruction, we want to _leave_ that instruction + * unchanged as the current case is, modify a temporary buffer, + * and point the active pointer (TEMP) at that. + * + * Furthermore, we want to implement a saved data pointer, + * set by the SAVE_DATA_POINTERs message. + * + * So, the data transfer segments will change to + * CALL data_transfer, WHEN NOT data phase + * MOVE x, x, WHEN data phase + * ( repeat ) + * JUMP other_transfer + */ + + data_transfer_instructions = datain + dataout; + + /* + * When we perform a request sense, we overwrite various things, + * including the data transfer code. Make sure we have enough + * space to do that. + */ + + if (data_transfer_instructions < 2) + data_transfer_instructions = 2; + + + /* + * The saved data pointer is set up so that a RESTORE POINTERS message + * will start the data transfer over at the begining. + */ + + tmp->saved_data_pointer = virt_to_bus (hostdata->script) + + hostdata->E_data_transfer; + + /* + * Initialize Linux specific fields. + */ + + tmp->cmd = cmd; + tmp->next = NULL; + tmp->flags = 0; + tmp->dsa_next_addr = virt_to_bus(tmp->dsa) + hostdata->dsa_next - + hostdata->dsa_start; + tmp->dsa_addr = virt_to_bus(tmp->dsa) - hostdata->dsa_start; + + /* + * Calculate addresses of dynamic code to fill in DSA + */ + + tmp->data_transfer_start = tmp->dsa + (hostdata->dsa_end - + hostdata->dsa_start) / sizeof(u32); + tmp->data_transfer_end = tmp->data_transfer_start + + 2 * data_transfer_instructions; + + cmd_datain = datain ? tmp->data_transfer_start : NULL; + cmd_dataout = dataout ? (datain ? cmd_datain + 2 * datain : tmp-> + data_transfer_start) : NULL; + + /* + * Fill in the NCR53c7x0_cmd structure as follows + * dsa, with fixed up DSA code + * datain code + * dataout code + */ + + /* Copy template code into dsa and perform all necessary fixups */ + if (hostdata->dsa_fixup) + hostdata->dsa_fixup(tmp); + + patch_dsa_32(tmp->dsa, dsa_next, 0, 0); + patch_dsa_32(tmp->dsa, dsa_cmnd, 0, virt_to_bus(cmd)); + +flush_cache_all(); +cache_push(virt_to_bus(hostdata->script), flushsize); +cache_clear(virt_to_bus(hostdata->script), flushsize); + + if (hostdata->options & OPTION_DEBUG_SYNCHRONOUS) { + + exp_select_indirect = ((1 << cmd->target) << 16) | + (hostdata->sync[cmd->target].sxfer_sanity << 8); + + if (hostdata->sync[cmd->target].select_indirect != + exp_select_indirect) { + printk ("scsi%d : sanity check failed select_indirect=0x%x\n", + host->host_no, hostdata->sync[cmd->target].select_indirect); + FATAL(host); + + } + } + + patch_dsa_32(tmp->dsa, dsa_select, 0, + hostdata->sync[cmd->target].select_indirect); + + +flush_cache_all(); +cache_push(virt_to_bus(hostdata->script), flushsize); +cache_clear(virt_to_bus(hostdata->script), flushsize); + + /* + * Right now, we'll do the WIDE and SYNCHRONOUS negotiations on + * different commands; although it should be trivial to do them + * both at the same time. + */ + if (hostdata->initiate_wdtr & (1 << cmd->target)) { + memcpy ((void *) (tmp->select + 1), (void *) wdtr_message, + sizeof(wdtr_message)); + patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1 + sizeof(wdtr_message)); + save_flags(flags); + cli(); + hostdata->initiate_wdtr &= ~(1 << cmd->target); + restore_flags(flags); + } else if (hostdata->initiate_sdtr & (1 << cmd->target)) { + memcpy ((void *) (tmp->select + 1), (void *) sdtr_message, + sizeof(sdtr_message)); + patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1 + sizeof(sdtr_message)); + tmp->flags |= CMD_FLAG_SDTR; + save_flags(flags); + cli(); + hostdata->initiate_sdtr &= ~(1 << cmd->target); + restore_flags(flags); + + } +#if 1 + else if (!(hostdata->talked_to & (1 << cmd->target)) && + !(hostdata->options & OPTION_NO_ASYNC)) { + +flush_cache_all(); +cache_push(virt_to_bus(hostdata->script), flushsize); +cache_clear(virt_to_bus(hostdata->script), flushsize); + + memcpy ((void *) (tmp->select + 1), (void *) async_message, + sizeof(async_message)); + patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1 + sizeof(async_message)); + tmp->flags |= CMD_FLAG_SDTR; + } +#endif + else + patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1); + +flush_cache_all(); +cache_push(virt_to_bus(hostdata->script), flushsize); +cache_clear(virt_to_bus(hostdata->script), flushsize); + + hostdata->talked_to |= (1 << cmd->target); + tmp->select[0] = (hostdata->options & OPTION_DISCONNECT) ? + IDENTIFY (1, cmd->lun) : IDENTIFY (0, cmd->lun); + patch_dsa_32(tmp->dsa, dsa_msgout, 1, virt_to_bus(tmp->select)); + patch_dsa_32(tmp->dsa, dsa_cmdout, 0, cmd->cmd_len); + patch_dsa_32(tmp->dsa, dsa_cmdout, 1, virt_to_bus(cmd->cmnd)); + patch_dsa_32(tmp->dsa, dsa_dataout, 0, cmd_dataout ? + virt_to_bus (cmd_dataout) + : virt_to_bus (hostdata->script) + hostdata->E_other_transfer); + patch_dsa_32(tmp->dsa, dsa_datain, 0, cmd_datain ? + virt_to_bus (cmd_datain) + : virt_to_bus (hostdata->script) + hostdata->E_other_transfer); + /* + * XXX - need to make endian aware, should use separate variables + * for both status and message bytes. + */ + patch_dsa_32(tmp->dsa, dsa_msgin, 0, 1); +/* + * FIXME : these only works for little endian. We probably want to + * provide message and status fields in the NCR53c7x0_cmd + * structure, and assign them to cmd->result when we're done. + */ +#ifdef BIG_ENDIAN + patch_dsa_32(tmp->dsa, dsa_msgin, 1, virt_to_bus(&cmd->result) + 2); + patch_dsa_32(tmp->dsa, dsa_status, 0, 1); + patch_dsa_32(tmp->dsa, dsa_status, 1, virt_to_bus(&cmd->result) + 3); +#else + patch_dsa_32(tmp->dsa, dsa_msgin, 1, virt_to_bus(&cmd->result) + 1); + patch_dsa_32(tmp->dsa, dsa_status, 0, 1); + patch_dsa_32(tmp->dsa, dsa_status, 1, virt_to_bus(&cmd->result)); +#endif + patch_dsa_32(tmp->dsa, dsa_msgout_other, 0, 1); + patch_dsa_32(tmp->dsa, dsa_msgout_other, 1, + virt_to_bus(&(hostdata->NCR53c7xx_msg_nop))); + + /* + * Generate code for zero or more of the DATA IN, DATA OUT phases + * in the format + * + * CALL data_transfer, WHEN NOT phase + * MOVE first buffer length, first buffer address, WHEN phase + * ... + * MOVE last buffer length, last buffer address, WHEN phase + * JUMP other_transfer + */ + +/* + * See if we're getting to data transfer by generating an unconditional + * interrupt. + */ +#if 0 + if (datain) { + cmd_datain[0] = 0x98080000; + cmd_datain[1] = 0x03ffd00d; + cmd_datain += 2; + } +#endif + +/* + * XXX - I'm undecided whether all of this nonsense is faster + * in the long run, or whether I should just go and implement a loop + * on the NCR chip using table indirect mode? + * + * In any case, this is how it _must_ be done for 53c700/700-66 chips, + * so this stays even when we come up with something better. + * + * When we're limited to 1 simultaneous command, no overlapping processing, + * we're seeing 630K/sec, with 7% CPU usage on a slow Syquest 45M + * drive. + * + * Not bad, not good. We'll see. + */ + +flush_cache_all(); +cache_push(virt_to_bus(hostdata->script), flushsize); +cache_clear(virt_to_bus(hostdata->script), flushsize); + + + for (i = 0; cmd->use_sg ? (i < cmd->use_sg) : !i; cmd_datain += 4, + cmd_dataout += 4, ++i) { + u32 buf = cmd->use_sg ? + virt_to_bus(((struct scatterlist *)cmd->buffer)[i].address) : + virt_to_bus(cmd->request_buffer); + u32 count = cmd->use_sg ? + ((struct scatterlist *)cmd->buffer)[i].length : + cmd->request_bufflen; + + if (datain) { + /* CALL other_in, WHEN NOT DATA_IN */ + cmd_datain[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_CALL | + DCMD_TCI_IO) << 24) | + DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE; + cmd_datain[1] = virt_to_bus (hostdata->script) + + hostdata->E_other_in; + /* MOVE count, buf, WHEN DATA_IN */ + cmd_datain[2] = ((DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I | DCMD_BMI_IO) + << 24) | count; + cmd_datain[3] = buf; +#if 0 + print_insn (host, cmd_datain, "dynamic ", 1); + print_insn (host, cmd_datain + 2, "dynamic ", 1); +#endif + } + if (dataout) { + /* CALL other_out, WHEN NOT DATA_OUT */ + cmd_dataout[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_CALL) << 24) | + DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE; + cmd_dataout[1] = virt_to_bus(hostdata->script) + + hostdata->E_other_out; + /* MOVE count, buf, WHEN DATA+OUT */ + cmd_dataout[2] = ((DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I) << 24) + | count; + cmd_dataout[3] = buf; +#if 0 + print_insn (host, cmd_dataout, "dynamic ", 1); + print_insn (host, cmd_dataout + 2, "dynamic ", 1); +#endif + } + } + +flush_cache_all(); +cache_push(virt_to_bus(hostdata->script), flushsize); +cache_clear(virt_to_bus(hostdata->script), flushsize); + + + /* + * Install JUMP instructions after the data transfer routines to return + * control to the do_other_transfer routines. + */ + + + if (datain) { + cmd_datain[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP) << 24) | + DBC_TCI_TRUE; + cmd_datain[1] = virt_to_bus(hostdata->script) + + hostdata->E_other_transfer; +#if 0 + print_insn (host, cmd_datain, "dynamic jump ", 1); +#endif + cmd_datain += 2; + } +#if 0 + if (datain) { + cmd_datain[0] = 0x98080000; + cmd_datain[1] = 0x03ffdeed; + cmd_datain += 2; + } +#endif + if (dataout) { + cmd_dataout[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP) << 24) | + DBC_TCI_TRUE; + cmd_dataout[1] = virt_to_bus(hostdata->script) + + hostdata->E_other_transfer; +#if 0 + print_insn (host, cmd_dataout, "dynamic jump ", 1); +#endif + cmd_dataout += 2; + } + +flush_cache_all(); +cache_push(virt_to_bus(hostdata->script), flushsize); +cache_clear(virt_to_bus(hostdata->script), flushsize); + + return tmp; +} + +/* + * Function : int NCR53c7xx_queue_command (Scsi_Cmnd *cmd, + * void (*done)(Scsi_Cmnd *)) + * + * Purpose : enqueues a SCSI command + * + * Inputs : cmd - SCSI command, done - function called on completion, with + * a pointer to the command descriptor. + * + * Returns : 0 + * + * Side effects : + * cmd is added to the per instance driver issue_queue, with major + * twiddling done to the host specific fields of cmd. If the + * process_issue_queue coroutine isn't running, it is restarted. + * + * NOTE : we use the host_scribble field of the Scsi_Cmnd structure to + * hold our own data, and pervert the ptr field of the SCp field + * to create a linked list. + */ + +int +NCR53c7xx_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *)) { + struct Scsi_Host *host = cmd->host; + struct NCR53c7x0_hostdata *hostdata = + (struct NCR53c7x0_hostdata *) host->hostdata; + unsigned long flags; + Scsi_Cmnd *tmp; + + cmd->scsi_done = done; + cmd->host_scribble = NULL; + cmd->SCp.ptr = NULL; + cmd->SCp.buffer = NULL; + +#ifdef VALID_IDS + /* Ignore commands on invalid IDs */ + if (!hostdata->valid_ids[cmd->target]) { + printk("scsi%d : ignoring target %d lun %d\n", host->host_no, + cmd->target, cmd->lun); + cmd->result = (DID_BAD_TARGET << 16); + done(cmd); + return 0; + } +#endif + + save_flags(flags); + cli(); + if ((hostdata->options & (OPTION_DEBUG_INIT_ONLY|OPTION_DEBUG_PROBE_ONLY)) + || ((hostdata->options & OPTION_DEBUG_TARGET_LIMIT) && + !(hostdata->debug_lun_limit[cmd->target] & (1 << cmd->lun))) +#ifdef LINUX_1_2 + || cmd->target > 7 +#else + || cmd->target > host->max_id +#endif + || cmd->target == host->this_id + || hostdata->state == STATE_DISABLED) { + printk("scsi%d : disabled or bad target %d lun %d\n", host->host_no, + cmd->target, cmd->lun); + cmd->result = (DID_BAD_TARGET << 16); + done(cmd); + restore_flags (flags); + return 0; + } + + if ((hostdata->options & OPTION_DEBUG_NCOMMANDS_LIMIT) && + (hostdata->debug_count_limit == 0)) { + printk("scsi%d : maximum commands exceeded\n", host->host_no); + cmd->result = (DID_BAD_TARGET << 16); + done(cmd); + restore_flags (flags); + return 0; + } + + if (hostdata->options & OPTION_DEBUG_READ_ONLY) { + switch (cmd->cmnd[0]) { + case WRITE_6: + case WRITE_10: + printk("scsi%d : WRITE attempted with NO_WRITE debugging flag set\n", + host->host_no); + cmd->result = (DID_BAD_TARGET << 16); + done(cmd); + restore_flags (flags); + return 0; + } + } + + if ((hostdata->options & OPTION_DEBUG_TARGET_LIMIT) && + hostdata->debug_count_limit != -1) + --hostdata->debug_count_limit; + + cmd->result = 0xffff; /* The NCR will overwrite message + and status with valid data */ + cmd->host_scribble = (unsigned char *) tmp = create_cmd (cmd); + + /* + * REQUEST SENSE commands are inserted at the head of the queue + * so that we do not clear the contingent allegiance condition + * they may be looking at. + */ + + if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) { + cmd->SCp.ptr = (unsigned char *) hostdata->issue_queue; + hostdata->issue_queue = cmd; + } else { + for (tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp->SCp.ptr; + tmp = (Scsi_Cmnd *) tmp->SCp.ptr); + tmp->SCp.ptr = (unsigned char *) cmd; + } + restore_flags (flags); + run_process_issue_queue(); + return 0; +} + +/* + * Function : void to_schedule_list (struct Scsi_Host *host, + * struct NCR53c7x0_hostdata * hostdata, Scsi_Cmnd *cmd) + * + * Purpose : takes a SCSI command which was just removed from the + * issue queue, and deals with it by inserting it in the first + * free slot in the schedule list or by terminating it immediately. + * + * Inputs : + * host - SCSI host adapter; hostdata - hostdata structure for + * this adapter; cmd - a pointer to the command; should have + * the host_scribble field initialized to point to a valid + * + * Side effects : + * cmd is added to the per instance schedule list, with minor + * twiddling done to the host specific fields of cmd. + * + */ + +static __inline__ void +to_schedule_list (struct Scsi_Host *host, struct NCR53c7x0_hostdata *hostdata, + struct NCR53c7x0_cmd *cmd) { + NCR53c7x0_local_declare(); + Scsi_Cmnd *tmp = cmd->cmd; + unsigned long flags; + /* dsa start is negative, so subtraction is used */ + volatile u32 *ncrcurrent; + + int i; + NCR53c7x0_local_setup(host); +#if 0 + printk("scsi%d : new dsa is 0x%lx (virt 0x%p)\n", host->host_no, + virt_to_bus(hostdata->dsa), hostdata->dsa); +#endif + + save_flags(flags); + cli(); + + /* + * Work around race condition : if an interrupt fired and we + * got disabled forget about this command. + */ + + if (hostdata->state == STATE_DISABLED) { + printk("scsi%d : driver disabled\n", host->host_no); + tmp->result = (DID_BAD_TARGET << 16); + cmd->next = (struct NCR53c7x0_cmd *) hostdata->free; + hostdata->free = cmd; + tmp->scsi_done(tmp); + restore_flags (flags); + return; + } + + for (i = host->can_queue, ncrcurrent = hostdata->schedule; + i > 0 && ncrcurrent[0] != hostdata->NOP_insn; + --i, ncrcurrent += 2 /* JUMP instructions are two words */); + + +flush_cache_all(); +cache_push(virt_to_bus(hostdata->script), flushsize); +cache_clear(virt_to_bus(hostdata->script), flushsize); + + if (i > 0) { + ++hostdata->busy[tmp->target][tmp->lun]; + cmd->next = hostdata->running_list; + hostdata->running_list = cmd; + + /* Restore this instruction to a NOP once the command starts */ + cmd->dsa [(hostdata->dsa_jump_dest - hostdata->dsa_start) / + sizeof(u32)] = (u32) virt_to_bus ((void *)ncrcurrent); + /* Replace the current jump operand. */ + ncrcurrent[1] = + virt_to_bus ((void *) cmd->dsa) + hostdata->E_dsa_code_begin - + hostdata->E_dsa_code_template; + /* Replace the NOP instruction with a JUMP */ + ncrcurrent[0] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24) | + DBC_TCI_TRUE; + } else { + printk ("scsi%d: no free slot\n", host->host_no); + disable(host); + tmp->result = (DID_ERROR << 16); + cmd->next = (struct NCR53c7x0_cmd *) hostdata->free; + hostdata->free = cmd; + tmp->scsi_done(tmp); + restore_flags (flags); + return; + } + + cache_push(virt_to_bus(cmd->dsa), hostdata->dsa_len); + cache_push(virt_to_bus(ncrcurrent), sizeof(ncrcurrent)); + + /* + * If the NCR chip is in an idle state, start it running the scheduler + * immediately. Otherwise, signal the chip to jump to schedule as + * soon as it is idle. + */ + + if (hostdata->idle) { + hostdata->idle = 0; + hostdata->state = STATE_RUNNING; + NCR53c7x0_write32 (DSP_REG, virt_to_bus ((void *)hostdata->schedule)); + if (hostdata->options & OPTION_DEBUG_TRACE) + NCR53c7x0_write8 (DCNTL_REG, hostdata->saved_dcntl | + DCNTL_SSM | DCNTL_STD); + } else { + NCR53c7x0_write8(hostdata->istat, ISTAT_10_SIGP); + } + + restore_flags(flags); +} + +/* + * Function : busyp (struct Scsi_Host *host, struct NCR53c7x0_hostdata + * *hostdata, Scsi_Cmnd *cmd) + * + * Purpose : decide if we can pass the given SCSI command on to the + * device in question or not. + * + * Returns : non-zero when we're busy, 0 when we aren't. + */ + +static __inline__ int +busyp (struct Scsi_Host *host, struct NCR53c7x0_hostdata *hostdata, + Scsi_Cmnd *cmd) { + /* FIXME : in the future, this needs to accommodate SCSI-II tagged + queuing, and we may be able to play with fairness here a bit. + */ + return hostdata->busy[cmd->target][cmd->lun]; +} + +/* + * Function : process_issue_queue (void) + * + * Purpose : transfer commands from the issue queue to NCR start queue + * of each NCR53c7/8xx in the system, avoiding kernel stack + * overflows when the scsi_done() function is invoked recursively. + * + * NOTE : process_issue_queue exits with interrupts *disabled*, so the + * caller must reenable them if it desires. + * + * NOTE : process_issue_queue should be called from both + * NCR53c7x0_queue_command() and from the interrupt handler + * after command completion in case NCR53c7x0_queue_command() + * isn't invoked again but we've freed up resources that are + * needed. + */ + +static void +process_issue_queue (unsigned long flags) { + Scsi_Cmnd *tmp, *prev; + struct Scsi_Host *host; + struct NCR53c7x0_hostdata *hostdata; + int done; + + /* + * We run (with interrupts disabled) until we're sure that none of + * the host adapters have anything that can be done, at which point + * we set process_issue_queue_running to 0 and exit. + * + * Interrupts are enabled before doing various other internal + * instructions, after we've decided that we need to run through + * the loop again. + * + */ + + do { + cli(); /* Freeze request queues */ + done = 1; + for (host = first_host; host && host->hostt == the_template; + host = host->next) { + hostdata = (struct NCR53c7x0_hostdata *) host->hostdata; + cli(); + if (hostdata->issue_queue) { + if (hostdata->state == STATE_DISABLED) { + tmp = (Scsi_Cmnd *) hostdata->issue_queue; + hostdata->issue_queue = (Scsi_Cmnd *) tmp->SCp.ptr; + tmp->result = (DID_BAD_TARGET << 16); + if (tmp->host_scribble) { + ((struct NCR53c7x0_cmd *)tmp->host_scribble)->next = + hostdata->free; + hostdata->free = + (struct NCR53c7x0_cmd *)tmp->host_scribble; + tmp->host_scribble = NULL; + } + tmp->scsi_done (tmp); + done = 0; + } else + for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, + prev = NULL; tmp; prev = tmp, tmp = (Scsi_Cmnd *) + tmp->SCp.ptr) + if (!tmp->host_scribble || + !busyp (host, hostdata, tmp)) { + if (prev) + prev->SCp.ptr = tmp->SCp.ptr; + else + hostdata->issue_queue = (Scsi_Cmnd *) + tmp->SCp.ptr; + tmp->SCp.ptr = NULL; + if (tmp->host_scribble) { + if (hostdata->options & OPTION_DEBUG_QUEUES) + printk ("scsi%d : moving command for target %d lun %d to start list\n", + host->host_no, tmp->target, tmp->lun); + + + to_schedule_list (host, hostdata, + (struct NCR53c7x0_cmd *) + tmp->host_scribble); + } else { + if (((tmp->result & 0xff) == 0xff) || + ((tmp->result & 0xff00) == 0xff00)) { + printk ("scsi%d : danger Will Robinson!\n", + host->host_no); + tmp->result = DID_ERROR << 16; + disable (host); + } + tmp->scsi_done(tmp); + } + done = 0; + } /* if target/lun is not busy */ + } /* if hostdata->issue_queue */ + if (!done) + restore_flags (flags); + } /* for host */ + } while (!done); + process_issue_queue_running = 0; +} + +/* + * Function : static void intr_scsi (struct Scsi_Host *host, + * struct NCR53c7x0_cmd *cmd) + * + * Purpose : handle all SCSI interrupts, indicated by the setting + * of the SIP bit in the ISTAT register. + * + * Inputs : host, cmd - host and NCR command causing the interrupt, cmd + * may be NULL. + */ + +static void +intr_scsi (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) { + NCR53c7x0_local_declare(); + struct NCR53c7x0_hostdata *hostdata = + (struct NCR53c7x0_hostdata *) host->hostdata; + unsigned char sstat0_sist0, sist1, /* Registers */ + fatal; /* Did a fatal interrupt + occur ? */ + + NCR53c7x0_local_setup(host); + + fatal = 0; + + sstat0_sist0 = NCR53c7x0_read8(SSTAT0_REG); + sist1 = 0; + + if (hostdata->options & OPTION_DEBUG_INTR) + printk ("scsi%d : SIST0 0x%0x, SIST1 0x%0x\n", host->host_no, + sstat0_sist0, sist1); + + /* 250ms selection timeout */ + if (sstat0_sist0 & SSTAT0_700_STO) { + fatal = 1; + if (hostdata->options & OPTION_DEBUG_INTR) { + printk ("scsi%d : Selection Timeout\n", host->host_no); + if (cmd) { + printk("scsi%d : target %d, lun %d, command ", + host->host_no, cmd->cmd->target, cmd->cmd->lun); + print_command (cmd->cmd->cmnd); + printk("scsi%d : dsp = 0x%x (virt 0x%p)\n", host->host_no, + NCR53c7x0_read32(DSP_REG), + bus_to_virt(NCR53c7x0_read32(DSP_REG))); + } else { + printk("scsi%d : no command\n", host->host_no); + } + } +/* + * XXX - question : how do we want to handle the Illegal Instruction + * interrupt, which may occur before or after the Selection Timeout + * interrupt? + */ + + if (1) { + hostdata->idle = 1; + hostdata->expecting_sto = 0; + + if (hostdata->test_running) { + hostdata->test_running = 0; + hostdata->test_completed = 3; + } else if (cmd) { + abnormal_finished(cmd, DID_BAD_TARGET << 16); + } +#if 0 + hostdata->intrs = 0; +#endif + } + } + +/* + * FIXME : in theory, we can also get a UDC when a STO occurs. + */ + if (sstat0_sist0 & SSTAT0_UDC) { + fatal = 1; + if (cmd) { + printk("scsi%d : target %d lun %d unexpected disconnect\n", + host->host_no, cmd->cmd->target, cmd->cmd->lun); + print_lots (host); + abnormal_finished(cmd, DID_ERROR << 16); + } else + printk("scsi%d : unexpected disconnect (no command)\n", + host->host_no); + + hostdata->dsp = (u32 *) hostdata->schedule; + hostdata->dsp_changed = 1; + } + + /* SCSI PARITY error */ + if (sstat0_sist0 & SSTAT0_PAR) { + fatal = 1; + if (cmd && cmd->cmd) { + printk("scsi%d : target %d lun %d parity error.\n", + host->host_no, cmd->cmd->target, cmd->cmd->lun); + abnormal_finished (cmd, DID_PARITY << 16); + } else + printk("scsi%d : parity error\n", host->host_no); + /* Should send message out, parity error */ + + /* XXX - Reduce synchronous transfer rate! */ + hostdata->dsp = hostdata->script + hostdata->E_initiator_abort / + sizeof(u32); + hostdata->dsp_changed = 1; + /* SCSI GROSS error */ + } + + if (sstat0_sist0 & SSTAT0_SGE) { + fatal = 1; + printk("scsi%d : gross error, saved2_dsa = 0x%x\n", host->host_no, + (unsigned int)hostdata->saved2_dsa); + print_lots (host); + + /* + * A SCSI gross error may occur when we have + * + * - A synchronous offset which causes the SCSI FIFO to be overwritten. + * + * - A REQ which causes the maximum synchronous offset programmed in + * the SXFER register to be exceeded. + * + * - A phase change with an outstanding synchronous offset. + * + * - Residual data in the synchronous data FIFO, with a transfer + * other than a synchronous receive is started.$# + */ + + + /* XXX Should deduce synchronous transfer rate! */ + hostdata->dsp = hostdata->script + hostdata->E_initiator_abort / + sizeof(u32); + hostdata->dsp_changed = 1; + /* Phase mismatch */ + } + + if (sstat0_sist0 & SSTAT0_MA) { + fatal = 1; + if (hostdata->options & OPTION_DEBUG_INTR) + printk ("scsi%d : SSTAT0_MA\n", host->host_no); + intr_phase_mismatch (host, cmd); + } + +#if 0 + if (sstat0_sist0 & SIST0_800_RSL) + printk ("scsi%d : Oh no Mr. Bill!\n", host->host_no); +#endif + +/* + * If a fatal SCSI interrupt occurs, we must insure that the DMA and + * SCSI FIFOs were flushed. + */ + + if (fatal) { + if (!hostdata->dstat_valid) { + hostdata->dstat = NCR53c7x0_read8(DSTAT_REG); + hostdata->dstat_valid = 1; + } + + if (!(hostdata->dstat & DSTAT_DFE)) { + printk ("scsi%d : DMA FIFO not empty\n", host->host_no); + /* + * Really need to check this code for 710 RGH. + * Havn't seen any problems, but maybe we should FLUSH before + * clearing sometimes. + */ + NCR53c7x0_write8 (CTEST8_REG, CTEST8_10_CLF); + while (NCR53c7x0_read8 (CTEST8_REG) & CTEST8_10_CLF) + ; + hostdata->dstat |= DSTAT_DFE; + } + } +} + +#ifdef CYCLIC_TRACE + +/* + * The following implements a cyclic log of instructions executed, if you turn + * TRACE on. It will also print the log for you. Very useful when debugging + * 53c710 support, possibly not really needed any more. + */ + +u32 insn_log[4096]; +u32 insn_log_index = 0; + +void log1 (u32 i) +{ + insn_log[insn_log_index++] = i; + if (insn_log_index == 4096) + insn_log_index = 0; +} + +void log_insn (u32 *ip) +{ + log1 ((u32)ip); + log1 (*ip); + log1 (*(ip+1)); + if (((*ip >> 24) & DCMD_TYPE_MASK) == DCMD_TYPE_MMI) + log1 (*(ip+2)); +} + +void dump_log(void) +{ + int cnt = 0; + int i = insn_log_index; + int size; + struct Scsi_Host *host = first_host; + + while (cnt < 4096) { + printk ("%08x (+%6x): ", insn_log[i], (insn_log[i] - (u32)&(((struct NCR53c7x0_hostdata *)host->hostdata)->script))/4); + if (++i == 4096) + i = 0; + cnt++; + if (((insn_log[i] >> 24) & DCMD_TYPE_MASK) == DCMD_TYPE_MMI) + size = 3; + else + size = 2; + while (size--) { + printk ("%08x ", insn_log[i]); + if (++i == 4096) + i = 0; + cnt++; + } + printk ("\n"); + } +} +#endif + +/* + * Function : static void NCR53c7x0_intr (int irq, void *dev_id, struct pt_regs * regs) + * + * Purpose : handle NCR53c7x0 interrupts for all NCR devices sharing + * the same IRQ line. + * + * Inputs : Since we're using the SA_INTERRUPT interrupt handler + * semantics, irq indicates the interrupt which invoked + * this handler. + * + * On the 710 we simualte an INTFLY with a script interrupt, and the + * script interrupt handler will call back to this function. + */ + +void +NCR53c7x0_intr (int irq, void *dev_id, struct pt_regs * regs) { + NCR53c7x0_local_declare(); + struct Scsi_Host *host; /* Host we are looking at */ + unsigned char istat; /* Values of interrupt regs */ + struct NCR53c7x0_hostdata *hostdata; /* host->hostdata */ + struct NCR53c7x0_cmd *cmd, /* command which halted */ + **cmd_prev_ptr; + u32 *dsa; /* DSA */ + int done = 1; /* Indicates when handler + should terminate */ + int interrupted = 0; /* This HA generated + an interrupt */ + int have_intfly; /* Don't print warning + messages when we stack + INTFLYs */ + unsigned long flags; + +#ifdef NCR_DEBUG + char buf[80]; /* Debugging sprintf buffer */ + size_t buflen; /* Length of same */ +#endif + +#if defined(CONFIG_AMIGA) + custom.intena = IF_PORTS; +#endif + + do { + done = 1; + for (host = first_host; host; host = host->next) + if (host->hostt == the_template +#if defined(MVME166_INTFLY) + /* We have two different interrupts pointing + * at this routine, so remove this check */ +#else + && host->irq == irq +#endif + ) { + NCR53c7x0_local_setup(host); + + hostdata = (struct NCR53c7x0_hostdata *) host->hostdata; + hostdata->dsp_changed = 0; + interrupted = 0; + have_intfly = 0; + + do { + hostdata->dstat_valid = 0; + interrupted = 0; + /* + * Only read istat once, since reading it again will unstack + * interrupts? + */ + istat = NCR53c7x0_read8(hostdata->istat); + + if ((hostdata->options & OPTION_INTFLY) && +#ifdef MVME166_INTFLY + /* the bit is set which indicates an on-the-fly int */ + (*(volatile unsigned long *)0xfff40068 & 0x8000)) +#else + (hostdata->emulated_intfly != 0)) +#endif + { + char search_found = 0; /* Got at least one ? */ + done = 0; + interrupted = 1; + +#ifdef MVME166_INTFLY + /* clear the INTFLY bit */ + *(volatile unsigned long *)0xfff40074 = 0x8000; +#endif + + if (hostdata->options & OPTION_DEBUG_INTR) + printk ("scsi%d : INTFLY\n", host->host_no); + + /* + * Traverse our list of running commands, and look + * for those with valid (non-0xff ff) status and message + * bytes encoded in the result which signify command + * completion. + */ + + + save_flags(flags); + cli(); +restart: + for (cmd_prev_ptr = (struct NCR53c7x0_cmd **) + &(hostdata->running_list), cmd = + (struct NCR53c7x0_cmd *) hostdata->running_list; cmd ; + cmd_prev_ptr = (struct NCR53c7x0_cmd **) &(cmd->next), + cmd = (struct NCR53c7x0_cmd *) cmd->next) { + Scsi_Cmnd *tmp; + + if (!cmd) { + printk("scsi%d : very weird.\n", host->host_no); + break; + } + + if (!(tmp = cmd->cmd)) { + printk("scsi%d : weird. NCR53c7x0_cmd has no Scsi_Cmnd\n", + host->host_no); + continue; + } +#if 0 + printk ("scsi%d : looking at result of 0x%x\n", + host->host_no, cmd->cmd->result); +#endif + + if (((tmp->result & 0xff) == 0xff) || + ((tmp->result & 0xff00) == 0xff00)) + continue; + + search_found = 1; + + /* Important - remove from list _before_ done is called */ + if (cmd_prev_ptr) + *cmd_prev_ptr = (struct NCR53c7x0_cmd *) cmd->next; + + --hostdata->busy[tmp->target][tmp->lun]; + cmd->next = hostdata->free; + hostdata->free = cmd; + + tmp->host_scribble = NULL; + + if (hostdata->options & OPTION_DEBUG_INTR) { + printk ("scsi%d : command complete : pid %lu, id %d,lun %d result 0x%x ", + host->host_no, tmp->pid, tmp->target, tmp->lun, tmp->result); + print_command (tmp->cmnd); + } + +#if 0 + hostdata->options &= ~OPTION_DEBUG_INTR; +#endif + tmp->scsi_done(tmp); + goto restart; + + } + restore_flags(flags); + + if (!search_found && !have_intfly) { + printk ("scsi%d : WARNING : INTFLY with no completed commands.\n", + host->host_no); + } else if (!have_intfly) { + have_intfly = 1; + run_process_issue_queue(); + } + } + + if (hostdata->emulated_intfly) + { + hostdata->emulated_intfly = 0; + return; + } + + if (istat & (ISTAT_SIP|ISTAT_DIP)) { + done = 0; + interrupted = 1; + hostdata->state = STATE_HALTED; + + if (NCR53c7x0_read8 (SSTAT2_REG) & SSTAT2_FF_MASK) + printk ("scsi%d : SCSI FIFO not empty\n", + host->host_no); + + /* + * NCR53c700 and NCR53c700-66 change the current SCSI + * process, hostdata->ncrcurrent, in the Linux driver so + * cmd = hostdata->ncrcurrent. + * + * With other chips, we must look through the commands + * executing and find the command structure which + * corresponds to the DSA register. + */ + + if (hostdata->options & OPTION_700) { + cmd = (struct NCR53c7x0_cmd *) hostdata->ncrcurrent; + } else { + dsa = bus_to_virt(NCR53c7x0_read32(DSA_REG)); + for (cmd = (struct NCR53c7x0_cmd *) + hostdata->running_list; cmd && + (dsa + (hostdata->dsa_start / sizeof(u32))) != + cmd->dsa; + cmd = (struct NCR53c7x0_cmd *)(cmd->next)); + } + if (hostdata->options & OPTION_DEBUG_INTR) { + if (cmd) { + printk("scsi%d : interrupt for pid %lu, id %d, lun %d ", + host->host_no, cmd->cmd->pid, (int) cmd->cmd->target, + (int) cmd->cmd->lun); + print_command (cmd->cmd->cmnd); + } else { + printk("scsi%d : no active command\n", host->host_no); + } + } + + if (istat & ISTAT_SIP) { + if (hostdata->options & OPTION_DEBUG_INTR) + printk ("scsi%d : ISTAT_SIP\n", host->host_no); + intr_scsi (host, cmd); + } + + if (istat & ISTAT_DIP) { + if (hostdata->options & OPTION_DEBUG_INTR) + printk ("scsi%d : ISTAT_DIP\n", host->host_no); + intr_dma (host, cmd); + } + + if (!hostdata->dstat_valid) { + hostdata->dstat = NCR53c7x0_read8(DSTAT_REG); + hostdata->dstat_valid = 1; + } + + if (!(hostdata->dstat & DSTAT_DFE)) { + printk ("scsi%d : DMA FIFO not empty\n", host->host_no); + /* Really need to check this out for 710 RGH */ + NCR53c7x0_write8 (CTEST8_REG, CTEST8_10_CLF); + while (NCR53c7x0_read8 (CTEST8_REG) & CTEST8_10_CLF); + hostdata->dstat |= DSTAT_DFE; + } + } + } while (interrupted); + + + + if (hostdata->intrs != -1) + hostdata->intrs++; +#if 0 + if (hostdata->intrs > 40) { + printk("scsi%d : too many interrupts, halting", host->host_no); + disable(host); + } +#endif + + if (!hostdata->idle && hostdata->state == STATE_HALTED) { + if (!hostdata->dsp_changed) { + hostdata->dsp = (u32 *) + bus_to_virt(NCR53c7x0_read32(DSP_REG)); + } + +#if 0 + printk("scsi%d : new dsp is 0x%lx (virt 0x%p)\n", + host->host_no, virt_to_bus(hostdata->dsp), hostdata->dsp); +#endif + + hostdata->state = STATE_RUNNING; + NCR53c7x0_write32 (DSP_REG, virt_to_bus(hostdata->dsp)); + if (hostdata->options & OPTION_DEBUG_TRACE) { +#ifdef CYCLIC_TRACE + log_insn (hostdata->dsp); +#else + print_insn (host, hostdata->dsp, "t ", 1); +#endif + NCR53c7x0_write8 (DCNTL_REG, hostdata->saved_dcntl | + DCNTL_SSM | DCNTL_STD); + } + } + } + } while (!done); +#ifdef CONFIG_AMIGA + custom.intena = IF_SETCLR | IF_PORTS; +#endif +} + + +/* + * Function : static int abort_connected (struct Scsi_Host *host) + * + * Purpose : Assuming that the NCR SCSI processor is currently + * halted, break the currently established nexus. Clean + * up of the NCR53c7x0_cmd and Scsi_Cmnd structures should + * be done on receipt of the abort interrupt. + * + * Inputs : host - SCSI host + * + */ + +static int +abort_connected (struct Scsi_Host *host) { +#ifdef NEW_ABORT + NCR53c7x0_local_declare(); +#endif + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; +/* FIXME : this probably should change for production kernels; at the + least, counter should move to a per-host structure. */ + static int counter = 5; +#ifdef NEW_ABORT + int sstat, phase, offset; + u32 *script; + NCR53c7x0_local_setup(host); +#endif + + if (--counter <= 0) { + disable(host); + return 0; + } + + printk ("scsi%d : DANGER : abort_connected() called \n", + host->host_no); + +#ifdef NEW_ABORT + +/* + * New strategy : Rather than using a generic abort routine, + * we'll specifically try to source or sink the appropriate + * amount of data for the phase we're currently in (taking into + * account the current synchronous offset) + */ + + sstat = (NCR53c8x0_read8 (SSTAT2_REG); + offset = OFFSET (sstat & SSTAT2_FF_MASK) >> SSTAT2_FF_SHIFT; + phase = sstat & SSTAT2_PHASE_MASK; + +/* + * SET ATN + * MOVE source_or_sink, WHEN CURRENT PHASE + * < repeat for each outstanding byte > + * JUMP send_abort_message + */ + + script = hostdata->abort_script = kmalloc ( + 8 /* instruction size */ * ( + 1 /* set ATN */ + + (!offset ? 1 : offset) /* One transfer per outstanding byte */ + + 1 /* send abort message */), + GFP_ATOMIC); + + +#else /* def NEW_ABORT */ + hostdata->dsp = hostdata->script + hostdata->E_initiator_abort / + sizeof(u32); +#endif /* def NEW_ABORT */ + hostdata->dsp_changed = 1; + +/* XXX - need to flag the command as aborted after the abort_connected + code runs + */ + return 0; +} + +/* + * Function : static int datapath_residual (Scsi_Host *host) + * + * Purpose : return residual data count of what's in the chip. + * + * Inputs : host - SCSI host + */ + +static int +datapath_residual (struct Scsi_Host *host) { + NCR53c7x0_local_declare(); + int count, synchronous, sstat; + unsigned int ddir; + + NCR53c7x0_local_setup(host); + /* COMPAT : the 700 and 700-66 need to use DFIFO_00_BO_MASK */ + count = ((NCR53c7x0_read8 (DFIFO_REG) & DFIFO_10_BO_MASK) - + (NCR53c7x0_read32 (DBC_REG) & DFIFO_10_BO_MASK)) & DFIFO_10_BO_MASK; + synchronous = NCR53c7x0_read8 (SXFER_REG) & SXFER_MO_MASK; + /* COMPAT : DDIR is elsewhere on non-'8xx chips. */ + ddir = NCR53c7x0_read8 (CTEST0_REG_700) & CTEST0_700_DDIR; + + if (ddir) { + /* Receive */ + if (synchronous) + count += (NCR53c7x0_read8 (SSTAT2_REG) & SSTAT2_FF_MASK) >> SSTAT2_FF_SHIFT; + else + if (NCR53c7x0_read8 (SSTAT1_REG) & SSTAT1_ILF) + ++count; + } else { + /* Send */ + sstat = NCR53c7x0_read8 (SSTAT1_REG); + if (sstat & SSTAT1_OLF) + ++count; + if (synchronous && (sstat & SSTAT1_ORF)) + ++count; + } + return count; +} + +/* + * Function : static const char * sbcl_to_phase (int sbcl)_ + * + * Purpose : Convert SBCL register to user-parsable phase representation + * + * Inputs : sbcl - value of sbcl register + */ + + +static const char * +sbcl_to_phase (int sbcl) { + switch (sbcl & SBCL_PHASE_MASK) { + case SBCL_PHASE_DATAIN: + return "DATAIN"; + case SBCL_PHASE_DATAOUT: + return "DATAOUT"; + case SBCL_PHASE_MSGIN: + return "MSGIN"; + case SBCL_PHASE_MSGOUT: + return "MSGOUT"; + case SBCL_PHASE_CMDOUT: + return "CMDOUT"; + case SBCL_PHASE_STATIN: + return "STATUSIN"; + default: + return "unknown"; + } +} + +/* + * Function : static const char * sstat2_to_phase (int sstat)_ + * + * Purpose : Convert SSTAT2 register to user-parsable phase representation + * + * Inputs : sstat - value of sstat register + */ + + +static const char * +sstat2_to_phase (int sstat) { + switch (sstat & SSTAT2_PHASE_MASK) { + case SSTAT2_PHASE_DATAIN: + return "DATAIN"; + case SSTAT2_PHASE_DATAOUT: + return "DATAOUT"; + case SSTAT2_PHASE_MSGIN: + return "MSGIN"; + case SSTAT2_PHASE_MSGOUT: + return "MSGOUT"; + case SSTAT2_PHASE_CMDOUT: + return "CMDOUT"; + case SSTAT2_PHASE_STATIN: + return "STATUSIN"; + default: + return "unknown"; + } +} + +/* + * Function : static void intr_phase_mismatch (struct Scsi_Host *host, + * struct NCR53c7x0_cmd *cmd) + * + * Purpose : Handle phase mismatch interrupts + * + * Inputs : host, cmd - host and NCR command causing the interrupt, cmd + * may be NULL. + * + * Side effects : The abort_connected() routine is called or the NCR chip + * is restarted, jumping to the command_complete entry point, or + * patching the address and transfer count of the current instruction + * and calling the msg_in entry point as appropriate. + */ + +static void +intr_phase_mismatch (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) { + NCR53c7x0_local_declare(); + u32 dbc_dcmd, *dsp, *dsp_next; + unsigned char dcmd, sbcl; + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + int residual; + enum {ACTION_ABORT, ACTION_ABORT_PRINT, ACTION_CONTINUE} action = + ACTION_ABORT_PRINT; + const char *where = NULL; + + NCR53c7x0_local_setup(host); + + /* + * Corrective action is based on where in the SCSI SCRIPT(tm) the error + * occurred, as well as which SCSI phase we are currently in. + */ + dsp_next = bus_to_virt(NCR53c7x0_read32(DSP_REG)); + + /* + * Fetch the current instruction, and remove the operands for easier + * interpretation. + */ + dbc_dcmd = NCR53c7x0_read32(DBC_REG); + dcmd = (dbc_dcmd & 0xff000000) >> 24; + /* + * Like other processors, the NCR adjusts the instruction pointer before + * instruction decode. Set the DSP address back to what it should + * be for this instruction based on its size (2 or 3 32 bit words). + */ + dsp = dsp_next - NCR53c7x0_insn_size(dcmd); + + + /* + * Read new SCSI phase from the SBCL lines. Since all of our code uses + * a WHEN conditional instead of an IF conditional, we don't need to + * wait for a new REQ. + */ + sbcl = NCR53c7x0_read8(SBCL_REG) & SBCL_PHASE_MASK; + + if (!cmd) { + action = ACTION_ABORT_PRINT; + where = "no current command"; + /* + * The way my SCSI SCRIPTS(tm) are architected, recoverable phase + * mismatches should only occur where we're doing a multi-byte + * BMI instruction. Specifically, this means + * + * - select messages (a SCSI-I target may ignore additional messages + * after the IDENTIFY; any target may reject a SDTR or WDTR) + * + * - command out (targets may send a message to signal an error + * condition, or go into STATUSIN after they've decided + * they don't like the command. + * + * - reply_message (targets may reject a multi-byte message in the + * middle) + * + * - data transfer routines (command completion with buffer space + * left, disconnect message, or error message) + */ + } else if (((dsp >= cmd->data_transfer_start && + dsp < cmd->data_transfer_end)) || dsp == (cmd->residual + 2)) { + if ((dcmd & (DCMD_TYPE_MASK|DCMD_BMI_OP_MASK|DCMD_BMI_INDIRECT| + DCMD_BMI_MSG|DCMD_BMI_CD)) == (DCMD_TYPE_BMI| + DCMD_BMI_OP_MOVE_I)) { + residual = datapath_residual (host); + if (hostdata->options & OPTION_DEBUG_DISCONNECT) + printk ("scsi%d : handling residual transfer (+ %d bytes from DMA FIFO)\n", + host->host_no, residual); + + /* + * The first instruction is a CALL to the alternate handler for + * this data transfer phase, so we can do calls to + * munge_msg_restart as we would if control were passed + * from normal dynamic code. + */ + if (dsp != cmd->residual + 2) { + cmd->residual[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_CALL | + ((dcmd & DCMD_BMI_IO) ? DCMD_TCI_IO : 0)) << 24) | + DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE; + cmd->residual[1] = virt_to_bus(hostdata->script) + + ((dcmd & DCMD_BMI_IO) + ? hostdata->E_other_in : hostdata->E_other_out); + } + + /* + * The second instruction is the a data transfer block + * move instruction, reflecting the pointer and count at the + * time of the phase mismatch. + */ + cmd->residual[2] = dbc_dcmd + residual; + cmd->residual[3] = NCR53c7x0_read32(DNAD_REG) - residual; + + /* + * The third and final instruction is a jump to the instruction + * which follows the instruction which had to be 'split' + */ + if (dsp != cmd->residual + 2) { + cmd->residual[4] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) + << 24) | DBC_TCI_TRUE; + cmd->residual[5] = virt_to_bus(dsp_next); + } + + /* + * For the sake of simplicity, transfer control to the + * conditional CALL at the start of the residual buffer. + */ + hostdata->dsp = cmd->residual; + hostdata->dsp_changed = 1; + action = ACTION_CONTINUE; + } else { + where = "non-BMI dynamic DSA code"; + action = ACTION_ABORT_PRINT; + } + } else if (dsp == (hostdata->script + hostdata->E_select_msgout / 4)) { + /* Release ATN */ + NCR53c7x0_write8 (SOCL_REG, 0); + switch (sbcl) { + /* + * Some devices (SQ555 come to mind) grab the IDENTIFY message + * sent on selection, and decide to go into COMMAND OUT phase + * rather than accepting the rest of the messages or rejecting + * them. Handle these devices gracefully. + */ + case SBCL_PHASE_CMDOUT: + hostdata->dsp = dsp + 2 /* two _words_ */; + hostdata->dsp_changed = 1; + printk ("scsi%d : target %d ignored SDTR and went into COMMAND OUT\n", + host->host_no, cmd->cmd->target); + cmd->flags &= ~CMD_FLAG_SDTR; + action = ACTION_CONTINUE; + break; + case SBCL_PHASE_MSGIN: + hostdata->dsp = hostdata->script + hostdata->E_msg_in / + sizeof(u32); + hostdata->dsp_changed = 1; + action = ACTION_CONTINUE; + break; + default: + where="select message out"; + action = ACTION_ABORT_PRINT; + } + /* + * Some SCSI devices will interpret a command as they read the bytes + * off the SCSI bus, and may decide that the command is Bogus before + * they've read the entire command off the bus. + */ + } else if (dsp == hostdata->script + hostdata->E_cmdout_cmdout / sizeof + (u32)) { + hostdata->dsp = hostdata->script + hostdata->E_data_transfer / + sizeof (u32); + hostdata->dsp_changed = 1; + action = ACTION_CONTINUE; + /* FIXME : we need to handle message reject, etc. within msg_respond. */ +#ifdef notyet + } else if (dsp == hostdata->script + hostdata->E_reply_message) { + switch (sbcl) { + /* Any other phase mismatches abort the currently executing command. */ +#endif + } else { + where = "unknown location"; + action = ACTION_ABORT_PRINT; + } + + /* Flush DMA FIFO */ + if (!hostdata->dstat_valid) { + hostdata->dstat = NCR53c7x0_read8(DSTAT_REG); + hostdata->dstat_valid = 1; + } + if (!(hostdata->dstat & DSTAT_DFE)) { + /* Really need to check this out for 710 RGH */ + NCR53c7x0_write8 (CTEST8_REG, CTEST8_10_CLF); + while (NCR53c7x0_read8 (CTEST8_REG) & CTEST8_10_CLF); + hostdata->dstat |= DSTAT_DFE; + } + + switch (action) { + case ACTION_ABORT_PRINT: + printk("scsi%d : %s : unexpected phase %s.\n", + host->host_no, where ? where : "unknown location", + sbcl_to_phase(sbcl)); + print_lots (host); + /* Fall through to ACTION_ABORT */ + case ACTION_ABORT: + abort_connected (host); + break; + case ACTION_CONTINUE: + break; + } + +#if 0 + if (hostdata->dsp_changed) { + printk("scsi%d: new dsp 0x%p\n", host->host_no, hostdata->dsp); + print_insn (host, hostdata->dsp, "", 1); + } +#endif + + cache_push(virt_to_bus(hostdata->script), flushsize); +} + +/* + * Function : static void intr_bf (struct Scsi_Host *host, + * struct NCR53c7x0_cmd *cmd) + * + * Purpose : handle BUS FAULT interrupts + * + * Inputs : host, cmd - host and NCR command causing the interrupt, cmd + * may be NULL. + */ + +static void +intr_bf (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) { + NCR53c7x0_local_declare(); + u32 *dsp, + *next_dsp, /* Current dsp */ + *dsa, + dbc_dcmd; /* DCMD (high eight bits) + DBC */ + char *reason = NULL; + /* Default behavior is for a silent error, with a retry until we've + exhausted retries. */ + enum {MAYBE, ALWAYS, NEVER} retry = MAYBE; + int report = 0; + NCR53c7x0_local_setup(host); + + dbc_dcmd = NCR53c7x0_read32 (DBC_REG); + next_dsp = bus_to_virt (NCR53c7x0_read32(DSP_REG)); + dsp = next_dsp - NCR53c7x0_insn_size ((dbc_dcmd >> 24) & 0xff); +/* FIXME - check chip type */ + dsa = bus_to_virt (NCR53c7x0_read32(DSA_REG)); + + /* + * Bus faults can be caused by either a Bad Address or + * Target Abort. We should check the Received Target Abort + * bit of the PCI status register and Master Abort Bit. + * + * - Master Abort bit indicates that no device claimed + * the address with DEVSEL within five clocks + * + * - Target Abort bit indicates that a target claimed it, + * but changed its mind once it saw the byte enables. + * + */ + + /* 53c710, not PCI system */ + report = 1; + reason = "Unknown"; + +#ifndef notyet + report = 1; +#endif + if (report && reason) + { + printk(KERN_ALERT "scsi%d : BUS FAULT reason = %s\n", + host->host_no, reason ? reason : "unknown"); + print_lots (host); + } + +#ifndef notyet + retry = NEVER; +#endif + + /* + * TODO : we should attempt to recover from any spurious bus + * faults. After X retries, we should figure that things are + * sufficiently wedged, and call NCR53c7xx_reset. + * + * This code should only get executed once we've decided that we + * cannot retry. + */ + + if (retry == NEVER) { + printk(KERN_ALERT " mail drew@PoohSticks.ORG\n"); + FATAL (host); + } +} + +/* + * Function : static void intr_dma (struct Scsi_Host *host, + * struct NCR53c7x0_cmd *cmd) + * + * Purpose : handle all DMA interrupts, indicated by the setting + * of the DIP bit in the ISTAT register. + * + * Inputs : host, cmd - host and NCR command causing the interrupt, cmd + * may be NULL. + */ + +static void +intr_dma (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) { + NCR53c7x0_local_declare(); + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + unsigned char dstat; /* DSTAT */ + u32 *dsp, + *next_dsp, /* Current dsp */ + *dsa, + dbc_dcmd; /* DCMD (high eight bits) + DBC */ + int tmp; + unsigned long flags; + NCR53c7x0_local_setup(host); + + if (!hostdata->dstat_valid) { + hostdata->dstat = NCR53c7x0_read8(DSTAT_REG); + hostdata->dstat_valid = 1; + } + + dstat = hostdata->dstat; + + if (hostdata->options & OPTION_DEBUG_INTR) + printk("scsi%d : DSTAT=0x%x\n", host->host_no, (int) dstat); + + dbc_dcmd = NCR53c7x0_read32 (DBC_REG); + next_dsp = bus_to_virt(NCR53c7x0_read32(DSP_REG)); + dsp = next_dsp - NCR53c7x0_insn_size ((dbc_dcmd >> 24) & 0xff); +/* XXX - check chip type */ + dsa = bus_to_virt(NCR53c7x0_read32(DSA_REG)); + + /* + * DSTAT_ABRT is the aborted interrupt. This is set whenever the + * SCSI chip is aborted. + * + * With NCR53c700 and NCR53c700-66 style chips, we should only + * get this when the chip is currently running the accept + * reselect/select code and we have set the abort bit in the + * ISTAT register. + * + */ + + if (dstat & DSTAT_ABRT) { +#if 0 + /* XXX - add code here to deal with normal abort */ + if ((hostdata->options & OPTION_700) && (hostdata->state == + STATE_ABORTING)) { + } else +#endif + { + printk(KERN_ALERT "scsi%d : unexpected abort interrupt at\n" + " ", host->host_no); + print_insn (host, dsp, KERN_ALERT "s ", 1); + FATAL (host); + } + } + + /* + * DSTAT_SSI is the single step interrupt. Should be generated + * whenever we have single stepped or are tracing. + */ + + if (dstat & DSTAT_SSI) { + if (hostdata->options & OPTION_DEBUG_TRACE) { + /* Don't print instr. until we write DSP at end of intr function */ + } else if (hostdata->options & OPTION_DEBUG_SINGLE) { + print_insn (host, dsp, "s ", 0); + save_flags(flags); + cli(); +/* XXX - should we do this, or can we get away with writing dsp? */ + + NCR53c7x0_write8 (DCNTL_REG, (NCR53c7x0_read8(DCNTL_REG) & + ~DCNTL_SSM) | DCNTL_STD); + restore_flags(flags); + } else { + printk(KERN_ALERT "scsi%d : unexpected single step interrupt at\n" + " ", host->host_no); + print_insn (host, dsp, KERN_ALERT "", 1); + printk(KERN_ALERT " mail drew@PoohSticks.ORG\n"); + FATAL (host); + } + } + + /* + * DSTAT_IID / DSTAT_OPC (same bit, same meaning, only the name + * is different) is generated whenever an illegal instruction is + * encountered. + * + * XXX - we may want to emulate INTFLY here, so we can use + * the same SCSI SCRIPT (tm) for NCR53c710 through NCR53c810 + * chips. + */ + + if (dstat & DSTAT_OPC) { + /* + * Ascertain if this IID interrupts occurred before or after a STO + * interrupt. Since the interrupt handling code now leaves + * DSP unmodified until _after_ all stacked interrupts have been + * processed, reading the DSP returns the original DSP register. + * This means that if dsp lies between the select code, and + * message out following the selection code (where the IID interrupt + * would have to have occurred by due to the implicit wait for REQ), + * we have an IID interrupt resulting from a STO condition and + * can ignore it. + */ + + if (((dsp >= (hostdata->script + hostdata->E_select / sizeof(u32))) && + (dsp <= (hostdata->script + hostdata->E_select_msgout / + sizeof(u32) + 8))) || (hostdata->test_running == 2)) { + if (hostdata->options & OPTION_DEBUG_INTR) + printk ("scsi%d : ignoring DSTAT_IID for SSTAT_STO\n", + host->host_no); + if (hostdata->expecting_iid) { + hostdata->expecting_iid = 0; + hostdata->idle = 1; + if (hostdata->test_running == 2) { + hostdata->test_running = 0; + hostdata->test_completed = 3; + } else if (cmd) + abnormal_finished (cmd, DID_BAD_TARGET << 16); + } else { + hostdata->expecting_sto = 1; + } + /* + * We can't guarantee we'll be able to execute the WAIT DISCONNECT + * instruction within the 3.4us of bus free and arbitration delay + * that a target can RESELECT in and assert REQ after we've dropped + * ACK. If this happens, we'll get an illegal instruction interrupt. + * Doing away with the WAIT DISCONNECT instructions broke everything, + * so instead I'll settle for moving one WAIT DISCONNECT a few + * instructions closer to the CLEAR ACK before it to minimize the + * chances of this happening, and handle it if it occurs anyway. + * + * Simply continue with what we were doing, and control should + * be transfered to the schedule routine which will ultimately + * pass control onto the reselection or selection (not yet) + * code. + */ + } else if (dbc_dcmd == 0x48000000 && (NCR53c7x0_read8 (SBCL_REG) & + SBCL_REQ)) { + if (!(hostdata->options & OPTION_NO_PRINT_RACE)) + { + printk("scsi%d: REQ before WAIT DISCONNECT IID\n", + host->host_no); + hostdata->options |= OPTION_NO_PRINT_RACE; + } + } else { + printk(KERN_ALERT "scsi%d : illegal instruction\n", host->host_no); + print_lots (host); + printk(KERN_ALERT " mail Richard@sleepie.demon.co.uk with ALL\n" + " boot messages and diagnostic output\n"); + FATAL (host); + } + } + + /* + * DSTAT_BF are bus fault errors. DSTAT_800_BF is valid for 710 also. + */ + + if (dstat & DSTAT_800_BF) { + intr_bf (host, cmd); + } + + + /* + * DSTAT_SIR interrupts are generated by the execution of + * the INT instruction. Since the exact values available + * are determined entirely by the SCSI script running, + * and are local to a particular script, a unique handler + * is called for each script. + */ + + if (dstat & DSTAT_SIR) { + if (hostdata->options & OPTION_DEBUG_INTR) + printk ("scsi%d : DSTAT_SIR\n", host->host_no); + switch ((tmp = hostdata->dstat_sir_intr (host, cmd))) { + case SPECIFIC_INT_NOTHING: + case SPECIFIC_INT_RESTART: + break; + case SPECIFIC_INT_ABORT: + abort_connected(host); + break; + case SPECIFIC_INT_PANIC: + printk(KERN_ALERT "scsi%d : failure at ", host->host_no); + print_insn (host, dsp, KERN_ALERT "", 1); + printk(KERN_ALERT " dstat_sir_intr() returned SPECIFIC_INT_PANIC\n"); + FATAL (host); + break; + case SPECIFIC_INT_BREAK: + intr_break (host, cmd); + break; + default: + printk(KERN_ALERT "scsi%d : failure at ", host->host_no); + print_insn (host, dsp, KERN_ALERT "", 1); + printk(KERN_ALERT" dstat_sir_intr() returned unknown value %d\n", + tmp); + FATAL (host); + } + } +} + +/* + * Function : static int print_insn (struct Scsi_Host *host, + * u32 *insn, int kernel) + * + * Purpose : print numeric representation of the instruction pointed + * to by insn to the debugging or kernel message buffer + * as appropriate. + * + * If desired, a user level program can interpret this + * information. + * + * Inputs : host, insn - host, pointer to instruction, prefix - + * string to prepend, kernel - use printk instead of debugging buffer. + * + * Returns : size, in u32s, of instruction printed. + */ + +/* + * FIXME: should change kernel parameter so that it takes an ENUM + * specifying severity - either KERN_ALERT or KERN_PANIC so + * all panic messages are output with the same severity. + */ + +static int +print_insn (struct Scsi_Host *host, const u32 *insn, + const char *prefix, int kernel) { + char buf[160], /* Temporary buffer and pointer. ICKY + arbitrary length. */ + + + *tmp; + unsigned char dcmd; /* dcmd register for *insn */ + int size; + + /* + * Check to see if the instruction pointer is not bogus before + * indirecting through it; avoiding red-zone at start of + * memory. + * + * FIXME: icky magic needs to happen here on non-intel boxes which + * don't have kernel memory mapped in like this. Might be reasonable + * to use vverify()? + */ + + if (MAP_NR(insn) < 1 || MAP_NR(insn + 8) > MAP_NR(high_memory) || + ((((dcmd = (insn[0] >> 24) & 0xff) & DCMD_TYPE_MMI) == DCMD_TYPE_MMI) && + MAP_NR(insn + 12) > MAP_NR(high_memory))) { + size = 0; + sprintf (buf, "%s%p: address out of range\n", + prefix, insn); + } else { +/* + * FIXME : (void *) cast in virt_to_bus should be unnecessary, because + * it should take const void * as argument. + */ +#ifndef CONFIG_MVME166 + sprintf(buf, "%s0x%lx (virt 0x%p) : 0x%08x 0x%08x (virt 0x%p)", + (prefix ? prefix : ""), virt_to_bus((void *) insn), insn, + insn[0], insn[1], bus_to_virt (insn[1])); +#else + /* Remove virtual addresses to reduce output, as they are the same */ + sprintf(buf, "%s0x%x (+%x) : 0x%08x 0x%08x", + (prefix ? prefix : ""), (u32)insn, ((u32)insn - + (u32)&(((struct NCR53c7x0_hostdata *)host->hostdata)->script))/4, + insn[0], insn[1]); +#endif + tmp = buf + strlen(buf); + if ((dcmd & DCMD_TYPE_MASK) == DCMD_TYPE_MMI) { +#ifndef CONFIG_MVME166 + sprintf (tmp, " 0x%08x (virt 0x%p)\n", insn[2], + bus_to_virt(insn[2])); +#else + /* Remove virtual addr to reduce output, as it is the same */ + sprintf (tmp, " 0x%08x\n", insn[2]); +#endif + size = 3; + } else { + sprintf (tmp, "\n"); + size = 2; + } + } + + if (kernel) + printk ("%s", buf); +#ifdef NCR_DEBUG + else { + size_t len = strlen(buf); + debugger_kernel_write(host, buf, len); + } +#endif + return size; +} + +/* + * Function : int NCR53c7xx_abort (Scsi_Cmnd *cmd) + * + * Purpose : Abort an errant SCSI command, doing all necessary + * cleanup of the issue_queue, running_list, shared Linux/NCR + * dsa issue and reconnect queues. + * + * Inputs : cmd - command to abort, code - entire result field + * + * Returns : 0 on success, -1 on failure. + */ + +int +NCR53c7xx_abort (Scsi_Cmnd *cmd) { + NCR53c7x0_local_declare(); + struct Scsi_Host *host = cmd->host; + struct NCR53c7x0_hostdata *hostdata = host ? (struct NCR53c7x0_hostdata *) + host->hostdata : NULL; + unsigned long flags; + struct NCR53c7x0_cmd *curr, **prev; + Scsi_Cmnd *me, **last; +#if 0 + static long cache_pid = -1; +#endif + + + if (!host) { + printk ("Bogus SCSI command pid %ld; no host structure\n", + cmd->pid); + return SCSI_ABORT_ERROR; + } else if (!hostdata) { + printk ("Bogus SCSI host %d; no hostdata\n", host->host_no); + return SCSI_ABORT_ERROR; + } + NCR53c7x0_local_setup(host); + +/* + * CHECK : I don't think that reading ISTAT will unstack any interrupts, + * since we need to write the INTF bit to clear it, and SCSI/DMA + * interrupts don't clear until we read SSTAT/SIST and DSTAT registers. + * + * See that this is the case. Appears to be correct on the 710, at least. + * + * I suspect that several of our failures may be coming from a new fatal + * interrupt (possibly due to a phase mismatch) happening after we've left + * the interrupt handler, but before the PIC has had the interrupt condition + * cleared. + */ + + if (NCR53c7x0_read8(hostdata->istat) & (ISTAT_DIP|ISTAT_SIP)) { + printk ("scsi%d : dropped interrupt for command %ld\n", host->host_no, + cmd->pid); + NCR53c7x0_intr (host->irq, NULL, NULL); + return SCSI_ABORT_BUSY; + } + + save_flags(flags); + cli(); +#if 0 + if (cache_pid == cmd->pid) + panic ("scsi%d : bloody fetus %d\n", host->host_no, cmd->pid); + else + cache_pid = cmd->pid; +#endif + + +/* + * The command could be hiding in the issue_queue. This would be very + * nice, as commands can't be moved from the high level driver's issue queue + * into the shared queue until an interrupt routine is serviced, and this + * moving is atomic. + * + * If this is the case, we don't have to worry about anything - we simply + * pull the command out of the old queue, and call it aborted. + */ + + for (me = (Scsi_Cmnd *) hostdata->issue_queue, + last = (Scsi_Cmnd **) &(hostdata->issue_queue); + me && me != cmd; last = (Scsi_Cmnd **)&(me->SCp.ptr), + me = (Scsi_Cmnd *)me->SCp.ptr); + + if (me) { + *last = (Scsi_Cmnd *) me->SCp.ptr; + if (me->host_scribble) { + ((struct NCR53c7x0_cmd *)me->host_scribble)->next = hostdata->free; + hostdata->free = (struct NCR53c7x0_cmd *) me->host_scribble; + me->host_scribble = NULL; + } + cmd->result = DID_ABORT << 16; + cmd->scsi_done(cmd); + printk ("scsi%d : found command %ld in Linux issue queue\n", + host->host_no, me->pid); + restore_flags(flags); + run_process_issue_queue(); + return SCSI_ABORT_SUCCESS; + } + +/* + * That failing, the command could be in our list of already executing + * commands. If this is the case, drastic measures are called for. + */ + + for (curr = (struct NCR53c7x0_cmd *) hostdata->running_list, + prev = (struct NCR53c7x0_cmd **) &(hostdata->running_list); + curr && curr->cmd != cmd; prev = (struct NCR53c7x0_cmd **) + &(curr->next), curr = (struct NCR53c7x0_cmd *) curr->next); + + if (curr) { + if ((cmd->result & 0xff) != 0xff && (cmd->result & 0xff00) != 0xff00) { + if (prev) + *prev = (struct NCR53c7x0_cmd *) curr->next; + curr->next = (struct NCR53c7x0_cmd *) hostdata->free; + cmd->host_scribble = NULL; + hostdata->free = curr; + cmd->scsi_done(cmd); + printk ("scsi%d : found finished command %ld in running list\n", + host->host_no, cmd->pid); + restore_flags(flags); + return SCSI_ABORT_NOT_RUNNING; + } else { + printk ("scsi%d : DANGER : command running, can not abort.\n", + cmd->host->host_no); + restore_flags(flags); + return SCSI_ABORT_BUSY; + } + } + +/* + * And if we couldn't find it in any of our queues, it must have been + * a dropped interrupt. + */ + + curr = (struct NCR53c7x0_cmd *) cmd->host_scribble; + if (curr) { + curr->next = hostdata->free; + hostdata->free = curr; + cmd->host_scribble = NULL; + } + + if (((cmd->result & 0xff00) == 0xff00) || + ((cmd->result & 0xff) == 0xff)) { + printk ("scsi%d : did this command ever run?\n", host->host_no); + cmd->result = DID_ABORT << 16; + } else { + printk ("scsi%d : probably lost INTFLY, normal completion\n", + host->host_no); +/* + * FIXME : We need to add an additional flag which indicates if a + * command was ever counted as BUSY, so if we end up here we can + * decrement the busy count if and only if it is necessary. + */ + --hostdata->busy[cmd->target][cmd->lun]; + } + restore_flags(flags); + cmd->scsi_done(cmd); + +/* + * We need to run process_issue_queue since termination of this command + * may allow another queued command to execute first? + */ + return SCSI_ABORT_NOT_RUNNING; +} + +/* + * Function : int NCR53c7xx_reset (Scsi_Cmnd *cmd) + * + * Purpose : perform a hard reset of the SCSI bus and NCR + * chip. + * + * Inputs : cmd - command which caused the SCSI RESET + * + * Returns : 0 on success. + */ + +int +NCR53c7xx_reset (Scsi_Cmnd *cmd, unsigned int reset_flags) { + NCR53c7x0_local_declare(); + unsigned long flags; + int found = 0; + struct NCR53c7x0_cmd * c; + Scsi_Cmnd *tmp; + /* + * When we call scsi_done(), it's going to wake up anything sleeping on the + * resources which were in use by the aborted commands, and we'll start to + * get new commands. + * + * We can't let this happen until after we've re-initialized the driver + * structures, and can't reinitialize those structures until after we've + * dealt with their contents. + * + * So, we need to find all of the commands which were running, stick + * them on a linked list of completed commands (we'll use the host_scribble + * pointer), do our reinitialization, and then call the done function for + * each command. + */ + Scsi_Cmnd *nuke_list = NULL; + struct Scsi_Host *host = cmd->host; + struct NCR53c7x0_hostdata *hostdata = + (struct NCR53c7x0_hostdata *) host->hostdata; + + NCR53c7x0_local_setup(host); + save_flags(flags); + cli(); + ncr_halt (host); + print_lots (host); + dump_events (host, 30); + ncr_scsi_reset (host); + for (tmp = nuke_list = return_outstanding_commands (host, 1 /* free */, + 0 /* issue */ ); tmp; tmp = (Scsi_Cmnd *) tmp->SCp.buffer) + if (tmp == cmd) { + found = 1; + break; + } + + /* + * If we didn't find the command which caused this reset in our running + * list, then we've lost it. See that it terminates normally anyway. + */ + if (!found) { + c = (struct NCR53c7x0_cmd *) cmd->host_scribble; + if (c) { + cmd->host_scribble = NULL; + c->next = hostdata->free; + hostdata->free = c; + } else + printk ("scsi%d: lost command %ld\n", host->host_no, cmd->pid); + cmd->SCp.buffer = (struct scatterlist *) nuke_list; + nuke_list = cmd; + } + + NCR53c7x0_driver_init (host); + hostdata->soft_reset (host); + if (hostdata->resets == 0) + disable(host); + else if (hostdata->resets != -1) + --hostdata->resets; + sti(); + for (; nuke_list; nuke_list = tmp) { + tmp = (Scsi_Cmnd *) nuke_list->SCp.buffer; + nuke_list->result = DID_RESET << 16; + nuke_list->scsi_done (nuke_list); + } + restore_flags(flags); + return SCSI_RESET_SUCCESS; +} + +/* + * The NCR SDMS bios follows Annex A of the SCSI-CAM draft, and + * therefore shares the scsicam_bios_param function. + */ + +/* + * Function : int insn_to_offset (Scsi_Cmnd *cmd, u32 *insn) + * + * Purpose : convert instructions stored at NCR pointer into data + * pointer offset. + * + * Inputs : cmd - SCSI command; insn - pointer to instruction. Either current + * DSP, or saved data pointer. + * + * Returns : offset on success, -1 on failure. + */ + + +static int +insn_to_offset (Scsi_Cmnd *cmd, u32 *insn) { + struct NCR53c7x0_hostdata *hostdata = + (struct NCR53c7x0_hostdata *) cmd->host->hostdata; + struct NCR53c7x0_cmd *ncmd = + (struct NCR53c7x0_cmd *) cmd->host_scribble; + int offset = 0, buffers; + struct scatterlist *segment; + char *ptr; + int found = 0; + +/* + * With the current code implementation, if the insn is inside dynamically + * generated code, the data pointer will be the instruction preceding + * the next transfer segment. + */ + + if (!check_address ((unsigned long) ncmd, sizeof (struct NCR53c7x0_cmd)) && + ((insn >= ncmd->data_transfer_start && + insn < ncmd->data_transfer_end) || + (insn >= ncmd->residual && + insn < (ncmd->residual + + sizeof(ncmd->residual))))) { + ptr = bus_to_virt(insn[3]); + + if ((buffers = cmd->use_sg)) { + for (offset = 0, + segment = (struct scatterlist *) cmd->buffer; + buffers && !((found = ((ptr >= segment->address) && + (ptr < (segment->address + segment->length))))); + --buffers, offset += segment->length, ++segment) +#if 0 + printk("scsi%d: comparing 0x%p to 0x%p\n", + cmd->host->host_no, saved, segment->address); +#else + ; +#endif + offset += ptr - segment->address; + } else { + found = 1; + offset = ptr - (char *) (cmd->request_buffer); + } + } else if ((insn >= hostdata->script + + hostdata->E_data_transfer / sizeof(u32)) && + (insn <= hostdata->script + + hostdata->E_end_data_transfer / sizeof(u32))) { + found = 1; + offset = 0; + } + return found ? offset : -1; +} + + + +/* + * Function : void print_progress (Scsi_Cmnd *cmd) + * + * Purpose : print the current location of the saved data pointer + * + * Inputs : cmd - command we are interested in + * + */ + +static void +print_progress (Scsi_Cmnd *cmd) { + NCR53c7x0_local_declare(); + struct NCR53c7x0_cmd *ncmd = + (struct NCR53c7x0_cmd *) cmd->host_scribble; + int offset, i; + char *where; + u32 *ptr; + NCR53c7x0_local_setup (cmd->host); + for (i = 0; i < 2; ++i) { + if (check_address ((unsigned long) ncmd, + sizeof (struct NCR53c7x0_cmd)) == -1) + continue; + if (!i) { + where = "saved"; + ptr = bus_to_virt(ncmd->saved_data_pointer); + } else { + where = "active"; + ptr = bus_to_virt (NCR53c7x0_read32 (DSP_REG) - + NCR53c7x0_insn_size (NCR53c7x0_read8 (DCMD_REG)) * + sizeof(u32)); + } + offset = insn_to_offset (cmd, ptr); + + if (offset != -1) + printk ("scsi%d : %s data pointer at offset %d\n", + cmd->host->host_no, where, offset); + else { + int size; + printk ("scsi%d : can't determine %s data pointer offset\n", + cmd->host->host_no, where); + if (ncmd) { + size = print_insn (cmd->host, + bus_to_virt(ncmd->saved_data_pointer), "", 1); + print_insn (cmd->host, + bus_to_virt(ncmd->saved_data_pointer) + size * sizeof(u32), + "", 1); + } + } + } +} + + +static void +print_dsa (struct Scsi_Host *host, u32 *dsa, const char *prefix) { + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + int i, len; + char *ptr; + Scsi_Cmnd *cmd; + + if (check_address ((unsigned long) dsa, hostdata->dsa_end - + hostdata->dsa_start) == -1) { + printk("scsi%d : bad dsa virt 0x%p\n", host->host_no, dsa); + return; + } + printk("%sscsi%d : dsa at phys 0x%lx (virt 0x%p)\n" + " + %d : dsa_msgout length = %u, data = 0x%x (virt 0x%p)\n" , + prefix ? prefix : "", + host->host_no, virt_to_bus (dsa), dsa, hostdata->dsa_msgout, + dsa[hostdata->dsa_msgout / sizeof(u32)], + dsa[hostdata->dsa_msgout / sizeof(u32) + 1], + bus_to_virt (dsa[hostdata->dsa_msgout / sizeof(u32) + 1])); + + /* + * Only print messages if they're sane in length so we don't + * blow the kernel printk buffer on something which won't buy us + * anything. + */ + + if (dsa[hostdata->dsa_msgout / sizeof(u32)] < + sizeof (hostdata->free->select)) + for (i = dsa[hostdata->dsa_msgout / sizeof(u32)], + ptr = bus_to_virt (dsa[hostdata->dsa_msgout / sizeof(u32) + 1]); + i > 0 && !check_address ((unsigned long) ptr, 1); + ptr += len, i -= len) { + printk(" "); + len = print_msg (ptr); + printk("\n"); + if (!len) + break; + } + + printk(" + %d : select_indirect = 0x%x\n", + hostdata->dsa_select, dsa[hostdata->dsa_select / sizeof(u32)]); + cmd = (Scsi_Cmnd *) bus_to_virt(dsa[hostdata->dsa_cmnd / sizeof(u32)]); + printk(" + %d : dsa_cmnd = 0x%x ", hostdata->dsa_cmnd, + (u32) virt_to_bus(cmd)); + if (cmd) { + printk(" result = 0x%x, target = %d, lun = %d, cmd = ", + cmd->result, cmd->target, cmd->lun); + print_command(cmd->cmnd); + } else + printk("\n"); + printk(" + %d : dsa_next = 0x%x\n", hostdata->dsa_next, + dsa[hostdata->dsa_next / sizeof(u32)]); + if (cmd) { + printk("scsi%d target %d : sxfer_sanity = 0x%x, scntl3_sanity = 0x%x\n" + " script : ", + host->host_no, cmd->target, + hostdata->sync[cmd->target].sxfer_sanity, + hostdata->sync[cmd->target].scntl3_sanity); + for (i = 0; i < (sizeof(hostdata->sync[cmd->target].script) / 4); ++i) + printk ("0x%x ", hostdata->sync[cmd->target].script[i]); + printk ("\n"); + print_progress (cmd); + } +} +/* + * Function : void print_queues (Scsi_Host *host) + * + * Purpose : print the contents of the NCR issue and reconnect queues + * + * Inputs : host - SCSI host we are interested in + * + */ + +static void +print_queues (struct Scsi_Host *host) { + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + u32 *dsa, *next_dsa; + volatile u32 *ncrcurrent; + int left; + Scsi_Cmnd *cmd, *next_cmd; + unsigned long flags; + + printk ("scsi%d : issue queue\n", host->host_no); + + for (left = host->can_queue, cmd = (Scsi_Cmnd *) hostdata->issue_queue; + left >= 0 && cmd; + cmd = next_cmd) { + next_cmd = (Scsi_Cmnd *) cmd->SCp.ptr; + save_flags(flags); + cli(); + if (cmd->host_scribble) { + if (check_address ((unsigned long) (cmd->host_scribble), + sizeof (cmd->host_scribble)) == -1) + printk ("scsi%d: scsi pid %ld bad pointer to NCR53c7x0_cmd\n", + host->host_no, cmd->pid); + /* print_dsa does sanity check on address, no need to check */ + else + print_dsa (host, ((struct NCR53c7x0_cmd *) cmd->host_scribble) + -> dsa, ""); + } else + printk ("scsi%d : scsi pid %ld for target %d lun %d has no NCR53c7x0_cmd\n", + host->host_no, cmd->pid, cmd->target, cmd->lun); + restore_flags(flags); + } + + if (left <= 0) { + printk ("scsi%d : loop detected in issue queue\n", + host->host_no); + } + + /* + * Traverse the NCR reconnect and start DSA structures, printing out + * each element until we hit the end or detect a loop. Currently, + * the reconnect structure is a linked list; and the start structure + * is an array. Eventually, the reconnect structure will become a + * list as well, since this simplifies the code. + */ + + printk ("scsi%d : schedule dsa array :\n", host->host_no); + for (left = host->can_queue, ncrcurrent = hostdata->schedule; + left > 0; ncrcurrent += 2, --left) + if (ncrcurrent[0] != hostdata->NOP_insn) +/* FIXME : convert pointer to dsa_begin to pointer to dsa. */ + print_dsa (host, bus_to_virt (ncrcurrent[1] - + (hostdata->E_dsa_code_begin - + hostdata->E_dsa_code_template)), ""); + printk ("scsi%d : end schedule dsa array\n", host->host_no); + + printk ("scsi%d : reconnect_dsa_head :\n", host->host_no); + + for (left = host->can_queue, + dsa = bus_to_virt (hostdata->reconnect_dsa_head); + left >= 0 && dsa; + dsa = next_dsa) { + save_flags (flags); + cli(); + if (check_address ((unsigned long) dsa, sizeof(dsa)) == -1) { + printk ("scsi%d: bad DSA pointer 0x%p", host->host_no, + dsa); + next_dsa = NULL; + } + else + { + next_dsa = bus_to_virt(dsa[hostdata->dsa_next / sizeof(u32)]); + print_dsa (host, dsa, ""); + } + restore_flags(flags); + } + printk ("scsi%d : end reconnect_dsa_head\n", host->host_no); + if (left < 0) + printk("scsi%d: possible loop in ncr reconnect list\n", + host->host_no); +} + +static void +print_lots (struct Scsi_Host *host) { + NCR53c7x0_local_declare(); + struct NCR53c7x0_hostdata *hostdata = + (struct NCR53c7x0_hostdata *) host->hostdata; + u32 *dsp_next, *dsp, *dsa, dbc_dcmd; + unsigned char dcmd, sbcl; + int i, size; + NCR53c7x0_local_setup(host); + + if ((dsp_next = bus_to_virt(NCR53c7x0_read32 (DSP_REG)))) { + dbc_dcmd = NCR53c7x0_read32(DBC_REG); + dcmd = (dbc_dcmd & 0xff000000) >> 24; + dsp = dsp_next - NCR53c7x0_insn_size(dcmd); + dsa = bus_to_virt(NCR53c7x0_read32(DSA_REG)); + sbcl = NCR53c7x0_read8 (SBCL_REG); + + /* + * For the 53c710, the following will report value 0 for SCNTL3 + * and STEST0 - we dont have these registers. + */ + printk ("scsi%d : DCMD|DBC=0x%x, DNAD=0x%x (virt 0x%p)\n" + " DSA=0x%lx (virt 0x%p)\n" + " DSPS=0x%x, TEMP=0x%x (virt 0x%p), DMODE=0x%x\n" + " SXFER=0x%x, SCNTL3=0x%x\n" + " %s%s%sphase=%s, %d bytes in SCSI FIFO\n" + " STEST0=0x%x\n", + host->host_no, dbc_dcmd, NCR53c7x0_read32(DNAD_REG), + bus_to_virt(NCR53c7x0_read32(DNAD_REG)), + virt_to_bus(dsa), dsa, + NCR53c7x0_read32(DSPS_REG), NCR53c7x0_read32(TEMP_REG), + bus_to_virt (NCR53c7x0_read32(TEMP_REG)), + (int) NCR53c7x0_read8(hostdata->dmode), + (int) NCR53c7x0_read8(SXFER_REG), + ((hostdata->chip / 100) == 8) ? + (int) NCR53c7x0_read8(SCNTL3_REG_800) : 0, + (sbcl & SBCL_BSY) ? "BSY " : "", + (sbcl & SBCL_SEL) ? "SEL " : "", + (sbcl & SBCL_REQ) ? "REQ " : "", + sstat2_to_phase(NCR53c7x0_read8 (((hostdata->chip / 100) == 8) ? + SSTAT1_REG : SSTAT2_REG)), + (NCR53c7x0_read8 ((hostdata->chip / 100) == 8 ? + SSTAT1_REG : SSTAT2_REG) & SSTAT2_FF_MASK) >> SSTAT2_FF_SHIFT, + ((hostdata->chip / 100) == 8) ? + NCR53c7x0_read8 (STEST0_REG_800) : 0); + printk ("scsi%d : DSP 0x%lx (virt 0x%p) ->\n", host->host_no, + virt_to_bus(dsp), dsp); + for (i = 6; i > 0; --i, dsp += size) + size = print_insn (host, dsp, "", 1); + if (NCR53c7x0_read8 (SCNTL1_REG) & SCNTL1_CON) { + if ((hostdata->chip / 100) == 8) + printk ("scsi%d : connected (SDID=0x%x, SSID=0x%x)\n", + host->host_no, NCR53c7x0_read8 (SDID_REG_800), + NCR53c7x0_read8 (SSID_REG_800)); + else + printk ("scsi%d : connected (SDID=0x%x)\n", + host->host_no, NCR53c7x0_read8 (SDID_REG_700)); + print_dsa (host, dsa, ""); + } + +#if 1 + print_queues (host); +#endif + } +} + +/* + * Function : static int shutdown (struct Scsi_Host *host) + * + * Purpose : does a clean (we hope) shutdown of the NCR SCSI + * chip. Use prior to dumping core, unloading the NCR driver, + * + * Returns : 0 on success + */ +static int +shutdown (struct Scsi_Host *host) { + NCR53c7x0_local_declare(); + unsigned long flags; + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + NCR53c7x0_local_setup(host); + save_flags (flags); + cli(); +/* Get in a state where we can reset the SCSI bus */ + ncr_halt (host); + ncr_scsi_reset (host); + hostdata->soft_reset(host); + + disable (host); + restore_flags (flags); + return 0; +} + +/* + * Function : void ncr_scsi_reset (struct Scsi_Host *host) + * + * Purpose : reset the SCSI bus. + */ + +static void +ncr_scsi_reset (struct Scsi_Host *host) { + NCR53c7x0_local_declare(); + unsigned long flags; + NCR53c7x0_local_setup(host); + save_flags (flags); + cli(); + NCR53c7x0_write8(SCNTL1_REG, SCNTL1_RST); + udelay(25); /* Minimum amount of time to assert RST */ + NCR53c7x0_write8(SCNTL1_REG, 0); + restore_flags (flags); +} + +/* + * Function : void hard_reset (struct Scsi_Host *host) + * + */ + +static void +hard_reset (struct Scsi_Host *host) { + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + unsigned long flags; + save_flags (flags); + cli(); + ncr_scsi_reset(host); + NCR53c7x0_driver_init (host); + if (hostdata->soft_reset) + hostdata->soft_reset (host); + restore_flags(flags); +} + + +/* + * Function : Scsi_Cmnd *return_outstanding_commands (struct Scsi_Host *host, + * int free, int issue) + * + * Purpose : return a linked list (using the SCp.buffer field as next, + * so we don't perturb hostdata. We don't use a field of the + * NCR53c7x0_cmd structure since we may not have allocated one + * for the command causing the reset.) of Scsi_Cmnd structures that + * had propogated below the Linux issue queue level. If free is set, + * free the NCR53c7x0_cmd structures which are associated with + * the Scsi_Cmnd structures, and clean up any internal + * NCR lists that the commands were on. If issue is set, + * also return commands in the issue queue. + * + * Returns : linked list of commands + * + * NOTE : the caller should insure that the NCR chip is halted + * if the free flag is set. + */ + +static Scsi_Cmnd * +return_outstanding_commands (struct Scsi_Host *host, int free, int issue) { + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + struct NCR53c7x0_cmd *c; + int i; + u32 *ncrcurrent; + Scsi_Cmnd *list = NULL, *tmp; + for (c = (struct NCR53c7x0_cmd *) hostdata->running_list; c; + c = (struct NCR53c7x0_cmd *) c->next) { + if (c->cmd->SCp.buffer) { + printk ("scsi%d : loop detected in running list!\n", host->host_no); + break; + } else { + printk ("The sti() implicit in a printk() prevents hangs\n"); + break; + } + + c->cmd->SCp.buffer = (struct scatterlist *) list; + list = c->cmd; + if (free) { + c->next = hostdata->free; + hostdata->free = c; + } + } + + if (free) { + for (i = 0, ncrcurrent = (u32 *) hostdata->schedule; + i < host->can_queue; ++i, ncrcurrent += 2) { + ncrcurrent[0] = hostdata->NOP_insn; + ncrcurrent[1] = 0xdeadbeef; + } + hostdata->ncrcurrent = NULL; + } + + if (issue) { + for (tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp; tmp = tmp->next) { + if (tmp->SCp.buffer) { + printk ("scsi%d : loop detected in issue queue!\n", + host->host_no); + break; + } + tmp->SCp.buffer = (struct scatterlist *) list; + list = tmp; + } + if (free) + hostdata->issue_queue = NULL; + + } + return list; +} + +/* + * Function : static int disable (struct Scsi_Host *host) + * + * Purpose : disables the given NCR host, causing all commands + * to return a driver error. Call this so we can unload the + * module during development and try again. Eventually, + * we should be able to find clean workarounds for these + * problems. + * + * Inputs : host - hostadapter to twiddle + * + * Returns : 0 on success. + */ + +static int +disable (struct Scsi_Host *host) { + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + unsigned long flags; + Scsi_Cmnd *nuke_list, *tmp; + save_flags(flags); + cli(); + if (hostdata->state != STATE_HALTED) + ncr_halt (host); + nuke_list = return_outstanding_commands (host, 1 /* free */, 1 /* issue */); + hard_reset (host); + hostdata->state = STATE_DISABLED; + restore_flags(flags); + printk ("scsi%d : nuking commands\n", host->host_no); + for (; nuke_list; nuke_list = tmp) { + tmp = (Scsi_Cmnd *) nuke_list->SCp.buffer; + nuke_list->result = DID_ERROR << 16; + nuke_list->scsi_done(nuke_list); + } + printk ("scsi%d : done. \n", host->host_no); + printk (KERN_ALERT "scsi%d : disabled. Unload and reload\n", + host->host_no); + return 0; +} + +/* + * Function : static int ncr_halt (struct Scsi_Host *host) + * + * Purpose : halts the SCSI SCRIPTS(tm) processor on the NCR chip + * + * Inputs : host - SCSI chip to halt + * + * Returns : 0 on success + */ + +static int +ncr_halt (struct Scsi_Host *host) { + NCR53c7x0_local_declare(); + unsigned long flags; + unsigned char istat, tmp; + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + int stage; + NCR53c7x0_local_setup(host); + + save_flags(flags); + cli(); + /* Stage 0 : eat all interrupts + Stage 1 : set ABORT + Stage 2 : eat all but abort interrupts + Stage 3 : eat all interrupts + */ + for (stage = 0;;) { + if (stage == 1) { + NCR53c7x0_write8(hostdata->istat, ISTAT_ABRT); + ++stage; + } + istat = NCR53c7x0_read8 (hostdata->istat); + if (istat & ISTAT_SIP) { + tmp = NCR53c7x0_read8(SSTAT0_REG); + } else if (istat & ISTAT_DIP) { + tmp = NCR53c7x0_read8(DSTAT_REG); + if (stage == 2) { + if (tmp & DSTAT_ABRT) { + NCR53c7x0_write8(hostdata->istat, 0); + ++stage; + } else { + printk(KERN_ALERT "scsi%d : could not halt NCR chip\n", + host->host_no); + disable (host); + } + } + } + if (!(istat & (ISTAT_SIP|ISTAT_DIP))) + if (stage == 0) + ++stage; + else if (stage == 3) + break; + } + hostdata->state = STATE_HALTED; + restore_flags(flags); +#if 0 + print_lots (host); +#endif + return 0; +} + +/* + * Function: event_name (int event) + * + * Purpose: map event enum into user-readable strings. + */ + +static const char * +event_name (int event) { + switch (event) { + case EVENT_NONE: return "none"; + case EVENT_ISSUE_QUEUE: return "to issue queue"; + case EVENT_START_QUEUE: return "to start queue"; + case EVENT_SELECT: return "selected"; + case EVENT_DISCONNECT: return "disconnected"; + case EVENT_RESELECT: return "reselected"; + case EVENT_COMPLETE: return "completed"; + case EVENT_IDLE: return "idle"; + case EVENT_SELECT_FAILED: return "select failed"; + case EVENT_BEFORE_SELECT: return "before select"; + case EVENT_RESELECT_FAILED: return "reselect failed"; + default: return "unknown"; + } +} + +/* + * Function : void dump_events (struct Scsi_Host *host, count) + * + * Purpose : print last count events which have occurred. + */ +static void +dump_events (struct Scsi_Host *host, int count) { + struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) + host->hostdata; + struct NCR53c7x0_event event; + int i; + unsigned long flags; + if (hostdata->events) { + if (count > hostdata->event_size) + count = hostdata->event_size; + for (i = hostdata->event_index; count > 0; + i = (i ? i - 1 : hostdata->event_size -1), --count) { + save_flags(flags); +/* + * By copying the event we're currently examining with interrupts + * disabled, we can do multiple printk(), etc. operations and + * still be guaranteed that they're happening on the same + * event structure. + */ + cli(); +#if 0 + event = hostdata->events[i]; +#else + memcpy ((void *) &event, (void *) &(hostdata->events[i]), + sizeof(event)); +#endif + + restore_flags(flags); + printk ("scsi%d : %s event %d at %ld secs %ld usecs target %d lun %d\n", + host->host_no, event_name (event.event), count, + (long) event.time.tv_sec, (long) event.time.tv_usec, + event.target, event.lun); + if (event.dsa) + printk (" event for dsa 0x%lx (virt 0x%p)\n", + virt_to_bus(event.dsa), event.dsa); + if (event.pid != -1) { + printk (" event for pid %ld ", event.pid); + print_command (event.cmnd); + } + } + } +} + +/* + * Function: check_address + * + * Purpose: Check to see if a possibly corrupt pointer will fault the + * kernel. + * + * Inputs: addr - address; size - size of area + * + * Returns: 0 if area is OK, -1 on error. + * + * NOTES: should be implemented in terms of vverify on kernels + * that have it. + */ + +static int +check_address (unsigned long addr, int size) { + return (MAP_NR(addr) < 1 || MAP_NR(addr + size) > MAP_NR(high_memory) ? + -1 : 0); +} + +#ifdef MODULE +int +NCR53c7x0_release(struct Scsi_Host *host) { + struct NCR53c7x0_hostdata *hostdata = + (struct NCR53c7x0_hostdata *) host->hostdata; + struct NCR53c7x0_cmd *cmd, *tmp; + shutdown (host); + if (host->irq != IRQ_NONE) + { + int irq_count; + struct Scsi_Host *tmp; + for (irq_count = 0, tmp = first_host; tmp; tmp = tmp->next) + if (tmp->hostt == the_template && tmp->irq == host->irq) + ++irq_count; + if (irq_count == 1) + free_irq(host->irq, NULL); + } + if (host->dma_channel != DMA_NONE) + free_dma(host->dma_channel); + if (host->io_port) + release_region(host->io_port, host->n_io_port); + + for (cmd = (struct NCR53c7x0_cmd *) hostdata->free; cmd; cmd = tmp, + --hostdata->num_cmds) { + tmp = (struct NCR53c7x0_cmd *) cmd->next; + /* + * If we're going to loop, try to stop it to get a more accurate + * count of the leaked commands. + */ + cmd->next = NULL; + if (cmd->free) + cmd->free ((void *) cmd->real, cmd->size); + } + if (hostdata->num_cmds) + printk ("scsi%d : leaked %d NCR53c7x0_cmd structures\n", + host->host_no, hostdata->num_cmds); + if (hostdata->events) + vfree ((void *)hostdata->events); + return 1; +} +Scsi_Host_Template driver_template = NCR53c7xx; +#include "scsi_module.c" +#endif /* def MODULE */ diff -u --recursive --new-file v2.1.39/linux/drivers/scsi/53c7xx.h linux/drivers/scsi/53c7xx.h --- v2.1.39/linux/drivers/scsi/53c7xx.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/53c7xx.h Sun May 18 17:10:37 1997 @@ -0,0 +1,1675 @@ +/* + * 53c710 driver. Modified from Drew Eckhardts driver + * for 53c810 by Richard Hirst [richard@sleepie.demon.co.uk] + * + * I have left the code for the 53c8xx family in here, because it didn't + * seem worth removing it. The possibility of IO_MAPPED chips rather + * than MEMORY_MAPPED remains, in case someone wants to add support for + * 53c710 chips on Intel PCs (some older machines have them on the + * motherboard). + * + * NOTE THERE MAY BE PROBLEMS WITH CASTS IN read8 AND Co. + */ + +/* + * NCR 53c{7,8}0x0 driver, header file + * + * Sponsored by + * iX Multiuser Multitasking Magazine + * Hannover, Germany + * hm@ix.de + * + * Copyright 1993, 1994, 1995 Drew Eckhardt + * Visionary Computing + * (Unix and Linux consulting and custom programming) + * drew@PoohSticks.ORG + * +1 (303) 786-7975 + * + * TolerANT and SCSI SCRIPTS are registered trademarks of NCR Corporation. + * + * PRE-ALPHA + * + * For more information, please consult + * + * NCR 53C700/53C700-66 + * SCSI I/O Processor + * Data Manual + * + * NCR 53C810 + * PCI-SCSI I/O Processor + * Data Manual + * + * NCR Microelectronics + * 1635 Aeroplaza Drive + * Colorado Springs, CO 80916 + * +1 (719) 578-3400 + * + * Toll free literature number + * +1 (800) 334-5454 + * + */ + +#ifndef NCR53c710_H +#define NCR53c710_H +#if !defined(LINUX_1_2) && !defined(LINUX_1_3) +#include +#if LINUX_VERSION_CODE > 65536 + 3 * 256 +#define LINUX_1_3 +#else +#define LINUX_1_2 +#endif +#endif + +/* + * Prevent name space pollution in hosts.c, and only provide the + * define we need to get the NCR53c7x0 driver into the host template + * array. + */ + +#if defined(HOSTS_C) || defined(MODULE) +#include + +extern int NCR53c7xx_abort(Scsi_Cmnd *); +extern int NCR53c7xx_detect(Scsi_Host_Template *tpnt); +extern int NCR53c7xx_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); +extern int NCR53c7xx_reset(Scsi_Cmnd *, unsigned int); +#ifdef MODULE +extern int NCR53c7xx_release(struct Scsi_Host *); +#else +#define NCR53c7xx_release NULL +#endif + +#ifdef LINUX_1_2 +#define NCR53c7xx {NULL, NULL, "NCR53c{7,8}xx (rel 17)", NCR53c7xx_detect,\ + NULL, /* info */ NULL, /* command, deprecated */ NULL, \ + NCR53c7xx_queue_command, NCR53c7xx_abort, NCR53c7xx_reset, \ + NULL /* slave attach */, scsicam_bios_param, /* can queue */ 24, \ + /* id */ 7, 127 /* old SG_ALL */, /* cmd per lun */ 3, \ + /* present */ 0, /* unchecked isa dma */ 0, DISABLE_CLUSTERING} +#else +#define NCR53c7xx {NULL, NULL, NULL, NULL, \ + "NCR53c{7,8}xx (rel 17)", NCR53c7xx_detect,\ + NULL, /* info */ NULL, /* command, deprecated */ NULL, \ + NCR53c7xx_queue_command, NCR53c7xx_abort, NCR53c7xx_reset, \ + NULL /* slave attach */, scsicam_bios_param, /* can queue */ 24, \ + /* id */ 7, 127 /* old SG_ALL */, /* cmd per lun */ 3, \ + /* present */ 0, /* unchecked isa dma */ 0, DISABLE_CLUSTERING} +#endif + +#endif /* defined(HOSTS_C) || defined(MODULE) */ + +#ifndef HOSTS_C +#ifdef LINUX_1_2 +/* + * Change virtual addresses to physical addresses and vv. + * These are trivial on the 1:1 Linux/i386 mapping (but if we ever + * make the kernel segment mapped at 0, we need to do translation + * on the i386 as well) + */ +extern inline unsigned long virt_to_phys(volatile void * address) +{ + return (unsigned long) address; +} + +extern inline void * phys_to_virt(unsigned long address) +{ + return (void *) address; +} + +/* + * IO bus memory addresses are also 1:1 with the physical address + */ +#define virt_to_bus virt_to_phys +#define bus_to_virt phys_to_virt + +/* + * readX/writeX() are used to access memory mapped devices. On some + * architectures the memory mapped IO stuff needs to be accessed + * differently. On the x86 architecture, we just read/write the + * memory location directly. + */ +#define readb(addr) (*(volatile unsigned char *) (addr)) +#define readw(addr) (*(volatile unsigned short *) (addr)) +#define readl(addr) (*(volatile unsigned int *) (addr)) + +#define writeb(b,addr) ((*(volatile unsigned char *) (addr)) = (b)) +#define writew(b,addr) ((*(volatile unsigned short *) (addr)) = (b)) +#define writel(b,addr) ((*(volatile unsigned int *) (addr)) = (b)) + +#define mb() + +#endif /* def LINUX_1_2 */ + +/* SCSI control 0 rw, default = 0xc0 */ +#define SCNTL0_REG 0x00 +#define SCNTL0_ARB1 0x80 /* 0 0 = simple arbitration */ +#define SCNTL0_ARB2 0x40 /* 1 1 = full arbitration */ +#define SCNTL0_STRT 0x20 /* Start Sequence */ +#define SCNTL0_WATN 0x10 /* Select with ATN */ +#define SCNTL0_EPC 0x08 /* Enable parity checking */ +/* Bit 2 is reserved on 800 series chips */ +#define SCNTL0_EPG_700 0x04 /* Enable parity generation */ +#define SCNTL0_AAP 0x02 /* ATN/ on parity error */ +#define SCNTL0_TRG 0x01 /* Target mode */ + +/* SCSI control 1 rw, default = 0x00 */ + +#define SCNTL1_REG 0x01 +#define SCNTL1_EXC 0x80 /* Extra Clock Cycle of Data setup */ +#define SCNTL1_ADB 0x40 /* contents of SODL on bus */ +#define SCNTL1_ESR_700 0x20 /* Enable SIOP response to selection + and reselection */ +#define SCNTL1_DHP_800 0x20 /* Disable halt on parity error or ATN + target mode only */ +#define SCNTL1_CON 0x10 /* Connected */ +#define SCNTL1_RST 0x08 /* SCSI RST/ */ +#define SCNTL1_AESP 0x04 /* Force bad parity */ +#define SCNTL1_SND_700 0x02 /* Start SCSI send */ +#define SCNTL1_IARB_800 0x02 /* Immediate Arbitration, start + arbitration immediately after + busfree is detected */ +#define SCNTL1_RCV_700 0x01 /* Start SCSI receive */ +#define SCNTL1_SST_800 0x01 /* Start SCSI transfer */ + +/* SCSI control 2 rw, */ + +#define SCNTL2_REG_800 0x02 +#define SCNTL2_800_SDU 0x80 /* SCSI disconnect unexpected */ + +/* SCSI control 3 rw */ + +#define SCNTL3_REG_800 0x03 +#define SCNTL3_800_SCF_SHIFT 4 +#define SCNTL3_800_SCF_MASK 0x70 +#define SCNTL3_800_SCF2 0x40 /* Synchronous divisor */ +#define SCNTL3_800_SCF1 0x20 /* 0x00 = SCLK/3 */ +#define SCNTL3_800_SCF0 0x10 /* 0x10 = SCLK/1 */ + /* 0x20 = SCLK/1.5 + 0x30 = SCLK/2 + 0x40 = SCLK/3 */ + +#define SCNTL3_800_CCF_SHIFT 0 +#define SCNTL3_800_CCF_MASK 0x07 +#define SCNTL3_800_CCF2 0x04 /* 0x00 50.01 to 66 */ +#define SCNTL3_800_CCF1 0x02 /* 0x01 16.67 to 25 */ +#define SCNTL3_800_CCF0 0x01 /* 0x02 25.01 - 37.5 + 0x03 37.51 - 50 + 0x04 50.01 - 66 */ + +/* + * SCSI destination ID rw - the appropriate bit is set for the selected + * target ID. This is written by the SCSI SCRIPTS processor. + * default = 0x00 + */ +#define SDID_REG_700 0x02 +#define SDID_REG_800 0x06 + +#define GP_REG_800 0x07 /* General purpose IO */ +#define GP_800_IO1 0x02 +#define GP_800_IO2 0x01 + +/* SCSI interrupt enable rw, default = 0x00 */ +#define SIEN_REG_700 0x03 +#define SIEN0_REG_800 0x40 +#define SIEN_MA 0x80 /* Phase mismatch (ini) or ATN (tgt) */ +#define SIEN_FC 0x40 /* Function complete */ +#define SIEN_700_STO 0x20 /* Selection or reselection timeout */ +#define SIEN_800_SEL 0x20 /* Selected */ +#define SIEN_700_SEL 0x10 /* Selected or reselected */ +#define SIEN_800_RESEL 0x10 /* Reselected */ +#define SIEN_SGE 0x08 /* SCSI gross error */ +#define SIEN_UDC 0x04 /* Unexpected disconnect */ +#define SIEN_RST 0x02 /* SCSI RST/ received */ +#define SIEN_PAR 0x01 /* Parity error */ + +/* + * SCSI chip ID rw + * NCR53c700 : + * When arbitrating, the highest bit is used, when reselection or selection + * occurs, the chip responds to all IDs for which a bit is set. + * default = 0x00 + * NCR53c810 : + * Uses bit mapping + */ +#define SCID_REG 0x04 +/* Bit 7 is reserved on 800 series chips */ +#define SCID_800_RRE 0x40 /* Enable response to reselection */ +#define SCID_800_SRE 0x20 /* Enable response to selection */ +/* Bits four and three are reserved on 800 series chips */ +#define SCID_800_ENC_MASK 0x07 /* Encoded SCSI ID */ + +/* SCSI transfer rw, default = 0x00 */ +#define SXFER_REG 0x05 +#define SXFER_DHP 0x80 /* Disable halt on parity */ + +#define SXFER_TP2 0x40 /* Transfer period msb */ +#define SXFER_TP1 0x20 +#define SXFER_TP0 0x10 /* lsb */ +#define SXFER_TP_MASK 0x70 +/* FIXME : SXFER_TP_SHIFT == 5 is right for '8xx chips */ +#define SXFER_TP_SHIFT 5 +#define SXFER_TP_4 0x00 /* Divisors */ +#define SXFER_TP_5 0x10<<1 +#define SXFER_TP_6 0x20<<1 +#define SXFER_TP_7 0x30<<1 +#define SXFER_TP_8 0x40<<1 +#define SXFER_TP_9 0x50<<1 +#define SXFER_TP_10 0x60<<1 +#define SXFER_TP_11 0x70<<1 + +#define SXFER_MO3 0x08 /* Max offset msb */ +#define SXFER_MO2 0x04 +#define SXFER_MO1 0x02 +#define SXFER_MO0 0x01 /* lsb */ +#define SXFER_MO_MASK 0x0f +#define SXFER_MO_SHIFT 0 + +/* + * SCSI output data latch rw + * The contents of this register are driven onto the SCSI bus when + * the Assert Data Bus bit of the SCNTL1 register is set and + * the CD, IO, and MSG bits of the SOCL register match the SCSI phase + */ +#define SODL_REG_700 0x06 +#define SODL_REG_800 0x54 + + +/* + * SCSI output control latch rw, default = 0 + * Note that when the chip is being manually programmed as an initiator, + * the MSG, CD, and IO bits must be set correctly for the phase the target + * is driving the bus in. Otherwise no data transfer will occur due to + * phase mismatch. + */ + +#define SOCL_REG 0x07 +#define SOCL_REQ 0x80 /* REQ */ +#define SOCL_ACK 0x40 /* ACK */ +#define SOCL_BSY 0x20 /* BSY */ +#define SOCL_SEL 0x10 /* SEL */ +#define SOCL_ATN 0x08 /* ATN */ +#define SOCL_MSG 0x04 /* MSG */ +#define SOCL_CD 0x02 /* C/D */ +#define SOCL_IO 0x01 /* I/O */ + +/* + * SCSI first byte received latch ro + * This register contains the first byte received during a block MOVE + * SCSI SCRIPTS instruction, including + * + * Initiator mode Target mode + * Message in Command + * Status Message out + * Data in Data out + * + * It also contains the selecting or reselecting device's ID and our + * ID. + * + * Note that this is the register the various IF conditionals can + * operate on. + */ +#define SFBR_REG 0x08 + +/* + * SCSI input data latch ro + * In initiator mode, data is latched into this register on the rising + * edge of REQ/. In target mode, data is latched on the rising edge of + * ACK/ + */ +#define SIDL_REG_700 0x09 +#define SIDL_REG_800 0x50 + +/* + * SCSI bus data lines ro + * This register reflects the instantaneous status of the SCSI data + * lines. Note that SCNTL0 must be set to disable parity checking, + * otherwise reading this register will latch new parity. + */ +#define SBDL_REG_700 0x0a +#define SBDL_REG_800 0x58 + +#define SSID_REG_800 0x0a +#define SSID_800_VAL 0x80 /* Exactly two bits asserted at sel */ +#define SSID_800_ENCID_MASK 0x07 /* Device which performed operation */ + + +/* + * SCSI bus control lines rw, + * instantaneous readout of control lines + */ +#define SBCL_REG 0x0b +#define SBCL_REQ 0x80 /* REQ ro */ +#define SBCL_ACK 0x40 /* ACK ro */ +#define SBCL_BSY 0x20 /* BSY ro */ +#define SBCL_SEL 0x10 /* SEL ro */ +#define SBCL_ATN 0x08 /* ATN ro */ +#define SBCL_MSG 0x04 /* MSG ro */ +#define SBCL_CD 0x02 /* C/D ro */ +#define SBCL_IO 0x01 /* I/O ro */ +#define SBCL_PHASE_CMDOUT SBCL_CD +#define SBCL_PHASE_DATAIN SBCL_IO +#define SBCL_PHASE_DATAOUT 0 +#define SBCL_PHASE_MSGIN (SBCL_CD|SBCL_IO|SBCL_MSG) +#define SBCL_PHASE_MSGOUT (SBCL_CD|SBCL_MSG) +#define SBCL_PHASE_STATIN (SBCL_CD|SBCL_IO) +#define SBCL_PHASE_MASK (SBCL_CD|SBCL_IO|SBCL_MSG) +/* + * Synchronous SCSI Clock Control bits + * 0 - set by DCNTL + * 1 - SCLK / 1.0 + * 2 - SCLK / 1.5 + * 3 - SCLK / 2.0 + */ +#define SBCL_SSCF1 0x02 /* wo, -66 only */ +#define SBCL_SSCF0 0x01 /* wo, -66 only */ +#define SBCL_SSCF_MASK 0x03 + +/* + * XXX note : when reading the DSTAT and STAT registers to clear interrupts, + * insure that 10 clocks elapse between the two + */ +/* DMA status ro */ +#define DSTAT_REG 0x0c +#define DSTAT_DFE 0x80 /* DMA FIFO empty */ +#define DSTAT_800_MDPE 0x40 /* Master Data Parity Error */ +#define DSTAT_800_BF 0x20 /* Bus Fault */ +#define DSTAT_ABRT 0x10 /* Aborted - set on error */ +#define DSTAT_SSI 0x08 /* SCRIPTS single step interrupt */ +#define DSTAT_SIR 0x04 /* SCRIPTS interrupt received - + set when INT instruction is + executed */ +#define DSTAT_WTD 0x02 /* Watchdog timeout detected */ +#define DSTAT_OPC 0x01 /* Illegal instruction */ +#define DSTAT_800_IID 0x01 /* Same thing, different name */ + + +/* NCR53c800 moves this stuff into SIST0 */ +#define SSTAT0_REG 0x0d /* SCSI status 0 ro */ +#define SIST0_REG_800 0x42 +#define SSTAT0_MA 0x80 /* ini : phase mismatch, + * tgt : ATN/ asserted + */ +#define SSTAT0_CMP 0x40 /* function complete */ +#define SSTAT0_700_STO 0x20 /* Selection or reselection timeout */ +#define SIST0_800_SEL 0x20 /* Selected */ +#define SSTAT0_700_SEL 0x10 /* Selected or reselected */ +#define SIST0_800_RSL 0x10 /* Reselected */ +#define SSTAT0_SGE 0x08 /* SCSI gross error */ +#define SSTAT0_UDC 0x04 /* Unexpected disconnect */ +#define SSTAT0_RST 0x02 /* SCSI RST/ received */ +#define SSTAT0_PAR 0x01 /* Parity error */ + +/* And uses SSTAT0 for what was SSTAT1 */ + +#define SSTAT1_REG 0x0e /* SCSI status 1 ro */ +#define SSTAT1_ILF 0x80 /* SIDL full */ +#define SSTAT1_ORF 0x40 /* SODR full */ +#define SSTAT1_OLF 0x20 /* SODL full */ +#define SSTAT1_AIP 0x10 /* Arbitration in progress */ +#define SSTAT1_LOA 0x08 /* Lost arbitration */ +#define SSTAT1_WOA 0x04 /* Won arbitration */ +#define SSTAT1_RST 0x02 /* Instant readout of RST/ */ +#define SSTAT1_SDP 0x01 /* Instant readout of SDP/ */ + +#define SSTAT2_REG 0x0f /* SCSI status 2 ro */ +#define SSTAT2_FF3 0x80 /* number of bytes in synchronous */ +#define SSTAT2_FF2 0x40 /* data FIFO */ +#define SSTAT2_FF1 0x20 +#define SSTAT2_FF0 0x10 +#define SSTAT2_FF_MASK 0xf0 +#define SSTAT2_FF_SHIFT 4 + +/* + * Latched signals, latched on the leading edge of REQ/ for initiators, + * ACK/ for targets. + */ +#define SSTAT2_SDP 0x08 /* SDP */ +#define SSTAT2_MSG 0x04 /* MSG */ +#define SSTAT2_CD 0x02 /* C/D */ +#define SSTAT2_IO 0x01 /* I/O */ +#define SSTAT2_PHASE_CMDOUT SSTAT2_CD +#define SSTAT2_PHASE_DATAIN SSTAT2_IO +#define SSTAT2_PHASE_DATAOUT 0 +#define SSTAT2_PHASE_MSGIN (SSTAT2_CD|SSTAT2_IO|SSTAT2_MSG) +#define SSTAT2_PHASE_MSGOUT (SSTAT2_CD|SSTAT2_MSG) +#define SSTAT2_PHASE_STATIN (SSTAT2_CD|SSTAT2_IO) +#define SSTAT2_PHASE_MASK (SSTAT2_CD|SSTAT2_IO|SSTAT2_MSG) + + +/* NCR53c700-66 only */ +#define SCRATCHA_REG_00 0x10 /* through 0x13 Scratch A rw */ +/* NCR53c710 and higher */ +#define DSA_REG 0x10 /* DATA structure address */ + +#define CTEST0_REG_700 0x14 /* Chip test 0 ro */ +#define CTEST0_REG_800 0x18 /* Chip test 0 rw, general purpose */ +/* 0x80 - 0x04 are reserved */ +#define CTEST0_700_RTRG 0x02 /* Real target mode */ +#define CTEST0_700_DDIR 0x01 /* Data direction, 1 = + * SCSI bus to host, 0 = + * host to SCSI. + */ + +#define CTEST1_REG_700 0x15 /* Chip test 1 ro */ +#define CTEST1_REG_800 0x19 /* Chip test 1 ro */ +#define CTEST1_FMT3 0x80 /* Identify which byte lanes are empty */ +#define CTEST1_FMT2 0x40 /* in the DMA FIFO */ +#define CTEST1_FMT1 0x20 +#define CTEST1_FMT0 0x10 + +#define CTEST1_FFL3 0x08 /* Identify which bytes lanes are full */ +#define CTEST1_FFL2 0x04 /* in the DMA FIFO */ +#define CTEST1_FFL1 0x02 +#define CTEST1_FFL0 0x01 + +#define CTEST2_REG_700 0x16 /* Chip test 2 ro */ +#define CTEST2_REG_800 0x1a /* Chip test 2 ro */ + +#define CTEST2_800_DDIR 0x80 /* 1 = SCSI->host */ +#define CTEST2_800_SIGP 0x40 /* A copy of SIGP in ISTAT. + Reading this register clears */ +#define CTEST2_800_CIO 0x20 /* Configured as IO */. +#define CTEST2_800_CM 0x10 /* Configured as memory */ + +/* 0x80 - 0x40 are reserved on 700 series chips */ +#define CTEST2_700_SOFF 0x20 /* SCSI Offset Compare, + * As an initiator, this bit is + * one when the synchronous offset + * is zero, as a target this bit + * is one when the synchronous + * offset is at the maximum + * defined in SXFER + */ +#define CTEST2_700_SFP 0x10 /* SCSI FIFO parity bit, + * reading CTEST3 unloads a byte + * from the FIFO and sets this + */ +#define CTEST2_700_DFP 0x08 /* DMA FIFO parity bit, + * reading CTEST6 unloads a byte + * from the FIFO and sets this + */ +#define CTEST2_TEOP 0x04 /* SCSI true end of process, + * indicates a totally finished + * transfer + */ +#define CTEST2_DREQ 0x02 /* Data request signal */ +/* 0x01 is reserved on 700 series chips */ +#define CTEST2_800_DACK 0x01 + +/* + * Chip test 3 ro + * Unloads the bottom byte of the eight deep SCSI synchronous FIFO, + * check SSTAT2 FIFO full bits to determine size. Note that a GROSS + * error results if a read is attempted on this register. Also note + * that 16 and 32 bit reads of this register will cause corruption. + */ +#define CTEST3_REG_700 0x17 +/* Chip test 3 rw */ +#define CTEST3_REG_800 0x1b +#define CTEST3_800_V3 0x80 /* Chip revision */ +#define CTEST3_800_V2 0x40 +#define CTEST3_800_V1 0x20 +#define CTEST3_800_V0 0x10 +#define CTEST3_800_FLF 0x08 /* Flush DMA FIFO */ +#define CTEST3_800_CLF 0x04 /* Clear DMA FIFO */ +#define CTEST3_800_FM 0x02 /* Fetch mode pin */ +/* bit 0 is reserved on 800 series chips */ + +#define CTEST4_REG_700 0x18 /* Chip test 4 rw */ +#define CTEST4_REG_800 0x21 /* Chip test 4 rw */ +/* 0x80 is reserved on 700 series chips */ +#define CTEST4_800_BDIS 0x80 /* Burst mode disable */ +#define CTEST4_ZMOD 0x40 /* High impedance mode */ +#define CTEST4_SZM 0x20 /* SCSI bus high impedance */ +#define CTEST4_700_SLBE 0x10 /* SCSI loopback enabled */ +#define CTEST4_800_SRTM 0x10 /* Shadow Register Test Mode */ +#define CTEST4_700_SFWR 0x08 /* SCSI FIFO write enable, + * redirects writes from SODL + * to the SCSI FIFO. + */ +#define CTEST4_800_MPEE 0x08 /* Enable parity checking + during master cycles on PCI + bus */ + +/* + * These bits send the contents of the CTEST6 register to the appropriate + * byte lane of the 32 bit DMA FIFO. Normal operation is zero, otherwise + * the high bit means the low two bits select the byte lane. + */ +#define CTEST4_FBL2 0x04 +#define CTEST4_FBL1 0x02 +#define CTEST4_FBL0 0x01 +#define CTEST4_FBL_MASK 0x07 +#define CTEST4_FBL_0 0x04 /* Select DMA FIFO byte lane 0 */ +#define CTEST4_FBL_1 0x05 /* Select DMA FIFO byte lane 1 */ +#define CTEST4_FBL_2 0x06 /* Select DMA FIFO byte lane 2 */ +#define CTEST4_FBL_3 0x07 /* Select DMA FIFO byte lane 3 */ +#define CTEST4_800_SAVE (CTEST4_800_BDIS) + + +#define CTEST5_REG_700 0x19 /* Chip test 5 rw */ +#define CTEST5_REG_800 0x22 /* Chip test 5 rw */ +/* + * Clock Address Incrementor. When set, it increments the + * DNAD register to the next bus size boundary. It automatically + * resets itself when the operation is complete. + */ +#define CTEST5_ADCK 0x80 +/* + * Clock Byte Counter. When set, it decrements the DBC register to + * the next bus size boundary. + */ +#define CTEST5_BBCK 0x40 +/* + * Reset SCSI Offset. Setting this bit to 1 clears the current offset + * pointer in the SCSI synchronous offset counter (SSTAT). This bit + * is set to 1 if a SCSI Gross Error Condition occurs. The offset should + * be cleared when a synchronous transfer fails. When written, it is + * automatically cleared after the SCSI synchronous offset counter is + * reset. + */ +/* Bit 5 is reserved on 800 series chips */ +#define CTEST5_700_ROFF 0x20 +/* + * Master Control for Set or Reset pulses. When 1, causes the low + * four bits of register to set when set, 0 causes the low bits to + * clear when set. + */ +#define CTEST5_MASR 0x10 +#define CTEST5_DDIR 0x08 /* DMA direction */ +/* + * Bits 2-0 are reserved on 800 series chips + */ +#define CTEST5_700_EOP 0x04 /* End of process */ +#define CTEST5_700_DREQ 0x02 /* Data request */ +#define CTEST5_700_DACK 0x01 /* Data acknowledge */ + +/* + * Chip test 6 rw - writing to this register writes to the byte + * lane in the DMA FIFO as determined by the FBL bits in the CTEST4 + * register. + */ +#define CTEST6_REG_700 0x1a +#define CTEST6_REG_800 0x23 + +#define CTEST7_REG 0x1b /* Chip test 7 rw */ +/* 0x80 - 0x40 are reserved on NCR53c700 and NCR53c700-66 chips */ +#define CTEST7_10_CDIS 0x80 /* Cache burst disable */ +#define CTEST7_10_SC1 0x40 /* Snoop control bits */ +#define CTEST7_10_SC0 0x20 +#define CTEST7_10_SC_MASK 0x60 +/* 0x20 is reserved on the NCR53c700 */ +#define CTEST7_0060_FM 0x20 /* Fetch mode */ +#define CTEST7_STD 0x10 /* Selection timeout disable */ +#define CTEST7_DFP 0x08 /* DMA FIFO parity bit for CTEST6 */ +#define CTEST7_EVP 0x04 /* 1 = host bus even parity, 0 = odd */ +#define CTEST7_10_TT1 0x02 /* Transfer type */ +#define CTEST7_00_DC 0x02 /* Set to drive DC low during instruction + fetch */ +#define CTEST7_DIFF 0x01 /* Differential mode */ + +#define CTEST7_SAVE ( CTEST7_EVP | CTEST7_DIFF ) + + +#define TEMP_REG 0x1c /* through 0x1f Temporary stack rw */ + +#define DFIFO_REG 0x20 /* DMA FIFO rw */ +/* + * 0x80 is reserved on the NCR53c710, the CLF and FLF bits have been + * moved into the CTEST8 register. + */ +#define DFIFO_00_FLF 0x80 /* Flush DMA FIFO to memory */ +#define DFIFO_00_CLF 0x40 /* Clear DMA and SCSI FIFOs */ +#define DFIFO_BO6 0x40 +#define DFIFO_BO5 0x20 +#define DFIFO_BO4 0x10 +#define DFIFO_BO3 0x08 +#define DFIFO_BO2 0x04 +#define DFIFO_BO1 0x02 +#define DFIFO_BO0 0x01 +#define DFIFO_10_BO_MASK 0x7f /* 7 bit counter */ +#define DFIFO_00_BO_MASK 0x3f /* 6 bit counter */ + +/* + * Interrupt status rw + * Note that this is the only register which can be read while SCSI + * SCRIPTS are being executed. + */ +#define ISTAT_REG_700 0x21 +#define ISTAT_REG_800 0x14 +#define ISTAT_ABRT 0x80 /* Software abort, write + *1 to abort, wait for interrupt. */ +/* 0x40 and 0x20 are reserved on NCR53c700 and NCR53c700-66 chips */ +#define ISTAT_10_SRST 0x40 /* software reset */ +#define ISTAT_10_SIGP 0x20 /* signal script */ +/* 0x10 is reserved on NCR53c700 series chips */ +#define ISTAT_800_SEM 0x10 /* semaphore */ +#define ISTAT_CON 0x08 /* 1 when connected */ +#define ISTAT_800_INTF 0x04 /* Interrupt on the fly */ +#define ISTAT_700_PRE 0x04 /* Pointer register empty. + * Set to 1 when DSPS and DSP + * registers are empty in pipeline + * mode, always set otherwise. + */ +#define ISTAT_SIP 0x02 /* SCSI interrupt pending from + * SCSI portion of SIOP see + * SSTAT0 + */ +#define ISTAT_DIP 0x01 /* DMA interrupt pending + * see DSTAT + */ + +/* NCR53c700-66 and NCR53c710 only */ +#define CTEST8_REG 0x22 /* Chip test 8 rw */ +#define CTEST8_0066_EAS 0x80 /* Enable alternate SCSI clock, + * ie read from SCLK/ rather than CLK/ + */ +#define CTEST8_0066_EFM 0x40 /* Enable fetch and master outputs */ +#define CTEST8_0066_GRP 0x20 /* Generate Receive Parity for + * pass through. This insures that + * bad parity won't reach the host + * bus. + */ +#define CTEST8_0066_TE 0x10 /* TolerANT enable. Enable + * active negation, should only + * be used for slow SCSI + * non-differential. + */ +#define CTEST8_0066_HSC 0x08 /* Halt SCSI clock */ +#define CTEST8_0066_SRA 0x04 /* Shorten REQ/ACK filtering, + * must be set for fast SCSI-II + * speeds. + */ +#define CTEST8_0066_DAS 0x02 /* Disable automatic target/initiator + * switching. + */ +#define CTEST8_0066_LDE 0x01 /* Last disconnect enable. + * The status of pending + * disconnect is maintained by + * the core, eliminating + * the possibility of missing a + * selection or reselection + * while waiting to fetch a + * WAIT DISCONNECT opcode. + */ + +#define CTEST8_10_V3 0x80 /* Chip revision */ +#define CTEST8_10_V2 0x40 +#define CTEST8_10_V1 0x20 +#define CTEST8_10_V0 0x10 +#define CTEST8_10_V_MASK 0xf0 +#define CTEST8_10_FLF 0x08 /* Flush FIFOs */ +#define CTEST8_10_CLF 0x04 /* Clear FIFOs */ +#define CTEST8_10_FM 0x02 /* Fetch pin mode */ +#define CTEST8_10_SM 0x01 /* Snoop pin mode */ + + +/* + * The CTEST9 register may be used to differentiate between a + * NCR53c700 and a NCR53c710. + * + * Write 0xff to this register. + * Read it. + * If the contents are 0xff, it is a NCR53c700 + * If the contents are 0x00, it is a NCR53c700-66 first revision + * If the contents are some other value, it is some other NCR53c700-66 + */ +#define CTEST9_REG_00 0x23 /* Chip test 9 ro */ +#define LCRC_REG_10 0x23 + +/* + * 0x24 through 0x27 are the DMA byte counter register. Instructions + * write their high 8 bits into the DCMD register, the low 24 bits into + * the DBC register. + * + * Function is dependent on the command type being executed. + */ + + +#define DBC_REG 0x24 +/* + * For Block Move Instructions, DBC is a 24 bit quantity representing + * the number of bytes to transfer. + * For Transfer Control Instructions, DBC is bit fielded as follows : + */ +/* Bits 20 - 23 should be clear */ +#define DBC_TCI_TRUE (1 << 19) /* Jump when true */ +#define DBC_TCI_COMPARE_DATA (1 << 18) /* Compare data */ +#define DBC_TCI_COMPARE_PHASE (1 << 17) /* Compare phase with DCMD field */ +#define DBC_TCI_WAIT_FOR_VALID (1 << 16) /* Wait for REQ */ +/* Bits 8 - 15 are reserved on some implementations ? */ +#define DBC_TCI_MASK_MASK 0xff00 /* Mask for data compare */ +#define DBC_TCI_MASK_SHIFT 8 +#define DBC_TCI_DATA_MASK 0xff /* Data to be compared */ +#define DBC_TCI_DATA_SHIFT 0 + +#define DBC_RWRI_IMMEDIATE_MASK 0xff00 /* Immediate data */ +#define DBC_RWRI_IMMEDIATE_SHIFT 8 /* Amount to shift */ +#define DBC_RWRI_ADDRESS_MASK 0x3f0000 /* Register address */ +#define DBC_RWRI_ADDRESS_SHIFT 16 + + +/* + * DMA command r/w + */ +#define DCMD_REG 0x27 +#define DCMD_TYPE_MASK 0xc0 /* Masks off type */ +#define DCMD_TYPE_BMI 0x00 /* Indicates a Block Move instruction */ +#define DCMD_BMI_IO 0x01 /* I/O, CD, and MSG bits selecting */ +#define DCMD_BMI_CD 0x02 /* the phase for the block MOVE */ +#define DCMD_BMI_MSG 0x04 /* instruction */ + +#define DCMD_BMI_OP_MASK 0x18 /* mask for opcode */ +#define DCMD_BMI_OP_MOVE_T 0x00 /* MOVE */ +#define DCMD_BMI_OP_MOVE_I 0x08 /* MOVE Initiator */ + +#define DCMD_BMI_INDIRECT 0x20 /* Indirect addressing */ + +#define DCMD_TYPE_TCI 0x80 /* Indicates a Transfer Control + instruction */ +#define DCMD_TCI_IO 0x01 /* I/O, CD, and MSG bits selecting */ +#define DCMD_TCI_CD 0x02 /* the phase for the block MOVE */ +#define DCMD_TCI_MSG 0x04 /* instruction */ +#define DCMD_TCI_OP_MASK 0x38 /* mask for opcode */ +#define DCMD_TCI_OP_JUMP 0x00 /* JUMP */ +#define DCMD_TCI_OP_CALL 0x08 /* CALL */ +#define DCMD_TCI_OP_RETURN 0x10 /* RETURN */ +#define DCMD_TCI_OP_INT 0x18 /* INT */ + +#define DCMD_TYPE_RWRI 0x40 /* Indicates I/O or register Read/Write + instruction */ +#define DCMD_RWRI_OPC_MASK 0x38 /* Opcode mask */ +#define DCMD_RWRI_OPC_WRITE 0x28 /* Write SFBR to register */ +#define DCMD_RWRI_OPC_READ 0x30 /* Read register to SFBR */ +#define DCMD_RWRI_OPC_MODIFY 0x38 /* Modify in place */ + +#define DCMD_RWRI_OP_MASK 0x07 +#define DCMD_RWRI_OP_MOVE 0x00 +#define DCMD_RWRI_OP_SHL 0x01 +#define DCMD_RWRI_OP_OR 0x02 +#define DCMD_RWRI_OP_XOR 0x03 +#define DCMD_RWRI_OP_AND 0x04 +#define DCMD_RWRI_OP_SHR 0x05 +#define DCMD_RWRI_OP_ADD 0x06 +#define DCMD_RWRI_OP_ADDC 0x07 + +#define DCMD_TYPE_MMI 0xc0 /* Indicates a Memory Move instruction + (three words) */ + + +#define DNAD_REG 0x28 /* through 0x2b DMA next address for + data */ +#define DSP_REG 0x2c /* through 0x2f DMA SCRIPTS pointer rw */ +#define DSPS_REG 0x30 /* through 0x33 DMA SCRIPTS pointer + save rw */ +#define DMODE_REG_00 0x34 /* DMA mode rw */ +#define DMODE_00_BL1 0x80 /* Burst length bits */ +#define DMODE_00_BL0 0x40 +#define DMODE_BL_MASK 0xc0 +/* Burst lengths (800) */ +#define DMODE_BL_2 0x00 /* 2 transfer */ +#define DMODE_BL_4 0x40 /* 4 transfers */ +#define DMODE_BL_8 0x80 /* 8 transfers */ +#define DMODE_BL_16 0xc0 /* 16 transfers */ + +#define DMODE_10_BL_1 0x00 /* 1 transfer */ +#define DMODE_10_BL_2 0x40 /* 2 transfers */ +#define DMODE_10_BL_4 0x80 /* 4 transfers */ +#define DMODE_10_BL_8 0xc0 /* 8 transfers */ +#define DMODE_10_FC2 0x20 /* Driven to FC2 pin */ +#define DMODE_10_FC1 0x10 /* Driven to FC1 pin */ +#define DMODE_710_PD 0x08 /* Program/data on FC0 pin */ +#define DMODE_710_UO 0x02 /* User prog. output */ + +#define DMODE_700_BW16 0x20 /* Host buswidth = 16 */ +#define DMODE_700_286 0x10 /* 286 mode */ +#define DMODE_700_IOM 0x08 /* Transfer to IO port */ +#define DMODE_700_FAM 0x04 /* Fixed address mode */ +#define DMODE_700_PIPE 0x02 /* Pipeline mode disables + * automatic fetch / exec + */ +#define DMODE_MAN 0x01 /* Manual start mode, + * requires a 1 to be written + * to the start DMA bit in the DCNTL + * register to run scripts + */ + +#define DMODE_700_SAVE ( DMODE_00_BL_MASK | DMODE_00_BW16 | DMODE_00_286 ) + +/* NCR53c800 series only */ +#define SCRATCHA_REG_800 0x34 /* through 0x37 Scratch A rw */ +/* NCR53c710 only */ +#define SCRATCHB_REG_10 0x34 /* through 0x37 scratch B rw */ + +#define DMODE_REG_10 0x38 /* DMA mode rw, NCR53c710 and newer */ +#define DMODE_800_SIOM 0x20 /* Source IO = 1 */ +#define DMODE_800_DIOM 0x10 /* Destination IO = 1 */ +#define DMODE_800_ERL 0x08 /* Enable Read Line */ + +/* 35-38 are reserved on 700 and 700-66 series chips */ +#define DIEN_REG 0x39 /* DMA interrupt enable rw */ +/* 0x80, 0x40, and 0x20 are reserved on 700-series chips */ +#define DIEN_800_MDPE 0x40 /* Master data parity error */ +#define DIEN_800_BF 0x20 /* BUS fault */ +#define DIEN_700_BF 0x20 /* BUS fault */ +#define DIEN_ABRT 0x10 /* Enable aborted interrupt */ +#define DIEN_SSI 0x08 /* Enable single step interrupt */ +#define DIEN_SIR 0x04 /* Enable SCRIPTS INT command + * interrupt + */ +/* 0x02 is reserved on 800 series chips */ +#define DIEN_700_WTD 0x02 /* Enable watchdog timeout interrupt */ +#define DIEN_700_OPC 0x01 /* Enable illegal instruction + * interrupt + */ +#define DIEN_800_IID 0x01 /* Same meaning, different name */ + +/* + * DMA watchdog timer rw + * set in 16 CLK input periods. + */ +#define DWT_REG 0x3a + +/* DMA control rw */ +#define DCNTL_REG 0x3b +#define DCNTL_700_CF1 0x80 /* Clock divisor bits */ +#define DCNTL_700_CF0 0x40 +#define DCNTL_700_CF_MASK 0xc0 +/* Clock divisors Divisor SCLK range (MHZ) */ +#define DCNTL_700_CF_2 0x00 /* 2.0 37.51-50.00 */ +#define DCNTL_700_CF_1_5 0x40 /* 1.5 25.01-37.50 */ +#define DCNTL_700_CF_1 0x80 /* 1.0 16.67-25.00 */ +#define DCNTL_700_CF_3 0xc0 /* 3.0 50.01-66.67 (53c700-66) */ + +#define DCNTL_700_S16 0x20 /* Load scripts 16 bits at a time */ +#define DCNTL_SSM 0x10 /* Single step mode */ +#define DCNTL_700_LLM 0x08 /* Low level mode, can only be set + * after selection */ +#define DCNTL_800_IRQM 0x08 /* Totem pole IRQ pin */ +#define DCNTL_STD 0x04 /* Start DMA / SCRIPTS */ +/* 0x02 is reserved */ +#define DCNTL_00_RST 0x01 /* Software reset, resets everything + * but 286 mode bit in DMODE. On the + * NCR53c710, this bit moved to CTEST8 + */ +#define DCNTL_10_COM 0x01 /* 700 software compatibility mode */ +#define DCNTL_10_EA 0x20 /* Enable Ack - needed for MVME166 */ + +#define DCNTL_700_SAVE ( DCNTL_CF_MASK | DCNTL_S16) + + +/* NCR53c700-66 only */ +#define SCRATCHB_REG_00 0x3c /* through 0x3f scratch b rw */ +#define SCRATCHB_REG_800 0x5c /* through 0x5f scratch b rw */ +/* NCR53c710 only */ +#define ADDER_REG_10 0x3c /* Adder, NCR53c710 only */ + +#define SIEN1_REG_800 0x41 +#define SIEN1_800_STO 0x04 /* selection/reselection timeout */ +#define SIEN1_800_GEN 0x02 /* general purpose timer */ +#define SIEN1_800_HTH 0x01 /* handshake to handshake */ + +#define SIST1_REG_800 0x43 +#define SIST1_800_STO 0x04 /* selection/reselection timeout */ +#define SIST1_800_GEN 0x02 /* general purpose timer */ +#define SIST1_800_HTH 0x01 /* handshake to handshake */ + +#define SLPAR_REG_800 0x44 /* Parity */ + +#define MACNTL_REG_800 0x46 /* Memory access control */ +#define MACNTL_800_TYP3 0x80 +#define MACNTL_800_TYP2 0x40 +#define MACNTL_800_TYP1 0x20 +#define MACNTL_800_TYP0 0x10 +#define MACNTL_800_DWR 0x08 +#define MACNTL_800_DRD 0x04 +#define MACNTL_800_PSCPT 0x02 +#define MACNTL_800_SCPTS 0x01 + +#define GPCNTL_REG_800 0x47 /* General Purpose Pin Control */ + +/* Timeouts are expressed such that 0=off, 1=100us, doubling after that */ +#define STIME0_REG_800 0x48 /* SCSI Timer Register 0 */ +#define STIME0_800_HTH_MASK 0xf0 /* Handshake to Handshake timeout */ +#define STIME0_800_HTH_SHIFT 4 +#define STIME0_800_SEL_MASK 0x0f /* Selection timeout */ +#define STIME0_800_SEL_SHIFT 0 + +#define STIME1_REG_800 0x49 +#define STIME1_800_GEN_MASK 0x0f /* General purpose timer */ + +#define RESPID_REG_800 0x4a /* Response ID, bit fielded. 8 + bits on narrow chips, 16 on WIDE */ + +#define STEST0_REG_800 0x4c +#define STEST0_800_SLT 0x08 /* Selection response logic test */ +#define STEST0_800_ART 0x04 /* Arbitration priority encoder test */ +#define STEST0_800_SOZ 0x02 /* Synchronous offset zero */ +#define STEST0_800_SOM 0x01 /* Synchronous offset maximum */ + +#define STEST1_REG_800 0x4d +#define STEST1_800_SCLK 0x80 /* Disable SCSI clock */ + +#define STEST2_REG_800 0x4e +#define STEST2_800_SCE 0x80 /* Enable SOCL/SODL */ +#define STEST2_800_ROF 0x40 /* Reset SCSI sync offset */ +#define STEST2_800_SLB 0x10 /* Enable SCSI loopback mode */ +#define STEST2_800_SZM 0x08 /* SCSI high impedance mode */ +#define STEST2_800_EXT 0x02 /* Extend REQ/ACK filter 30 to 60ns */ +#define STEST2_800_LOW 0x01 /* SCSI low level mode */ + +#define STEST3_REG_800 0x4f +#define STEST3_800_TE 0x80 /* Enable active negation */ +#define STEST3_800_STR 0x40 /* SCSI FIFO test read */ +#define STEST3_800_HSC 0x20 /* Halt SCSI clock */ +#define STEST3_800_DSI 0x10 /* Disable single initiator response */ +#define STEST3_800_TTM 0x04 /* Time test mode */ +#define STEST3_800_CSF 0x02 /* Clear SCSI FIFO */ +#define STEST3_800_STW 0x01 /* SCSI FIFO test write */ + +#define OPTION_PARITY 0x1 /* Enable parity checking */ +#define OPTION_TAGGED_QUEUE 0x2 /* Enable SCSI-II tagged queuing */ +#define OPTION_700 0x8 /* Always run NCR53c700 scripts */ +#define OPTION_INTFLY 0x10 /* Use INTFLY interrupts */ +#define OPTION_DEBUG_INTR 0x20 /* Debug interrupts */ +#define OPTION_DEBUG_INIT_ONLY 0x40 /* Run initialization code and + simple test code, return + DID_NO_CONNECT if any SCSI + commands are attempted. */ +#define OPTION_DEBUG_READ_ONLY 0x80 /* Return DID_ERROR if any + SCSI write is attempted */ +#define OPTION_DEBUG_TRACE 0x100 /* Animated trace mode, print + each address and instruction + executed to debug buffer. */ +#define OPTION_DEBUG_SINGLE 0x200 /* stop after executing one + instruction */ +#define OPTION_SYNCHRONOUS 0x400 /* Enable sync SCSI. */ +#define OPTION_MEMORY_MAPPED 0x800 /* NCR registers have valid + memory mapping */ +#define OPTION_IO_MAPPED 0x1000 /* NCR registers have valid + I/O mapping */ +#define OPTION_DEBUG_PROBE_ONLY 0x2000 /* Probe only, don't even init */ +#define OPTION_DEBUG_TESTS_ONLY 0x4000 /* Probe, init, run selected tests */ +#define OPTION_DEBUG_TEST0 0x08000 /* Run test 0 */ +#define OPTION_DEBUG_TEST1 0x10000 /* Run test 1 */ +#define OPTION_DEBUG_TEST2 0x20000 /* Run test 2 */ +#define OPTION_DEBUG_DUMP 0x40000 /* Dump commands */ +#define OPTION_DEBUG_TARGET_LIMIT 0x80000 /* Only talk to target+luns specified */ +#define OPTION_DEBUG_NCOMMANDS_LIMIT 0x100000 /* Limit the number of commands */ +#define OPTION_DEBUG_SCRIPT 0x200000 /* Print when checkpoints are passed */ +#define OPTION_DEBUG_FIXUP 0x400000 /* print fixup values */ +#define OPTION_DEBUG_DSA 0x800000 +#define OPTION_DEBUG_CORRUPTION 0x1000000 /* Detect script corruption */ +#define OPTION_DEBUG_SDTR 0x2000000 /* Debug SDTR problem */ +#define OPTION_DEBUG_MISMATCH 0x4000000 /* Debug phase mismatches */ +#define OPTION_DISCONNECT 0x8000000 /* Allow disconnect */ +#define OPTION_DEBUG_DISCONNECT 0x10000000 +#define OPTION_ALWAYS_SYNCHRONOUS 0x20000000 /* Negotiate sync. transfers + on power up */ +#define OPTION_DEBUG_QUEUES 0x80000000 +#define OPTION_DEBUG_ALLOCATION 0x100000000LL +#define OPTION_DEBUG_SYNCHRONOUS 0x200000000LL /* Sanity check SXFER and + SCNTL3 registers */ +#define OPTION_NO_ASYNC 0x400000000LL /* Don't automagically send + SDTR for async transfers when + we haven't been told to do + a synchronous transfer. */ +#define OPTION_NO_PRINT_RACE 0x800000000LL /* Don't print message when + the reselect/WAIT DISCONNECT + race condition hits */ +#if !defined(PERM_OPTIONS) +#define PERM_OPTIONS 0 +#endif + +/* + * Some data which is accessed by the NCR chip must be 4-byte aligned. + * For some hosts the default is less than that (eg. 68K uses 2-byte). + * Alignment has only been forced where it is important; also if one + * 32 bit structure field is aligned then it is assumed that following + * 32 bit fields are also aligned. Take care when adding fields + * which are other than 32 bit. + */ + +struct NCR53c7x0_synchronous { + u32 select_indirect /* Value used for indirect selection */ + __attribute__ ((aligned (4))); + u32 sscf_710; /* Used to set SSCF bits for 710 */ + u32 script[8]; /* Size ?? Script used when target is + reselected */ + unsigned char synchronous_want[5]; /* Per target desired SDTR */ +/* + * Set_synchronous programs these, select_indirect and current settings after + * int_debug_should show a match. + */ + unsigned char sxfer_sanity, scntl3_sanity; +}; + +#define CMD_FLAG_SDTR 1 /* Initiating synchronous + transfer negotiation */ +#define CMD_FLAG_WDTR 2 /* Initiating wide transfer + negotiation */ +#define CMD_FLAG_DID_SDTR 4 /* did SDTR */ +#define CMD_FLAG_DID_WDTR 8 /* did WDTR */ + +struct NCR53c7x0_table_indirect { + u32 count; + void *address; +}; + +enum ncr_event { + EVENT_NONE = 0, +/* + * Order is IMPORTANT, since these must correspond to the event interrupts + * in 53c7,8xx.scr + */ + + EVENT_ISSUE_QUEUE = 0x5000000, /* 0 Command was added to issue queue */ + EVENT_START_QUEUE, /* 1 Command moved to start queue */ + EVENT_SELECT, /* 2 Command completed selection */ + EVENT_DISCONNECT, /* 3 Command disconnected */ + EVENT_RESELECT, /* 4 Command reselected */ + EVENT_COMPLETE, /* 5 Command completed */ + EVENT_IDLE, /* 6 */ + EVENT_SELECT_FAILED, /* 7 */ + EVENT_BEFORE_SELECT, /* 8 */ + EVENT_RESELECT_FAILED /* 9 */ +}; + +struct NCR53c7x0_event { + enum ncr_event event; /* What type of event */ + unsigned char target; + unsigned char lun; + struct timeval time; + u32 *dsa; /* What's in the DSA register now (virt) */ +/* + * A few things from that SCSI pid so we know what happened after + * the Scsi_Cmnd structure in question may have disappeared. + */ + unsigned long pid; /* The SCSI PID which caused this + event */ + unsigned char cmnd[12]; +}; + +/* + * Things in the NCR53c7x0_cmd structure are split into two parts : + * + * 1. A fixed portion, for things which are not accessed directly by static NCR + * code (ie, are referenced only by the Linux side of the driver, + * or only by dynamically generated code). + * + * 2. The DSA portion, for things which are accessed directly by static NCR + * code. + * + * This is a little ugly, but it + * 1. Avoids conflicts between the NCR code's picture of the structure, and + * Linux code's idea of what it looks like. + * + * 2. Minimizes the pain in the Linux side of the code needed + * to calculate real dsa locations for things, etc. + * + */ + +struct NCR53c7x0_cmd { + void *real; /* Real, unaligned address for + free function */ + void (* free)(void *, int); /* Command to deallocate; NULL + for structures allocated with + scsi_register, etc. */ + Scsi_Cmnd *cmd; /* Associated Scsi_Cmnd + structure, Scsi_Cmnd points + at NCR53c7x0_cmd using + host_scribble structure */ + + int size; /* scsi_malloc'd size of this + structure */ + + int flags; /* CMD_* flags */ + +/* + * SDTR and WIDE messages are an either/or affair + * in this message, since we will go into message out and send + * _the whole mess_ without dropping out of message out to + * let the target go into message in after sending the first + * message. + */ + + unsigned char select[11]; /* Select message, includes + IDENTIFY + (optional) QUEUE TAG + (optional) SDTR or WDTR + */ + + + volatile struct NCR53c7x0_cmd *next; /* Linux maintained lists (free, + running, eventually finished */ + + + u32 *data_transfer_start; /* Start of data transfer routines */ + u32 *data_transfer_end; /* Address after end of data transfer o + routines */ +/* + * The following three fields were moved from the DSA proper to here + * since only dynamically generated NCR code refers to them, meaning + * we don't need dsa_* absolutes, and it is simpler to let the + * host code refer to them directly. + */ + +/* + * HARD CODED : residual and saved_residual need to agree with the sizes + * used in NCR53c7,8xx.scr. + * + * FIXME: we want to consider the case where we have odd-length + * scatter/gather buffers and a WIDE transfer, in which case + * we'll need to use the CHAIN MOVE instruction. Ick. + */ + u32 residual[6] __attribute__ ((aligned (4))); + /* Residual data transfer which + allows pointer code to work + right. + + [0-1] : Conditional call to + appropriate other transfer + routine. + [2-3] : Residual block transfer + instruction. + [4-5] : Jump to instruction + after splice. + */ + u32 saved_residual[6]; /* Copy of old residual, so we + can get another partial + transfer and still recover + */ + + u32 saved_data_pointer; /* Saved data pointer */ + + u32 dsa_next_addr; /* _Address_ of dsa_next field + in this dsa for RISCy + style constant. */ + + u32 dsa_addr; /* Address of dsa; RISCy style + constant */ + + u32 dsa[0]; /* Variable length (depending + on host type, number of scatter / + gather buffers, etc). */ +}; + +struct NCR53c7x0_break { + u32 *address, old_instruction[2]; + struct NCR53c7x0_break *next; + unsigned char old_size; /* Size of old instruction */ +}; + +/* Indicates that the NCR is not executing code */ +#define STATE_HALTED 0 +/* + * Indicates that the NCR is executing the wait for select / reselect + * script. Only used when running NCR53c700 compatible scripts, only + * state during which an ABORT is _not_ considered an error condition. + */ +#define STATE_WAITING 1 +/* Indicates that the NCR is executing other code. */ +#define STATE_RUNNING 2 +/* + * Indicates that the NCR was being aborted. + */ +#define STATE_ABORTING 3 +/* Indicates that the NCR was successfully aborted. */ +#define STATE_ABORTED 4 +/* Indicates that the NCR has been disabled due to a fatal error */ +#define STATE_DISABLED 5 + +/* + * Where knowledge of SCSI SCRIPT(tm) specified values are needed + * in an interrupt handler, an interrupt handler exists for each + * different SCSI script so we don't have name space problems. + * + * Return values of these handlers are as follows : + */ +#define SPECIFIC_INT_NOTHING 0 /* don't even restart */ +#define SPECIFIC_INT_RESTART 1 /* restart at the next instruction */ +#define SPECIFIC_INT_ABORT 2 /* recoverable error, abort cmd */ +#define SPECIFIC_INT_PANIC 3 /* unrecoverable error, panic */ +#define SPECIFIC_INT_DONE 4 /* normal command completion */ +#define SPECIFIC_INT_BREAK 5 /* break point encountered */ + +struct NCR53c7x0_hostdata { + int size; /* Size of entire Scsi_Host + structure */ + int board; /* set to board type, useful if + we have host specific things, + ie, a general purpose I/O + bit is being used to enable + termination, etc. */ + + int chip; /* set to chip type; 700-66 is + 700-66, rest are last three + digits of part number */ + + char valid_ids[8]; /* Valid SCSI ID's for adapter */ + /* + * PCI bus, device, function, only for NCR53c8x0 chips. + * pci_valid indicates that the PCI configuration information + * is valid, and we can twiddle MAX_LAT, etc. as recommended + * for maximum performance in the NCR documentation. + */ + unsigned char pci_bus, pci_device_fn; + unsigned pci_valid:1; + + u32 *dsp; /* dsp to restart with after + all stacked interrupts are + handled. */ + + unsigned dsp_changed:1; /* Has dsp changed within this + set of stacked interrupts ? */ + + unsigned char dstat; /* Most recent value of dstat */ + unsigned dstat_valid:1; + + unsigned expecting_iid:1; /* Expect IID interrupt */ + unsigned expecting_sto:1; /* Expect STO interrupt */ + + /* + * The code stays cleaner if we use variables with function + * pointers and offsets that are unique for the different + * scripts rather than having a slew of switch(hostdata->chip) + * statements. + * + * It also means that the #defines from the SCSI SCRIPTS(tm) + * don't have to be visible outside of the script-specific + * instructions, preventing name space pollution. + */ + + void (* init_fixup)(struct Scsi_Host *host); + void (* init_save_regs)(struct Scsi_Host *host); + void (* dsa_fixup)(struct NCR53c7x0_cmd *cmd); + void (* soft_reset)(struct Scsi_Host *host); + int (* run_tests)(struct Scsi_Host *host); + + /* + * Called when DSTAT_SIR is set, indicating an interrupt generated + * by the INT instruction, where values are unique for each SCSI + * script. Should return one of the SPEC_* values. + */ + + int (* dstat_sir_intr)(struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd); + + int dsa_len; /* Size of DSA structure */ + + /* + * Location of DSA fields for the SCSI SCRIPT corresponding to this + * chip. + */ + + s32 dsa_start; + s32 dsa_end; + s32 dsa_next; + s32 dsa_prev; + s32 dsa_cmnd; + s32 dsa_select; + s32 dsa_msgout; + s32 dsa_cmdout; + s32 dsa_dataout; + s32 dsa_datain; + s32 dsa_msgin; + s32 dsa_msgout_other; + s32 dsa_write_sync; + s32 dsa_write_resume; + s32 dsa_check_reselect; + s32 dsa_status; + s32 dsa_saved_pointer; + s32 dsa_jump_dest; + + /* + * Important entry points that generic fixup code needs + * to know about, fixed up. + */ + + s32 E_accept_message; + s32 E_command_complete; + s32 E_data_transfer; + s32 E_dsa_code_template; + s32 E_dsa_code_template_end; + s32 E_end_data_transfer; + s32 E_msg_in; + s32 E_initiator_abort; + s32 E_other_transfer; + s32 E_other_in; + s32 E_other_out; + s32 E_target_abort; + s32 E_debug_break; + s32 E_reject_message; + s32 E_respond_message; + s32 E_select; + s32 E_select_msgout; + s32 E_test_0; + s32 E_test_1; + s32 E_test_2; + s32 E_test_3; + s32 E_dsa_zero; + s32 E_cmdout_cmdout; + s32 E_wait_reselect; + s32 E_dsa_code_begin; + + long long options; /* Bitfielded set of options enabled */ + volatile u32 test_completed; /* Test completed */ + int test_running; /* Test currently running */ + s32 test_source + __attribute__ ((aligned (4))); + volatile s32 test_dest; + + volatile int state; /* state of driver, only used for + OPTION_700 */ + + unsigned char dmode; /* + * set to the address of the DMODE + * register for this chip. + */ + unsigned char istat; /* + * set to the address of the ISTAT + * register for this chip. + */ + + int scsi_clock; /* + * SCSI clock in HZ. 0 may be used + * for unknown, although this will + * disable synchronous negotiation. + */ + + volatile int intrs; /* Number of interrupts */ + volatile int resets; /* Number of SCSI resets */ + unsigned char saved_dmode; + unsigned char saved_ctest4; + unsigned char saved_ctest7; + unsigned char saved_dcntl; + unsigned char saved_scntl3; + + unsigned char this_id_mask; + + /* Debugger information */ + struct NCR53c7x0_break *breakpoints, /* Linked list of all break points */ + *breakpoint_current; /* Current breakpoint being stepped + through, NULL if we are running + normally. */ +#ifdef NCR_DEBUG + int debug_size; /* Size of debug buffer */ + volatile int debug_count; /* Current data count */ + volatile char *debug_buf; /* Output ring buffer */ + volatile char *debug_write; /* Current write pointer */ + volatile char *debug_read; /* Current read pointer */ +#endif /* def NCR_DEBUG */ + + /* XXX - primitive debugging junk, remove when working ? */ + int debug_print_limit; /* Number of commands to print + out exhaustive debugging + information for if + OPTION_DEBUG_DUMP is set */ + + unsigned char debug_lun_limit[16]; /* If OPTION_DEBUG_TARGET_LIMIT + set, puke if commands are sent + to other target/lun combinations */ + + int debug_count_limit; /* Number of commands to execute + before puking to limit debugging + output */ + + + volatile unsigned idle:1; /* set to 1 if idle */ + + /* + * Table of synchronous+wide transfer parameters set on a per-target + * basis. + */ + + volatile struct NCR53c7x0_synchronous sync[16] + __attribute__ ((aligned (4))); + + volatile Scsi_Cmnd *issue_queue + __attribute__ ((aligned (4))); + /* waiting to be issued by + Linux driver */ + volatile struct NCR53c7x0_cmd *running_list; + /* commands running, maintained + by Linux driver */ + + volatile struct NCR53c7x0_cmd *ncrcurrent; /* currently connected + nexus, ONLY valid for + NCR53c700/NCR53c700-66 + */ + + volatile struct NCR53c7x0_cmd *spare; /* pointer to spare, + allocated at probe time, + which we can use for + initialization */ + volatile struct NCR53c7x0_cmd *free; + int max_cmd_size; /* Maximum size of NCR53c7x0_cmd + based on number of + scatter/gather segments, etc. + */ + volatile int num_cmds; /* Number of commands + allocated */ + volatile int extra_allocate; + volatile unsigned char cmd_allocated[16]; /* Have we allocated commands + for this target yet? If not, + do so ASAP */ + volatile unsigned char busy[16][8]; /* number of commands + executing on each target + */ + /* + * Eventually, I'll switch to a coroutine for calling + * cmd->done(cmd), etc. so that we can overlap interrupt + * processing with this code for maximum performance. + */ + + volatile struct NCR53c7x0_cmd *finished_queue; + + /* Shared variables between SCRIPT and host driver */ + volatile u32 *schedule + __attribute__ ((aligned (4))); /* Array of JUMPs to dsa_begin + routines of various DSAs. + When not in use, replace + with jump to next slot */ + + + volatile unsigned char msg_buf[16]; /* buffer for messages + other than the command + complete message */ + + /* Per-target default synchronous and WIDE messages */ + volatile unsigned char synchronous_want[16][5]; + volatile unsigned char wide_want[16][4]; + + /* Bit fielded set of targets we want to speak synchronously with */ + volatile u16 initiate_sdtr; + /* Bit fielded set of targets we want to speak wide with */ + volatile u16 initiate_wdtr; + /* Bit fielded list of targets we've talked to. */ + volatile u16 talked_to; + + /* Array of bit-fielded lun lists that we need to request_sense */ + volatile unsigned char request_sense[16]; + + u32 addr_reconnect_dsa_head + __attribute__ ((aligned (4))); /* RISCy style constant, + address of following */ + volatile u32 reconnect_dsa_head; + /* Data identifying nexus we are trying to match during reselection */ + volatile unsigned char reselected_identify; /* IDENTIFY message */ + volatile unsigned char reselected_tag; /* second byte of queue tag + message or 0 */ + + /* These were static variables before we moved them */ + + s32 NCR53c7xx_zero + __attribute__ ((aligned (4))); + s32 NCR53c7xx_sink; + u32 NOP_insn; + char NCR53c7xx_msg_reject; + char NCR53c7xx_msg_abort; + char NCR53c7xx_msg_nop; + + /* + * Following item introduced by RGH to support NCRc710, which is + * VERY brain-dead when it come to memory moves + */ + + /* DSA save area used only by the NCR chip */ + volatile unsigned long saved2_dsa + __attribute__ ((aligned (4))); + + volatile unsigned long emulated_intfly + __attribute__ ((aligned (4))); + + volatile int event_size, event_index; + volatile struct NCR53c7x0_event *events; + + /* If we need to generate code to kill off the currently connected + command, this is where we do it. Should have a BMI instruction + to source or sink the current data, followed by a JUMP + to abort_connected */ + + u32 *abort_script; + + int script_count; /* Size of script in words */ + u32 script[0]; /* Relocated SCSI script */ + +}; + +#define IRQ_NONE 255 +#define DMA_NONE 255 +#define IRQ_AUTO 254 +#define DMA_AUTO 254 + +#define BOARD_GENERIC 0 + +#define NCR53c7x0_insn_size(insn) \ + (((insn) & DCMD_TYPE_MASK) == DCMD_TYPE_MMI ? 3 : 2) + + +#define NCR53c7x0_local_declare() \ + volatile unsigned char *NCR53c7x0_address_memory; \ + unsigned int NCR53c7x0_address_io; \ + int NCR53c7x0_memory_mapped + +#define NCR53c7x0_local_setup(host) \ + NCR53c7x0_address_memory = (void *) (host)->base; \ + NCR53c7x0_address_io = (unsigned int) (host)->io_port; \ + NCR53c7x0_memory_mapped = ((struct NCR53c7x0_hostdata *) \ + host->hostdata)-> options & OPTION_MEMORY_MAPPED + +#ifdef BIG_ENDIAN +/* These could be more efficient, given that we are always memory mapped, + * but they don't give the same problems as the write macros, so leave + * them. */ +#define NCR53c7x0_read8(address) \ + (NCR53c7x0_memory_mapped ? \ + (unsigned int)readb((u32)NCR53c7x0_address_memory + ((u32)(address)^3)) : \ + inb(NCR53c7x0_address_io + (address))) + +#define NCR53c7x0_read16(address) \ + (NCR53c7x0_memory_mapped ? \ + (unsigned int)readw((u32)NCR53c7x0_address_memory + ((u32)(address)^2)) : \ + inw(NCR53c7x0_address_io + (address))) +#else +#define NCR53c7x0_read8(address) \ + (NCR53c7x0_memory_mapped ? \ + (unsigned int)readb((u32)NCR53c7x0_address_memory + (u32)(address)) : \ + inb(NCR53c7x0_address_io + (address))) + +#define NCR53c7x0_read16(address) \ + (NCR53c7x0_memory_mapped ? \ + (unsigned int)readw((u32)NCR53c7x0_address_memory + (u32)(address)) : \ + inw(NCR53c7x0_address_io + (address))) +#endif +#define NCR53c7x0_read32(address) \ + (NCR53c7x0_memory_mapped ? \ + (unsigned int) readl((u32)NCR53c7x0_address_memory + (u32)(address)) : \ + inl(NCR53c7x0_address_io + (address))) + +#ifdef BIG_ENDIAN +/* If we are big-endian, then we are not Intel, so probably don't have + * an i/o map as well as a memory map. So, lets assume memory mapped. + * Also, I am having terrible problems trying to persuade the compiler + * not to lay down code which does a read after write for these macros. + * If you remove 'volatile' from writeb() and friends it is ok.... + */ + +#define NCR53c7x0_write8(address,value) \ + *(volatile unsigned char *) \ + ((u32)NCR53c7x0_address_memory + ((u32)(address)^3)) = (value) + +#define NCR53c7x0_write16(address,value) \ + *(volatile unsigned short *) \ + ((u32)NCR53c7x0_address_memory + ((u32)(address)^2)) = (value) + +#define NCR53c7x0_write32(address,value) \ + *(volatile unsigned long *) \ + ((u32)NCR53c7x0_address_memory + ((u32)(address))) = (value) + +#else + +#define NCR53c7x0_write8(address,value) \ + (NCR53c7x0_memory_mapped ? \ + ({writeb((value), (u32)NCR53c7x0_address_memory + (u32)(address)); mb();}) : \ + outb((value), NCR53c7x0_address_io + (address))) + +#define NCR53c7x0_write16(address,value) \ + (NCR53c7x0_memory_mapped ? \ + ({writew((value), (u32)NCR53c7x0_address_memory + (u32)(address)); mb();}) : \ + outw((value), NCR53c7x0_address_io + (address))) + +#define NCR53c7x0_write32(address,value) \ + (NCR53c7x0_memory_mapped ? \ + ({writel((value), (u32)NCR53c7x0_address_memory + (u32)(address)); mb();}) : \ + outl((value), NCR53c7x0_address_io + (address))) + +#endif + +/* Patch arbitrary 32 bit words in the script */ +#define patch_abs_32(script, offset, symbol, value) \ + for (i = 0; i < (sizeof (A_##symbol##_used) / sizeof \ + (u32)); ++i) { \ + (script)[A_##symbol##_used[i] - (offset)] += (value); \ + if (hostdata->options & OPTION_DEBUG_FIXUP) \ + printk("scsi%d : %s reference %d at 0x%x in %s is now 0x%x\n",\ + host->host_no, #symbol, i, A_##symbol##_used[i] - \ + (int)(offset), #script, (script)[A_##symbol##_used[i] - \ + (offset)]); \ + } + +/* Patch read/write instruction immediate field */ +#define patch_abs_rwri_data(script, offset, symbol, value) \ + for (i = 0; i < (sizeof (A_##symbol##_used) / sizeof \ + (u32)); ++i) \ + (script)[A_##symbol##_used[i] - (offset)] = \ + ((script)[A_##symbol##_used[i] - (offset)] & \ + ~DBC_RWRI_IMMEDIATE_MASK) | \ + (((value) << DBC_RWRI_IMMEDIATE_SHIFT) & \ + DBC_RWRI_IMMEDIATE_MASK) + +/* Patch transfer control instruction data field */ +#define patch_abs_tci_data(script, offset, symbol, value) \ + for (i = 0; i < (sizeof (A_##symbol##_used) / sizeof \ + (u32)); ++i) \ + (script)[A_##symbol##_used[i] - (offset)] = \ + ((script)[A_##symbol##_used[i] - (offset)] & \ + ~DBC_TCI_DATA_MASK) | \ + (((value) << DBC_TCI_DATA_SHIFT) & \ + DBC_TCI_DATA_MASK) + +/* Patch field in dsa structure (assignment should be +=?) */ +#define patch_dsa_32(dsa, symbol, word, value) \ + { \ + (dsa)[(hostdata->##symbol - hostdata->dsa_start) / sizeof(u32) \ + + (word)] = (value); \ + if (hostdata->options & OPTION_DEBUG_DSA) \ + printk("scsi : dsa %s symbol %s(%d) word %d now 0x%x\n", \ + #dsa, #symbol, hostdata->##symbol, \ + (word), (u32) (value)); \ + } + +/* Paranoid people could use panic() here. */ +#define FATAL(host) shutdown((host)); + +#endif /* NCR53c710_C */ +#endif /* NCR53c710_H */ diff -u --recursive --new-file v2.1.39/linux/drivers/scsi/53c7xx.scr linux/drivers/scsi/53c7xx.scr --- v2.1.39/linux/drivers/scsi/53c7xx.scr Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/53c7xx.scr Sun May 18 17:10:37 1997 @@ -0,0 +1,1591 @@ +#undef DEBUG +#undef EVENTS +#undef NO_SELECTION_TIMEOUT +#define BIG_ENDIAN + +; 53c710 driver. Modified from Drew Eckhardts driver +; for 53c810 by Richard Hirst [richard@sleepie.demon.co.uk] +; +; I have left the script for the 53c8xx family in here, as it is likely +; to be useful to see what I changed when bug hunting. + +; NCR 53c810 driver, main script +; Sponsored by +; iX Multiuser Multitasking Magazine +; hm@ix.de +; +; Copyright 1993, 1994, 1995 Drew Eckhardt +; Visionary Computing +; (Unix and Linux consulting and custom programming) +; drew@PoohSticks.ORG +; +1 (303) 786-7975 +; +; TolerANT and SCSI SCRIPTS are registered trademarks of NCR Corporation. +; +; PRE-ALPHA +; +; For more information, please consult +; +; NCR 53C810 +; PCI-SCSI I/O Processor +; Data Manual +; +; NCR 53C710 +; SCSI I/O Processor +; Programmers Guide +; +; NCR Microelectronics +; 1635 Aeroplaza Drive +; Colorado Springs, CO 80916 +; 1+ (719) 578-3400 +; +; Toll free literature number +; +1 (800) 334-5454 +; +; IMPORTANT : This code is self modifying due to the limitations of +; the NCR53c7,8xx series chips. Persons debugging this code with +; the remote debugger should take this into account, and NOT set +; breakpoints in modified instructions. +; +; Design: +; The NCR53c7,8xx family of SCSI chips are busmasters with an onboard +; microcontroller using a simple instruction set. +; +; So, to minimize the effects of interrupt latency, and to maximize +; throughput, this driver offloads the practical maximum amount +; of processing to the SCSI chip while still maintaining a common +; structure. +; +; Where tradeoffs were needed between efficiency on the older +; chips and the newer NCR53c800 series, the NCR53c800 series +; was chosen. +; +; While the NCR53c700 and NCR53c700-66 lacked the facilities to fully +; automate SCSI transfers without host processor intervention, this +; isn't the case with the NCR53c710 and newer chips which allow +; +; - reads and writes to the internal registers from within the SCSI +; scripts, allowing the SCSI SCRIPTS(tm) code to save processor +; state so that multiple threads of execution are possible, and also +; provide an ALU for loop control, etc. +; +; - table indirect addressing for some instructions. This allows +; pointers to be located relative to the DSA ((Data Structure +; Address) register. +; +; These features make it possible to implement a mailbox style interface, +; where the same piece of code is run to handle I/O for multiple threads +; at once minimizing our need to relocate code. Since the NCR53c700/ +; NCR53c800 series have a unique combination of features, making a +; a standard ingoing/outgoing mailbox system, costly, I've modified it. +; +; - Mailboxes are a mixture of code and data. This lets us greatly +; simplify the NCR53c810 code and do things that would otherwise +; not be possible. +; +; The saved data pointer is now implemented as follows : +; +; Control flow has been architected such that if control reaches +; munge_save_data_pointer, on a restore pointers message or +; reconnection, a jump to the address formerly in the TEMP register +; will allow the SCSI command to resume execution. +; + +; +; Note : the DSA structures must be aligned on 32 bit boundaries, +; since the source and destination of MOVE MEMORY instructions +; must share the same alignment and this is the alignment of the +; NCR registers. +; + +; For some systems (MVME166, for example) dmode is always the same, so don't +; waste time writing it + +#if 1 +#define DMODE_MEMORY_TO_NCR +#define DMODE_MEMORY_TO_MEMORY +#define DMODE_NCR_TO_MEMORY +#else +#define DMODE_MEMORY_TO_NCR MOVE dmode_memory_to_ncr TO DMODE +#define DMODE_MEMORY_TO_MEMORY MOVE dmode_memory_to_memory TO DMODE +#define DMODE_NCR_TO_MEMORY MOVE dmode_ncr_to_memory TO DMODE +#endif + +ABSOLUTE dsa_temp_lun = 0 ; Patch to lun for current dsa +ABSOLUTE dsa_temp_next = 0 ; Patch to dsa next for current dsa +ABSOLUTE dsa_temp_addr_next = 0 ; Patch to address of dsa next address + ; for current dsa +ABSOLUTE dsa_temp_sync = 0 ; Patch to address of per-target + ; sync routine +ABSOLUTE dsa_sscf_710 = 0 ; Patch to address of per-target + ; sscf value (53c710) +ABSOLUTE dsa_temp_target = 0 ; Patch to id for current dsa +ABSOLUTE dsa_temp_addr_saved_pointer = 0; Patch to address of per-command + ; saved data pointer +ABSOLUTE dsa_temp_addr_residual = 0 ; Patch to address of per-command + ; current residual code +ABSOLUTE dsa_temp_addr_saved_residual = 0; Patch to address of per-command + ; saved residual code +ABSOLUTE dsa_temp_addr_new_value = 0 ; Address of value for JUMP operand +ABSOLUTE dsa_temp_addr_array_value = 0 ; Address to copy to +ABSOLUTE dsa_temp_addr_dsa_value = 0 ; Address of this DSA value + +; +; Once a device has initiated reselection, we need to compare it +; against the singly linked list of commands which have disconnected +; and are pending reselection. These commands are maintained in +; an unordered singly linked list of DSA structures, through the +; DSA pointers at their 'centers' headed by the reconnect_dsa_head +; pointer. +; +; To avoid complications in removing commands from the list, +; I minimize the amount of expensive (at eight operations per +; addition @ 500-600ns each) pointer operations which must +; be done in the NCR driver by precomputing them on the +; host processor during dsa structure generation. +; +; The fixed-up per DSA code knows how to recognize the nexus +; associated with the corresponding SCSI command, and modifies +; the source and destination pointers for the MOVE MEMORY +; instruction which is executed when reselected_ok is called +; to remove the command from the list. Similarly, DSA is +; loaded with the address of the next DSA structure and +; reselected_check_next is called if a failure occurs. +; +; Perhaps more concisely, the net effect of the mess is +; +; for (dsa = reconnect_dsa_head, dest = &reconnect_dsa_head, +; src = NULL; dsa; dest = &dsa->next, dsa = dsa->next) { +; src = &dsa->next; +; if (target_id == dsa->id && target_lun == dsa->lun) { +; *dest = *src; +; break; +; } +; } +; +; if (!dsa) +; error (int_err_unexpected_reselect); +; else +; longjmp (dsa->jump_resume, 0); +; +; + +#if (CHIP != 700) && (CHIP != 70066) +; Define DSA structure used for mailboxes +ENTRY dsa_code_template +dsa_code_template: +ENTRY dsa_code_begin +dsa_code_begin: +; RGH: Don't care about TEMP and DSA here + DMODE_MEMORY_TO_NCR + MOVE MEMORY 4, dsa_temp_addr_dsa_value, addr_scratch + DMODE_MEMORY_TO_MEMORY +#if (CHIP == 710) + MOVE MEMORY 4, addr_scratch, saved_dsa + ; We are about to go and select the device, so must set SSCF bits + MOVE MEMORY 4, dsa_sscf_710, addr_scratch +#ifdef BIG_ENDIAN + MOVE SCRATCH3 TO SFBR +#else + MOVE SCRATCH0 TO SFBR +#endif + MOVE SFBR TO SBCL + MOVE MEMORY 4, saved_dsa, addr_dsa +#else + CALL scratch_to_dsa +#endif + CALL select +; Handle the phase mismatch which may have resulted from the +; MOVE FROM dsa_msgout if we returned here. The CLEAR ATN +; may or may not be necessary, and we should update script_asm.pl +; to handle multiple pieces. + CLEAR ATN + CLEAR ACK + +; Replace second operand with address of JUMP instruction dest operand +; in schedule table for this DSA. Becomes dsa_jump_dest in 53c7,8xx.c. +ENTRY dsa_code_fix_jump +dsa_code_fix_jump: + MOVE MEMORY 4, NOP_insn, 0 + JUMP select_done + +; wrong_dsa loads the DSA register with the value of the dsa_next +; field. +; +wrong_dsa: +#if (CHIP == 710) +; NOTE DSA is corrupt when we arrive here! +#endif +; Patch the MOVE MEMORY INSTRUCTION such that +; the destination address is the address of the OLD +; next pointer. +; + MOVE MEMORY 4, dsa_temp_addr_next, reselected_ok_patch + 8 + DMODE_MEMORY_TO_NCR +; +; Move the _contents_ of the next pointer into the DSA register as +; the next I_T_L or I_T_L_Q tupple to check against the established +; nexus. +; + MOVE MEMORY 4, dsa_temp_next, addr_scratch + DMODE_MEMORY_TO_MEMORY +#if (CHIP == 710) + MOVE MEMORY 4, addr_scratch, saved_dsa + MOVE MEMORY 4, saved_dsa, addr_dsa +#else + CALL scratch_to_dsa +#endif + JUMP reselected_check_next + +ABSOLUTE dsa_save_data_pointer = 0 +ENTRY dsa_code_save_data_pointer +dsa_code_save_data_pointer: +#if (CHIP == 710) + ; When we get here, TEMP has been saved in jump_temp+4, DSA is corrupt + ; We MUST return with DSA correct + MOVE MEMORY 4, jump_temp+4, dsa_temp_addr_saved_pointer +; HARD CODED : 24 bytes needs to agree with 53c7,8xx.h + MOVE MEMORY 24, dsa_temp_addr_residual, dsa_temp_addr_saved_residual + CLEAR ACK +#ifdef DEBUG + INT int_debug_saved +#endif + MOVE MEMORY 4, saved_dsa, addr_dsa + JUMP jump_temp +#else + DMODE_NCR_TO_MEMORY + MOVE MEMORY 4, addr_temp, dsa_temp_addr_saved_pointer + DMODE_MEMORY_TO_MEMORY +; HARD CODED : 24 bytes needs to agree with 53c7,8xx.h + MOVE MEMORY 24, dsa_temp_addr_residual, dsa_temp_addr_saved_residual + CLEAR ACK +#ifdef DEBUG + INT int_debug_saved +#endif + RETURN +#endif +ABSOLUTE dsa_restore_pointers = 0 +ENTRY dsa_code_restore_pointers +dsa_code_restore_pointers: +#if (CHIP == 710) + ; TEMP and DSA are corrupt when we get here, but who cares! + MOVE MEMORY 4, dsa_temp_addr_saved_pointer, jump_temp + 4 +; HARD CODED : 24 bytes needs to agree with 53c7,8xx.h + MOVE MEMORY 24, dsa_temp_addr_saved_residual, dsa_temp_addr_residual + CLEAR ACK + ; Restore DSA, note we don't care about TEMP + MOVE MEMORY 4, saved_dsa, addr_dsa +#ifdef DEBUG + INT int_debug_restored +#endif + JUMP jump_temp +#else + DMODE_MEMORY_TO_NCR + MOVE MEMORY 4, dsa_temp_addr_saved_pointer, addr_temp + DMODE_MEMORY_TO_MEMORY +; HARD CODED : 24 bytes needs to agree with 53c7,8xx.h + MOVE MEMORY 24, dsa_temp_addr_saved_residual, dsa_temp_addr_residual + CLEAR ACK +#ifdef DEBUG + INT int_debug_restored +#endif + RETURN +#endif + +ABSOLUTE dsa_check_reselect = 0 +; dsa_check_reselect determines whether or not the current target and +; lun match the current DSA +ENTRY dsa_code_check_reselect +dsa_code_check_reselect: +#if (CHIP == 710) + /* Arrives here with DSA correct */ + /* Assumes we are always ID 7 */ + MOVE LCRC TO SFBR ; LCRC has our ID and his ID bits set + JUMP REL (wrong_dsa), IF NOT dsa_temp_target, AND MASK 0x80 +#else + MOVE SSID TO SFBR ; SSID contains 3 bit target ID +; FIXME : we need to accommodate bit fielded and binary here for '7xx/'8xx chips + JUMP REL (wrong_dsa), IF NOT dsa_temp_target, AND MASK 0xf8 +#endif +; +; Hack - move to scratch first, since SFBR is not writeable +; via the CPU and hence a MOVE MEMORY instruction. +; + DMODE_MEMORY_TO_NCR + MOVE MEMORY 1, reselected_identify, addr_scratch + DMODE_MEMORY_TO_MEMORY +#ifdef BIG_ENDIAN + ; BIG ENDIAN ON MVME166 + MOVE SCRATCH3 TO SFBR +#else + MOVE SCRATCH0 TO SFBR +#endif +; FIXME : we need to accommodate bit fielded and binary here for '7xx/'8xx chips +; Are you sure about that? richard@sleepie.demon.co.uk + JUMP REL (wrong_dsa), IF NOT dsa_temp_lun, AND MASK 0xf8 +; Patch the MOVE MEMORY INSTRUCTION such that +; the source address is the address of this dsa's +; next pointer. + MOVE MEMORY 4, dsa_temp_addr_next, reselected_ok_patch + 4 + CALL reselected_ok +#if (CHIP == 710) +; Restore DSA following memory moves in reselected_ok +; dsa_temp_sync doesn't really care about DSA, but it has an +; optional debug INT so a valid DSA is a good idea. + MOVE MEMORY 4, saved_dsa, addr_dsa +#endif + CALL dsa_temp_sync +; Release ACK on the IDENTIFY message _after_ we've set the synchronous +; transfer parameters! + CLEAR ACK +; Implicitly restore pointers on reselection, so a RETURN +; will transfer control back to the right spot. + CALL REL (dsa_code_restore_pointers) + RETURN +ENTRY dsa_zero +dsa_zero: +ENTRY dsa_code_template_end +dsa_code_template_end: + +; Perform sanity check for dsa_fields_start == dsa_code_template_end - +; dsa_zero, puke. + +ABSOLUTE dsa_fields_start = 0 ; Sanity marker + ; pad 48 bytes (fix this RSN) +ABSOLUTE dsa_next = 48 ; len 4 Next DSA + ; del 4 Previous DSA address +ABSOLUTE dsa_cmnd = 56 ; len 4 Scsi_Cmnd * for this thread. +ABSOLUTE dsa_select = 60 ; len 4 Device ID, Period, Offset for + ; table indirect select +ABSOLUTE dsa_msgout = 64 ; len 8 table indirect move parameter for + ; select message +ABSOLUTE dsa_cmdout = 72 ; len 8 table indirect move parameter for + ; command +ABSOLUTE dsa_dataout = 80 ; len 4 code pointer for dataout +ABSOLUTE dsa_datain = 84 ; len 4 code pointer for datain +ABSOLUTE dsa_msgin = 88 ; len 8 table indirect move for msgin +ABSOLUTE dsa_status = 96 ; len 8 table indirect move for status byte +ABSOLUTE dsa_msgout_other = 104 ; len 8 table indirect for normal message out + ; (Synchronous transfer negotiation, etc). +ABSOLUTE dsa_end = 112 + +ABSOLUTE schedule = 0 ; Array of JUMP dsa_begin or JUMP (next), + ; terminated by a call to JUMP wait_reselect + +; Linked lists of DSA structures +ABSOLUTE reconnect_dsa_head = 0 ; Link list of DSAs which can reconnect +ABSOLUTE addr_reconnect_dsa_head = 0 ; Address of variable containing + ; address of reconnect_dsa_head + +; These select the source and destination of a MOVE MEMORY instruction +ABSOLUTE dmode_memory_to_memory = 0x0 +ABSOLUTE dmode_memory_to_ncr = 0x0 +ABSOLUTE dmode_ncr_to_memory = 0x0 + +ABSOLUTE addr_scratch = 0x0 +ABSOLUTE addr_temp = 0x0 +#if (CHIP == 710) +ABSOLUTE saved_dsa = 0x0 +ABSOLUTE emulfly = 0x0 +ABSOLUTE addr_dsa = 0x0 +#endif +#endif /* CHIP != 700 && CHIP != 70066 */ + +; Interrupts - +; MSB indicates type +; 0 handle error condition +; 1 handle message +; 2 handle normal condition +; 3 debugging interrupt +; 4 testing interrupt +; Next byte indicates specific error + +; XXX not yet implemented, I'm not sure if I want to - +; Next byte indicates the routine the error occurred in +; The LSB indicates the specific place the error occurred + +ABSOLUTE int_err_unexpected_phase = 0x00000000 ; Unexpected phase encountered +ABSOLUTE int_err_selected = 0x00010000 ; SELECTED (nee RESELECTED) +ABSOLUTE int_err_unexpected_reselect = 0x00020000 +ABSOLUTE int_err_check_condition = 0x00030000 +ABSOLUTE int_err_no_phase = 0x00040000 +ABSOLUTE int_msg_wdtr = 0x01000000 ; WDTR message received +ABSOLUTE int_msg_sdtr = 0x01010000 ; SDTR received +ABSOLUTE int_msg_1 = 0x01020000 ; single byte special message + ; received + +ABSOLUTE int_norm_select_complete = 0x02000000 ; Select complete, reprogram + ; registers. +ABSOLUTE int_norm_reselect_complete = 0x02010000 ; Nexus established +ABSOLUTE int_norm_command_complete = 0x02020000 ; Command complete +ABSOLUTE int_norm_disconnected = 0x02030000 ; Disconnected +ABSOLUTE int_norm_aborted =0x02040000 ; Aborted *dsa +ABSOLUTE int_norm_reset = 0x02050000 ; Generated BUS reset. +ABSOLUTE int_norm_emulateintfly = 0x02060000 ; 53C710 Emulated intfly +ABSOLUTE int_debug_break = 0x03000000 ; Break point +#ifdef DEBUG +ABSOLUTE int_debug_scheduled = 0x03010000 ; new I/O scheduled +ABSOLUTE int_debug_idle = 0x03020000 ; scheduler is idle +ABSOLUTE int_debug_dsa_loaded = 0x03030000 ; dsa reloaded +ABSOLUTE int_debug_reselected = 0x03040000 ; NCR reselected +ABSOLUTE int_debug_head = 0x03050000 ; issue head overwritten +ABSOLUTE int_debug_disconnected = 0x03060000 ; disconnected +ABSOLUTE int_debug_disconnect_msg = 0x03070000 ; got message to disconnect +ABSOLUTE int_debug_dsa_schedule = 0x03080000 ; in dsa_schedule +ABSOLUTE int_debug_reselect_check = 0x03090000 ; Check for reselection of DSA +ABSOLUTE int_debug_reselected_ok = 0x030a0000 ; Reselection accepted +#endif +ABSOLUTE int_debug_panic = 0x030b0000 ; Panic driver +#ifdef DEBUG +ABSOLUTE int_debug_saved = 0x030c0000 ; save/restore pointers +ABSOLUTE int_debug_restored = 0x030d0000 +ABSOLUTE int_debug_sync = 0x030e0000 ; Sanity check synchronous + ; parameters. +ABSOLUTE int_debug_datain = 0x030f0000 ; going into data in phase + ; now. +ABSOLUTE int_debug_check_dsa = 0x03100000 ; Sanity check DSA against + ; SDID. +#endif + +ABSOLUTE int_test_1 = 0x04000000 ; Test 1 complete +ABSOLUTE int_test_2 = 0x04010000 ; Test 2 complete +ABSOLUTE int_test_3 = 0x04020000 ; Test 3 complete + + +; These should start with 0x05000000, with low bits incrementing for +; each one. + +#ifdef EVENTS +ABSOLUTE int_EVENT_SELECT = 0 +ABSOLUTE int_EVENT_DISCONNECT = 0 +ABSOLUTE int_EVENT_RESELECT = 0 +ABSOLUTE int_EVENT_COMPLETE = 0 +ABSOLUTE int_EVENT_IDLE = 0 +ABSOLUTE int_EVENT_SELECT_FAILED = 0 +ABSOLUTE int_EVENT_BEFORE_SELECT = 0 +ABSOLUTE int_EVENT_RESELECT_FAILED = 0 +#endif + +ABSOLUTE NCR53c7xx_msg_abort = 0 ; Pointer to abort message +ABSOLUTE NCR53c7xx_msg_reject = 0 ; Pointer to reject message +ABSOLUTE NCR53c7xx_zero = 0 ; long with zero in it, use for source +ABSOLUTE NCR53c7xx_sink = 0 ; long to dump worthless data in +ABSOLUTE NOP_insn = 0 ; NOP instruction + +; Pointer to message, potentially multi-byte +ABSOLUTE msg_buf = 0 + +; Pointer to holding area for reselection information +ABSOLUTE reselected_identify = 0 +ABSOLUTE reselected_tag = 0 + +; Request sense command pointer, it's a 6 byte command, should +; be constant for all commands since we always want 16 bytes of +; sense and we don't need to change any fields as we did under +; SCSI-I when we actually cared about the LUN field. +;EXTERNAL NCR53c7xx_sense ; Request sense command + +#if (CHIP != 700) && (CHIP != 70066) +; dsa_schedule +; PURPOSE : after a DISCONNECT message has been received, and pointers +; saved, insert the current DSA structure at the head of the +; disconnected queue and fall through to the scheduler. +; +; CALLS : OK +; +; INPUTS : dsa - current DSA structure, reconnect_dsa_head - list +; of disconnected commands +; +; MODIFIES : SCRATCH, reconnect_dsa_head +; +; EXITS : always passes control to schedule + +ENTRY dsa_schedule +dsa_schedule: +#ifdef DEBUG + INT int_debug_dsa_schedule +#endif + +; +; Calculate the address of the next pointer within the DSA +; structure of the command that is currently disconnecting +; +#if (CHIP == 710) + ; Read what should be the current DSA from memory - actual DSA + ; register is probably corrupt + MOVE MEMORY 4, saved_dsa, addr_scratch +#else + CALL dsa_to_scratch +#endif + MOVE SCRATCH0 + dsa_next TO SCRATCH0 + MOVE SCRATCH1 + 0 TO SCRATCH1 WITH CARRY + MOVE SCRATCH2 + 0 TO SCRATCH2 WITH CARRY + MOVE SCRATCH3 + 0 TO SCRATCH3 WITH CARRY + +; Point the next field of this DSA structure at the current disconnected +; list + DMODE_NCR_TO_MEMORY + MOVE MEMORY 4, addr_scratch, dsa_schedule_insert + 8 + DMODE_MEMORY_TO_MEMORY +dsa_schedule_insert: + MOVE MEMORY 4, reconnect_dsa_head, 0 + +; And update the head pointer. +#if (CHIP == 710) + ; Read what should be the current DSA from memory - actual DSA + ; register is probably corrupt + MOVE MEMORY 4, saved_dsa, addr_scratch +#else + CALL dsa_to_scratch +#endif + DMODE_NCR_TO_MEMORY + MOVE MEMORY 4, addr_scratch, reconnect_dsa_head + DMODE_MEMORY_TO_MEMORY +/* Temporarily, see what happens. */ +#ifndef ORIGINAL +#if (CHIP != 710) + MOVE SCNTL2 & 0x7f TO SCNTL2 +#endif + CLEAR ACK +#endif +#if (CHIP == 710) + ; Time to correct DSA following memory move + MOVE MEMORY 4, saved_dsa, addr_dsa +#endif + WAIT DISCONNECT +#ifdef EVENTS + INT int_EVENT_DISCONNECT; +#endif +#ifdef DEBUG + INT int_debug_disconnected +#endif + JUMP schedule +#endif + +; +; select +; +; PURPOSE : establish a nexus for the SCSI command referenced by DSA. +; On success, the current DSA structure is removed from the issue +; queue. Usually, this is entered as a fall-through from schedule, +; although the contingent allegiance handling code will write +; the select entry address to the DSP to restart a command as a +; REQUEST SENSE. A message is sent (usually IDENTIFY, although +; additional SDTR or WDTR messages may be sent). COMMAND OUT +; is handled. +; +; INPUTS : DSA - SCSI command, issue_dsa_head +; +; CALLS : NOT OK +; +; MODIFIES : SCRATCH, issue_dsa_head +; +; EXITS : on reselection or selection, go to select_failed +; otherwise, RETURN so control is passed back to +; dsa_begin. +; + +ENTRY select +select: + +#ifdef EVENTS + INT int_EVENT_BEFORE_SELECT +#endif + +#ifdef DEBUG + INT int_debug_scheduled +#endif + CLEAR TARGET + +; XXX +; +; In effect, SELECTION operations are backgrounded, with execution +; continuing until code which waits for REQ or a fatal interrupt is +; encountered. +; +; So, for more performance, we could overlap the code which removes +; the command from the NCRs issue queue with the selection, but +; at this point I don't want to deal with the error recovery. +; + +#if (CHIP != 700) && (CHIP != 70066) +#if (CHIP == 710) + ; Enable selection timer +#ifdef NO_SELECTION_TIMEOUT + MOVE CTEST7 & 0xff TO CTEST7 +#else + MOVE CTEST7 & 0xef TO CTEST7 +#endif +#endif + SELECT ATN FROM dsa_select, select_failed + JUMP select_msgout, WHEN MSG_OUT +ENTRY select_msgout +select_msgout: +#if (CHIP == 710) + ; Disable selection timer + MOVE CTEST7 | 0x10 TO CTEST7 +#endif + MOVE FROM dsa_msgout, WHEN MSG_OUT +#else +ENTRY select_msgout + SELECT ATN 0, select_failed +select_msgout: + MOVE 0, 0, WHEN MSGOUT +#endif + +#ifdef EVENTS + INT int_EVENT_SELECT +#endif + RETURN + +; +; select_done +; +; PURPOSE: continue on to normal data transfer; called as the exit +; point from dsa_begin. +; +; INPUTS: dsa +; +; CALLS: OK +; +; + +select_done: +#if (CHIP == 710) +; NOTE DSA is corrupt when we arrive here! + MOVE MEMORY 4, saved_dsa, addr_dsa +#endif + +#ifdef DEBUG +ENTRY select_check_dsa +select_check_dsa: + INT int_debug_check_dsa +#endif + +; After a successful selection, we should get either a CMD phase or +; some transfer request negotiation message. + + JUMP cmdout, WHEN CMD + INT int_err_unexpected_phase, WHEN NOT MSG_IN + +select_msg_in: + CALL msg_in, WHEN MSG_IN + JUMP select_msg_in, WHEN MSG_IN + +cmdout: + INT int_err_unexpected_phase, WHEN NOT CMD +#if (CHIP == 700) + INT int_norm_selected +#endif +ENTRY cmdout_cmdout +cmdout_cmdout: +#if (CHIP != 700) && (CHIP != 70066) + MOVE FROM dsa_cmdout, WHEN CMD +#else + MOVE 0, 0, WHEN CMD +#endif /* (CHIP != 700) && (CHIP != 70066) */ + +; +; data_transfer +; other_out +; other_in +; other_transfer +; +; PURPOSE : handle the main data transfer for a SCSI command in +; several parts. In the first part, data_transfer, DATA_IN +; and DATA_OUT phases are allowed, with the user provided +; code (usually dynamically generated based on the scatter/gather +; list associated with a SCSI command) called to handle these +; phases. +; +; After control has passed to one of the user provided +; DATA_IN or DATA_OUT routines, back calls are made to +; other_transfer_in or other_transfer_out to handle non-DATA IN +; and DATA OUT phases respectively, with the state of the active +; data pointer being preserved in TEMP. +; +; On completion, the user code passes control to other_transfer +; which causes DATA_IN and DATA_OUT to result in unexpected_phase +; interrupts so that data overruns may be trapped. +; +; INPUTS : DSA - SCSI command +; +; CALLS : OK in data_transfer_start, not ok in other_out and other_in, ok in +; other_transfer +; +; MODIFIES : SCRATCH +; +; EXITS : if STATUS IN is detected, signifying command completion, +; the NCR jumps to command_complete. If MSG IN occurs, a +; CALL is made to msg_in. Otherwise, other_transfer runs in +; an infinite loop. +; + +ENTRY data_transfer +data_transfer: + JUMP cmdout_cmdout, WHEN CMD + CALL msg_in, WHEN MSG_IN + INT int_err_unexpected_phase, WHEN MSG_OUT + JUMP do_dataout, WHEN DATA_OUT + JUMP do_datain, WHEN DATA_IN + JUMP command_complete, WHEN STATUS + JUMP data_transfer +ENTRY end_data_transfer +end_data_transfer: + +; +; FIXME: On NCR53c700 and NCR53c700-66 chips, do_dataout/do_datain +; should be fixed up whenever the nexus changes so it can point to the +; correct routine for that command. +; + +#if (CHIP != 700) && (CHIP != 70066) +; Nasty jump to dsa->dataout +do_dataout: +#if (CHIP == 710) + MOVE MEMORY 4, saved_dsa, addr_scratch +#else + CALL dsa_to_scratch +#endif + MOVE SCRATCH0 + dsa_dataout TO SCRATCH0 + MOVE SCRATCH1 + 0 TO SCRATCH1 WITH CARRY + MOVE SCRATCH2 + 0 TO SCRATCH2 WITH CARRY + MOVE SCRATCH3 + 0 TO SCRATCH3 WITH CARRY + DMODE_NCR_TO_MEMORY + MOVE MEMORY 4, addr_scratch, dataout_to_jump + 4 + DMODE_MEMORY_TO_MEMORY +dataout_to_jump: + MOVE MEMORY 4, 0, dataout_jump + 4 +#if (CHIP == 710) + ; Time to correct DSA following memory move + MOVE MEMORY 4, saved_dsa, addr_dsa +#endif +dataout_jump: + JUMP 0 + +; Nasty jump to dsa->dsain +do_datain: +#if (CHIP == 710) + MOVE MEMORY 4, saved_dsa, addr_scratch +#else + CALL dsa_to_scratch +#endif + MOVE SCRATCH0 + dsa_datain TO SCRATCH0 + MOVE SCRATCH1 + 0 TO SCRATCH1 WITH CARRY + MOVE SCRATCH2 + 0 TO SCRATCH2 WITH CARRY + MOVE SCRATCH3 + 0 TO SCRATCH3 WITH CARRY + DMODE_NCR_TO_MEMORY + MOVE MEMORY 4, addr_scratch, datain_to_jump + 4 + DMODE_MEMORY_TO_MEMORY +ENTRY datain_to_jump +datain_to_jump: + MOVE MEMORY 4, 0, datain_jump + 4 +#if (CHIP == 710) + ; Time to correct DSA following memory move + MOVE MEMORY 4, saved_dsa, addr_dsa +#endif +#ifdef DEBUG + INT int_debug_datain +#endif +datain_jump: + JUMP 0 +#endif /* (CHIP != 700) && (CHIP != 70066) */ + + +; Note that other_out and other_in loop until a non-data phase +; is discovered, so we only execute return statements when we +; can go on to the next data phase block move statement. + +ENTRY other_out +other_out: +#if 0 + INT 0x03ffdead +#endif + INT int_err_unexpected_phase, WHEN CMD + JUMP msg_in_restart, WHEN MSG_IN + INT int_err_unexpected_phase, WHEN MSG_OUT + INT int_err_unexpected_phase, WHEN DATA_IN + JUMP command_complete, WHEN STATUS + JUMP other_out, WHEN NOT DATA_OUT +#if (CHIP == 710) +; TEMP should be OK, as we got here from a call in the user dataout code. +#endif + RETURN + +ENTRY other_in +other_in: +#if 0 + INT 0x03ffdead +#endif + INT int_err_unexpected_phase, WHEN CMD + JUMP msg_in_restart, WHEN MSG_IN + INT int_err_unexpected_phase, WHEN MSG_OUT + INT int_err_unexpected_phase, WHEN DATA_OUT + JUMP command_complete, WHEN STATUS + JUMP other_in, WHEN NOT DATA_IN +#if (CHIP == 710) +; TEMP should be OK, as we got here from a call in the user datain code. +#endif + RETURN + + +ENTRY other_transfer +other_transfer: + INT int_err_unexpected_phase, WHEN CMD + CALL msg_in, WHEN MSG_IN + INT int_err_unexpected_phase, WHEN MSG_OUT + INT int_err_unexpected_phase, WHEN DATA_OUT + INT int_err_unexpected_phase, WHEN DATA_IN + JUMP command_complete, WHEN STATUS + JUMP other_transfer + +; +; msg_in_restart +; msg_in +; munge_msg +; +; PURPOSE : process messages from a target. msg_in is called when the +; caller hasn't read the first byte of the message. munge_message +; is called when the caller has read the first byte of the message, +; and left it in SFBR. msg_in_restart is called when the caller +; hasn't read the first byte of the message, and wishes RETURN +; to transfer control back to the address of the conditional +; CALL instruction rather than to the instruction after it. +; +; Various int_* interrupts are generated when the host system +; needs to intervene, as is the case with SDTR, WDTR, and +; INITIATE RECOVERY messages. +; +; When the host system handles one of these interrupts, +; it can respond by reentering at reject_message, +; which rejects the message and returns control to +; the caller of msg_in or munge_msg, accept_message +; which clears ACK and returns control, or reply_message +; which sends the message pointed to by the DSA +; msgout_other table indirect field. +; +; DISCONNECT messages are handled by moving the command +; to the reconnect_dsa_queue. +#if (CHIP == 710) +; NOTE: DSA should be valid when we get here - we cannot save both it +; and TEMP in this routine. +#endif +; +; INPUTS : DSA - SCSI COMMAND, SFBR - first byte of message (munge_msg +; only) +; +; CALLS : NO. The TEMP register isn't backed up to allow nested calls. +; +; MODIFIES : SCRATCH, DSA on DISCONNECT +; +; EXITS : On receipt of SAVE DATA POINTER, RESTORE POINTERS, +; and normal return from message handlers running under +; Linux, control is returned to the caller. Receipt +; of DISCONNECT messages pass control to dsa_schedule. +; +ENTRY msg_in_restart +msg_in_restart: +; XXX - hackish +; +; Since it's easier to debug changes to the statically +; compiled code, rather than the dynamically generated +; stuff, such as +; +; MOVE x, y, WHEN data_phase +; CALL other_z, WHEN NOT data_phase +; MOVE x, y, WHEN data_phase +; +; I'd like to have certain routines (notably the message handler) +; restart on the conditional call rather than the next instruction. +; +; So, subtract 8 from the return address + + MOVE TEMP0 + 0xf8 TO TEMP0 + MOVE TEMP1 + 0xff TO TEMP1 WITH CARRY + MOVE TEMP2 + 0xff TO TEMP2 WITH CARRY + MOVE TEMP3 + 0xff TO TEMP3 WITH CARRY + +ENTRY msg_in +msg_in: + MOVE 1, msg_buf, WHEN MSG_IN + +munge_msg: + JUMP munge_extended, IF 0x01 ; EXTENDED MESSAGE + JUMP munge_2, IF 0x20, AND MASK 0xdf ; two byte message +; +; XXX - I've seen a handful of broken SCSI devices which fail to issue +; a SAVE POINTERS message before disconnecting in the middle of +; a transfer, assuming that the DATA POINTER will be implicitly +; restored. +; +; Historically, I've often done an implicit save when the DISCONNECT +; message is processed. We may want to consider having the option of +; doing that here. +; + JUMP munge_save_data_pointer, IF 0x02 ; SAVE DATA POINTER + JUMP munge_restore_pointers, IF 0x03 ; RESTORE POINTERS + JUMP munge_disconnect, IF 0x04 ; DISCONNECT + INT int_msg_1, IF 0x07 ; MESSAGE REJECT + INT int_msg_1, IF 0x0f ; INITIATE RECOVERY +#ifdef EVENTS + INT int_EVENT_SELECT_FAILED +#endif + JUMP reject_message + +munge_2: + JUMP reject_message +; +; The SCSI standard allows targets to recover from transient +; error conditions by backing up the data pointer with a +; RESTORE POINTERS message. +; +; So, we must save and restore the _residual_ code as well as +; the current instruction pointer. Because of this messiness, +; it is simpler to put dynamic code in the dsa for this and to +; just do a simple jump down there. +; + +munge_save_data_pointer: +#if (CHIP == 710) + ; We have something in TEMP here, so first we must save that + MOVE TEMP0 TO SFBR + MOVE SFBR TO SCRATCH0 + MOVE TEMP1 TO SFBR + MOVE SFBR TO SCRATCH1 + MOVE TEMP2 TO SFBR + MOVE SFBR TO SCRATCH2 + MOVE TEMP3 TO SFBR + MOVE SFBR TO SCRATCH3 + MOVE MEMORY 4, addr_scratch, jump_temp + 4 + ; Now restore DSA + MOVE MEMORY 4, saved_dsa, addr_dsa +#endif + MOVE DSA0 + dsa_save_data_pointer TO SFBR + MOVE SFBR TO SCRATCH0 + MOVE DSA1 + 0xff TO SFBR WITH CARRY + MOVE SFBR TO SCRATCH1 + MOVE DSA2 + 0xff TO SFBR WITH CARRY + MOVE SFBR TO SCRATCH2 + MOVE DSA3 + 0xff TO SFBR WITH CARRY + MOVE SFBR TO SCRATCH3 + + DMODE_NCR_TO_MEMORY + MOVE MEMORY 4, addr_scratch, jump_dsa_save + 4 + DMODE_MEMORY_TO_MEMORY +jump_dsa_save: + JUMP 0 + +munge_restore_pointers: +#if (CHIP == 710) + ; The code at dsa_restore_pointers will RETURN, but we don't care + ; about TEMP here, as it will overwrite it anyway. +#endif + MOVE DSA0 + dsa_restore_pointers TO SFBR + MOVE SFBR TO SCRATCH0 + MOVE DSA1 + 0xff TO SFBR WITH CARRY + MOVE SFBR TO SCRATCH1 + MOVE DSA2 + 0xff TO SFBR WITH CARRY + MOVE SFBR TO SCRATCH2 + MOVE DSA3 + 0xff TO SFBR WITH CARRY + MOVE SFBR TO SCRATCH3 + + DMODE_NCR_TO_MEMORY + MOVE MEMORY 4, addr_scratch, jump_dsa_restore + 4 + DMODE_MEMORY_TO_MEMORY +jump_dsa_restore: + JUMP 0 + + +munge_disconnect: +#ifdef DEBUG + INT int_debug_disconnect_msg +#endif + +/* + * Before, we overlapped processing with waiting for disconnect, but + * debugging was beginning to appear messy. Temporarily move things + * to just before the WAIT DISCONNECT. + */ + +#ifdef ORIGINAL +#if (CHIP == 710) +; Following clears Unexpected Disconnect bit. What do we do? +#else + MOVE SCNTL2 & 0x7f TO SCNTL2 +#endif + CLEAR ACK +#endif + +#if (CHIP != 700) && (CHIP != 70066) + JUMP dsa_schedule +#else + WAIT DISCONNECT + INT int_norm_disconnected +#endif + +munge_extended: + CLEAR ACK + INT int_err_unexpected_phase, WHEN NOT MSG_IN + MOVE 1, msg_buf + 1, WHEN MSG_IN + JUMP munge_extended_2, IF 0x02 + JUMP munge_extended_3, IF 0x03 + JUMP reject_message + +munge_extended_2: + CLEAR ACK + MOVE 1, msg_buf + 2, WHEN MSG_IN + JUMP reject_message, IF NOT 0x02 ; Must be WDTR + CLEAR ACK + MOVE 1, msg_buf + 3, WHEN MSG_IN + INT int_msg_wdtr + +munge_extended_3: + CLEAR ACK + MOVE 1, msg_buf + 2, WHEN MSG_IN + JUMP reject_message, IF NOT 0x01 ; Must be SDTR + CLEAR ACK + MOVE 2, msg_buf + 3, WHEN MSG_IN + INT int_msg_sdtr + +ENTRY reject_message +reject_message: + SET ATN + CLEAR ACK + MOVE 1, NCR53c7xx_msg_reject, WHEN MSG_OUT + RETURN + +ENTRY accept_message +accept_message: + CLEAR ATN + CLEAR ACK + RETURN + +ENTRY respond_message +respond_message: + SET ATN + CLEAR ACK + MOVE FROM dsa_msgout_other, WHEN MSG_OUT + RETURN + +; +; command_complete +; +; PURPOSE : handle command termination when STATUS IN is detected by reading +; a status byte followed by a command termination message. +; +; Normal termination results in an INTFLY instruction, and +; the host system can pick out which command terminated by +; examining the MESSAGE and STATUS buffers of all currently +; executing commands; +; +; Abnormal (CHECK_CONDITION) termination results in an +; int_err_check_condition interrupt so that a REQUEST SENSE +; command can be issued out-of-order so that no other command +; clears the contingent allegiance condition. +; +; +; INPUTS : DSA - command +; +; CALLS : OK +; +; EXITS : On successful termination, control is passed to schedule. +; On abnormal termination, the user will usually modify the +; DSA fields and corresponding buffers and return control +; to select. +; + +ENTRY command_complete +command_complete: + MOVE FROM dsa_status, WHEN STATUS +#if (CHIP != 700) && (CHIP != 70066) + MOVE SFBR TO SCRATCH0 ; Save status +#endif /* (CHIP != 700) && (CHIP != 70066) */ +ENTRY command_complete_msgin +command_complete_msgin: + MOVE FROM dsa_msgin, WHEN MSG_IN +; Indicate that we should be expecting a disconnect +#if (CHIP != 710) + MOVE SCNTL2 & 0x7f TO SCNTL2 +#else + ; Above code cleared the Unexpected Disconnect bit, what do we do? +#endif + CLEAR ACK +#if (CHIP != 700) && (CHIP != 70066) + WAIT DISCONNECT + +; +; The SCSI specification states that when a UNIT ATTENTION condition +; is pending, as indicated by a CHECK CONDITION status message, +; the target shall revert to asynchronous transfers. Since +; synchronous transfers parameters are maintained on a per INITIATOR/TARGET +; basis, and returning control to our scheduler could work on a command +; running on another lun on that target using the old parameters, we must +; interrupt the host processor to get them changed, or change them ourselves. +; +; Once SCSI-II tagged queueing is implemented, things will be even more +; hairy, since contingent allegiance conditions exist on a per-target/lun +; basis, and issuing a new command with a different tag would clear it. +; In these cases, we must interrupt the host processor to get a request +; added to the HEAD of the queue with the request sense command, or we +; must automatically issue the request sense command. + +#if 0 + MOVE SCRATCH0 TO SFBR + JUMP command_failed, IF 0x02 +#endif +#if (CHIP == 710) +#if defined(MVME166_INTFLY) +; For MVME166 (ie CHIP=710) we will force an INTFLY by triggering a software +; interupt (SW7). We can use SCRATCH, as we are about to jump to +; schedule, which corrupts it anyway. Will probably remove this later, +; but want to check performance effects first. + +#define INTFLY_ADDR 0xfff40070 + + MOVE 0 TO SCRATCH0 + MOVE 0x80 TO SCRATCH1 + MOVE 0 TO SCRATCH2 + MOVE 0 TO SCRATCH3 + MOVE MEMORY 4, addr_scratch, INTFLY_ADDR +#else + INT int_norm_emulateintfly +#endif +#else + INTFLY +#endif +#endif /* (CHIP != 700) && (CHIP != 70066) */ +#if (CHIP == 710) + ; Time to correct DSA following memory move + MOVE MEMORY 4, saved_dsa, addr_dsa +#endif +#ifdef EVENTS + INT int_EVENT_COMPLETE +#endif +#if (CHIP != 700) && (CHIP != 70066) + JUMP schedule +command_failed: + INT int_err_check_condition +#else + INT int_norm_command_complete +#endif + +; +; wait_reselect +; +; PURPOSE : This is essentially the idle routine, where control lands +; when there are no new processes to schedule. wait_reselect +; waits for reselection, selection, and new commands. +; +; When a successful reselection occurs, with the aid +; of fixed up code in each DSA, wait_reselect walks the +; reconnect_dsa_queue, asking each dsa if the target ID +; and LUN match its. +; +; If a match is found, a call is made back to reselected_ok, +; which through the miracles of self modifying code, extracts +; the found DSA from the reconnect_dsa_queue and then +; returns control to the DSAs thread of execution. +; +; INPUTS : NONE +; +; CALLS : OK +; +; MODIFIES : DSA, +; +; EXITS : On successful reselection, control is returned to the +; DSA which called reselected_ok. If the WAIT RESELECT +; was interrupted by a new commands arrival signaled by +; SIG_P, control is passed to schedule. If the NCR is +; selected, the host system is interrupted with an +; int_err_selected which is usually responded to by +; setting DSP to the target_abort address. + +ENTRY wait_reselect +wait_reselect: +#ifdef EVENTS + int int_EVENT_IDLE +#endif +#ifdef DEBUG + int int_debug_idle +#endif + WAIT RESELECT wait_reselect_failed + +reselected: +#ifdef EVENTS + int int_EVENT_RESELECT +#endif + CLEAR TARGET + DMODE_MEMORY_TO_MEMORY + ; Read all data needed to reestablish the nexus - + MOVE 1, reselected_identify, WHEN MSG_IN + ; We used to CLEAR ACK here. +#if (CHIP != 700) && (CHIP != 70066) +#ifdef DEBUG + int int_debug_reselected +#endif + + ; Point DSA at the current head of the disconnected queue. + DMODE_MEMORY_TO_NCR + MOVE MEMORY 4, reconnect_dsa_head, addr_scratch + DMODE_MEMORY_TO_MEMORY +#if (CHIP == 710) + MOVE MEMORY 4, addr_scratch, saved_dsa +#else + CALL scratch_to_dsa +#endif + + ; Fix the update-next pointer so that the reconnect_dsa_head + ; pointer is the one that will be updated if this DSA is a hit + ; and we remove it from the queue. + + MOVE MEMORY 4, addr_reconnect_dsa_head, reselected_ok_patch + 8 +#if (CHIP == 710) + ; Time to correct DSA following memory move + MOVE MEMORY 4, saved_dsa, addr_dsa +#endif + +ENTRY reselected_check_next +reselected_check_next: +#ifdef DEBUG + INT int_debug_reselect_check +#endif + ; Check for a NULL pointer. + MOVE DSA0 TO SFBR + JUMP reselected_not_end, IF NOT 0 + MOVE DSA1 TO SFBR + JUMP reselected_not_end, IF NOT 0 + MOVE DSA2 TO SFBR + JUMP reselected_not_end, IF NOT 0 + MOVE DSA3 TO SFBR + JUMP reselected_not_end, IF NOT 0 + INT int_err_unexpected_reselect + +reselected_not_end: + ; + ; XXX the ALU is only eight bits wide, and the assembler + ; wont do the dirt work for us. As long as dsa_check_reselect + ; is negative, we need to sign extend with 1 bits to the full + ; 32 bit width of the address. + ; + ; A potential work around would be to have a known alignment + ; of the DSA structure such that the base address plus + ; dsa_check_reselect doesn't require carrying from bytes + ; higher than the LSB. + ; + + MOVE DSA0 TO SFBR + MOVE SFBR + dsa_check_reselect TO SCRATCH0 + MOVE DSA1 TO SFBR + MOVE SFBR + 0xff TO SCRATCH1 WITH CARRY + MOVE DSA2 TO SFBR + MOVE SFBR + 0xff TO SCRATCH2 WITH CARRY + MOVE DSA3 TO SFBR + MOVE SFBR + 0xff TO SCRATCH3 WITH CARRY + + DMODE_NCR_TO_MEMORY + MOVE MEMORY 4, addr_scratch, reselected_check + 4 + DMODE_MEMORY_TO_MEMORY +#if (CHIP == 710) + ; Time to correct DSA following memory move + MOVE MEMORY 4, saved_dsa, addr_dsa +#endif +reselected_check: + JUMP 0 + + +; +; +#if (CHIP == 710) +; We have problems here - the memory move corrupts TEMP and DSA. This +; routine is called from DSA code, and patched from many places. Scratch +; is probably free when it is called. +; We have to: +; copy temp to scratch, one byte at a time +; write scratch to patch a jump in place of the return +; do the move memory +; jump to the patched in return address +; DSA is corrupt when we get here, and can be left corrupt + +ENTRY reselected_ok +reselected_ok: + MOVE TEMP0 TO SFBR + MOVE SFBR TO SCRATCH0 + MOVE TEMP1 TO SFBR + MOVE SFBR TO SCRATCH1 + MOVE TEMP2 TO SFBR + MOVE SFBR TO SCRATCH2 + MOVE TEMP3 TO SFBR + MOVE SFBR TO SCRATCH3 + MOVE MEMORY 4, addr_scratch, reselected_ok_jump + 4 +reselected_ok_patch: + MOVE MEMORY 4, 0, 0 +reselected_ok_jump: + JUMP 0 +#else +ENTRY reselected_ok +reselected_ok: +reselected_ok_patch: + MOVE MEMORY 4, 0, 0 ; Patched : first word + ; is address of + ; successful dsa_next + ; Second word is last + ; unsuccessful dsa_next, + ; starting with + ; dsa_reconnect_head + ; We used to CLEAR ACK here. +#ifdef DEBUG + INT int_debug_reselected_ok +#endif +#ifdef DEBUG + INT int_debug_check_dsa +#endif + RETURN ; Return control to where +#endif +#else + INT int_norm_reselected +#endif /* (CHIP != 700) && (CHIP != 70066) */ + +selected: + INT int_err_selected; + +; +; A select or reselect failure can be caused by one of two conditions : +; 1. SIG_P was set. This will be the case if the user has written +; a new value to a previously NULL head of the issue queue. +; +; 2. The NCR53c810 was selected or reselected by another device. +; +; 3. The bus was already busy since we were selected or reselected +; before starting the command. + +wait_reselect_failed: +#ifdef EVENTS + INT int_EVENT_RESELECT_FAILED +#endif +; Check selected bit. +#if (CHIP == 710) + ; Must work out how to tell if we are selected.... +#else + MOVE SIST0 & 0x20 TO SFBR + JUMP selected, IF 0x20 +#endif +; Reading CTEST2 clears the SIG_P bit in the ISTAT register. + MOVE CTEST2 & 0x40 TO SFBR + JUMP schedule, IF 0x40 +; Check connected bit. +; FIXME: this needs to change if we support target mode + MOVE ISTAT & 0x08 TO SFBR + JUMP reselected, IF 0x08 +; FIXME : Something bogus happened, and we shouldn't fail silently. +#if 0 + JUMP schedule +#else + INT int_debug_panic +#endif + + +select_failed: +#if (CHIP == 710) + ; Disable selection timer + MOVE CTEST7 | 0x10 TO CTEST7 +#endif +#ifdef EVENTS + int int_EVENT_SELECT_FAILED +#endif +; Otherwise, mask the selected and reselected bits off SIST0 +#if (CHIP ==710) + ; Let's assume we don't get selected for now + MOVE SSTAT0 & 0x10 TO SFBR +#else + MOVE SIST0 & 0x30 TO SFBR + JUMP selected, IF 0x20 +#endif + JUMP reselected, IF 0x10 +; If SIGP is set, the user just gave us another command, and +; we should restart or return to the scheduler. +; Reading CTEST2 clears the SIG_P bit in the ISTAT register. + MOVE CTEST2 & 0x40 TO SFBR + JUMP select, IF 0x40 +; Check connected bit. +; FIXME: this needs to change if we support target mode +; FIXME: is this really necessary? + MOVE ISTAT & 0x08 TO SFBR + JUMP reselected, IF 0x08 +; FIXME : Something bogus happened, and we shouldn't fail silently. +#if 0 + JUMP schedule +#else + INT int_debug_panic +#endif + +; +; test_1 +; test_2 +; +; PURPOSE : run some verification tests on the NCR. test_1 +; copies test_src to test_dest and interrupts the host +; processor, testing for cache coherency and interrupt +; problems in the processes. +; +; test_2 runs a command with offsets relative to the +; DSA on entry, and is useful for miscellaneous experimentation. +; + +; Verify that interrupts are working correctly and that we don't +; have a cache invalidation problem. + +ABSOLUTE test_src = 0, test_dest = 0 +ENTRY test_1 +test_1: + MOVE MEMORY 4, test_src, test_dest + INT int_test_1 + +; +; Run arbitrary commands, with test code establishing a DSA +; + +ENTRY test_2 +test_2: + CLEAR TARGET +#if (CHIP == 710) + ; Enable selection timer +#ifdef NO_SELECTION_TIMEOUT + MOVE CTEST7 & 0xff TO CTEST7 +#else + MOVE CTEST7 & 0xef TO CTEST7 +#endif +#endif + SELECT ATN FROM 0, test_2_fail + JUMP test_2_msgout, WHEN MSG_OUT +ENTRY test_2_msgout +test_2_msgout: +#if (CHIP == 710) + ; Disable selection timer + MOVE CTEST7 | 0x10 TO CTEST7 +#endif + MOVE FROM 8, WHEN MSG_OUT + MOVE FROM 16, WHEN CMD + MOVE FROM 24, WHEN DATA_IN + MOVE FROM 32, WHEN STATUS + MOVE FROM 40, WHEN MSG_IN +#if (CHIP != 710) + MOVE SCNTL2 & 0x7f TO SCNTL2 +#endif + CLEAR ACK + WAIT DISCONNECT +test_2_fail: +#if (CHIP == 710) + ; Disable selection timer + MOVE CTEST7 | 0x10 TO CTEST7 +#endif + INT int_test_2 + +ENTRY debug_break +debug_break: + INT int_debug_break + +; +; initiator_abort +; target_abort +; +; PURPOSE : Abort the currently established nexus from with initiator +; or target mode. +; +; + +ENTRY target_abort +target_abort: + SET TARGET + DISCONNECT + CLEAR TARGET + JUMP schedule + +ENTRY initiator_abort +initiator_abort: + SET ATN +; +; The SCSI-I specification says that targets may go into MSG out at +; their leisure upon receipt of the ATN single. On all versions of the +; specification, we can't change phases until REQ transitions true->false, +; so we need to sink/source one byte of data to allow the transition. +; +; For the sake of safety, we'll only source one byte of data in all +; cases, but to accommodate the SCSI-I dain bramage, we'll sink an +; arbitrary number of bytes. + JUMP spew_cmd, WHEN CMD + JUMP eat_msgin, WHEN MSG_IN + JUMP eat_datain, WHEN DATA_IN + JUMP eat_status, WHEN STATUS + JUMP spew_dataout, WHEN DATA_OUT + JUMP sated +spew_cmd: + MOVE 1, NCR53c7xx_zero, WHEN CMD + JUMP sated +eat_msgin: + MOVE 1, NCR53c7xx_sink, WHEN MSG_IN + JUMP eat_msgin, WHEN MSG_IN + JUMP sated +eat_status: + MOVE 1, NCR53c7xx_sink, WHEN STATUS + JUMP eat_status, WHEN STATUS + JUMP sated +eat_datain: + MOVE 1, NCR53c7xx_sink, WHEN DATA_IN + JUMP eat_datain, WHEN DATA_IN + JUMP sated +spew_dataout: + MOVE 1, NCR53c7xx_zero, WHEN DATA_OUT +sated: +#if (CHIP != 710) + MOVE SCNTL2 & 0x7f TO SCNTL2 +#endif + MOVE 1, NCR53c7xx_msg_abort, WHEN MSG_OUT + WAIT DISCONNECT + INT int_norm_aborted + +#if (CHIP != 710) +; +; dsa_to_scratch +; scratch_to_dsa +; +; PURPOSE : +; The NCR chips cannot do a move memory instruction with the DSA register +; as the source or destination. So, we provide a couple of subroutines +; that let us switch between the DSA register and scratch register. +; +; Memory moves to/from the DSPS register also don't work, but we +; don't use them. +; +; + + +dsa_to_scratch: + MOVE DSA0 TO SFBR + MOVE SFBR TO SCRATCH0 + MOVE DSA1 TO SFBR + MOVE SFBR TO SCRATCH1 + MOVE DSA2 TO SFBR + MOVE SFBR TO SCRATCH2 + MOVE DSA3 TO SFBR + MOVE SFBR TO SCRATCH3 + RETURN + +scratch_to_dsa: + MOVE SCRATCH0 TO SFBR + MOVE SFBR TO DSA0 + MOVE SCRATCH1 TO SFBR + MOVE SFBR TO DSA1 + MOVE SCRATCH2 TO SFBR + MOVE SFBR TO DSA2 + MOVE SCRATCH3 TO SFBR + MOVE SFBR TO DSA3 + RETURN +#endif + +#if (CHIP == 710) +; Little patched jump, used to overcome problems with TEMP getting +; corrupted on memory moves. + +jump_temp: + JUMP 0 +#endif diff -u --recursive --new-file v2.1.39/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile --- v2.1.39/linux/drivers/scsi/Makefile Tue May 13 22:41:12 1997 +++ linux/drivers/scsi/Makefile Sun May 18 17:10:37 1997 @@ -95,6 +95,30 @@ endif endif +ifeq ($(CONFIG_A4000T_SCSI),y) +L_OBJS += amiga7xx.o 53c7xx.o +else + ifeq ($(CONFIG_A4000T_SCSI),m) + M_OBJS += amiga7xx.o 53c7xx.o + endif +endif + +ifeq ($(CONFIG_A4091_SCSI),y) +L_OBJS += amiga7xx.o 53c7xx.o +else + ifeq ($(CONFIG_A4091_SCSI),m) + M_OBJS += amiga7xx.o 53c7xx.o + endif +endif + +ifeq ($(CONFIG_WARPENGINE_SCSI),y) +L_OBJS += amiga7xx.o 53c7xx.o +else + ifeq ($(CONFIG_WARPENGINE_SCSI),m) + M_OBJS += amiga7xx.o 53c7xx.o + endif +endif + ifeq ($(CONFIG_A3000_SCSI),y) L_OBJS += a3000.o wd33c93.o else @@ -416,6 +440,16 @@ $(CPP) -traditional -DCHIP=810 fake.c | grep -v '^#' | perl script_asm.pl mv script.h 53c8xx_d.h mv scriptu.h 53c8xx_u.h + rm fake.c + +53c7xx.o : 53c7xx_d.h 53c7xx.c + $(CC) $(CFLAGS) -c 53c7xx.c + +53c7xx_d.h 53c7xx_u.h : 53c7xx.scr script_asm.pl + ln -sf 53c7xx.scr fake.c + $(CPP) -traditional -DCHIP=710 fake.c | grep -v '^#' | perl -s script_asm.pl -ncr7x0_family + mv script.h 53c7xx_d.h + mv scriptu.h 53c7xx_u.h rm fake.c ncr53c8xx.o : ncr53c8xx.c diff -u --recursive --new-file v2.1.39/linux/drivers/scsi/amiga7xx.c linux/drivers/scsi/amiga7xx.c --- v2.1.39/linux/drivers/scsi/amiga7xx.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/amiga7xx.c Sun May 18 17:10:37 1997 @@ -0,0 +1,107 @@ +/* + * Detection routine for the NCR53c710 based Amiga SCSI Controllers for Linux. + * Amiga MacroSystemUS WarpEngine SCSI controller. + * Amiga Technologies A4000T SCSI controller. + * Amiga Technologies/DKB A4091 SCSI controller. + * + * Written 1997 by Alan Hourihane + * plus modifications of the 53c7xx.c driver to support the Amiga. + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "scsi.h" +#include "hosts.h" +#include "53c7xx.h" +#include "amiga7xx.h" + +#include + +struct proc_dir_entry proc_scsi_amiga7xx = { + PROC_SCSI_AMIGA7XX, 8, "Amiga7xx", + S_IFDIR | S_IRUGO | S_IXUGO, 2 +}; + +int amiga7xx_detect(Scsi_Host_Template *tpnt) +{ + static unsigned char called = 0; + int key; + int num = 0; + unsigned long address; + long long options; + struct ConfigDev *cd; + + if (called || !MACH_IS_AMIGA) + return 0; + + tpnt->proc_dir = &proc_scsi_amiga7xx; + +#ifdef CONFIG_WARPENGINE_SCSI + if ((key = zorro_find(MANUF_MACROSYSTEMS, PROD_WARP_ENGINE, 0, 0))) + { + cd = zorro_get_board(key); + address = (unsigned long)kernel_map((unsigned long)cd->cd_BoardAddr, + cd->cd_BoardSize, KERNELMAP_NOCACHE_SER, NULL); + + options = OPTION_MEMORY_MAPPED|OPTION_DEBUG_TEST1|OPTION_INTFLY|OPTION_SYNCHRONOUS|OPTION_ALWAYS_SYNCHRONOUS|OPTION_DISCONNECT; + + clock = 50000000; /* 50MHz SCSI Clock */ + + ncr53c7xx_init(tpnt, 0, 710, (u32)(unsigned char *)(address + 0x40000), + 0, IRQ_AMIGA_PORTS & ~IRQ_MACHSPEC, DMA_NONE, + options, clock); + + zorro_config_board(key, 0); + num++; + } +#endif + +#ifdef CONFIG_A4000T_SCSI + if (AMIGAHW_PRESENT(A4000_SCSI)) + { + options = OPTION_MEMORY_MAPPED|OPTION_DEBUG_TEST1|OPTION_INTFLY|OPTION_SYNCHRONOUS|OPTION_ALWAYS_SYNCHRONOUS|OPTION_DISCONNECT; + + clock = 50000000; /* 50MHz SCSI Clock */ + + ncr53c7xx_init(tpnt, 0, 710, (u32)(unsigned char *)ZTWO_VADDR(0xDD0040), + 0, IRQ_AMIGA_PORTS, DMA_NONE, + options, clock); + num++; + } +#endif + +#ifdef CONFIG_A4091_SCSI + while ( (key = zorro_find(MANUF_COMMODORE, PROD_A4091, 0, 0)) || + (key = zorro_find(MANUF_COMMODORE2, PROD_A4091_2, 0, 0)) ) + { + cd = zorro_get_board(key); + address = (unsigned long)kernel_map((unsigned long)cd->cd_BoardAddr, + cd->cd_BoardSize, KERNELMAP_NOCACHE_SER, NULL); + + options = OPTION_MEMORY_MAPPED|OPTION_DEBUG_TEST1|OPTION_INTFLY|OPTION_SYNCHRONOUS|OPTION_ALWAYS_SYNCHRONOUS|OPTION_DISCONNECT; + + clock = 50000000; /* 50MHz SCSI Clock */ + + ncr53c7xx_init(tpnt, 0, 710, (u32)(unsigned char *)(address+0x800000), + 0, IRQ_AMIGA_PORTS & ~IRQ_MACHSPEC, DMA_NONE, + options, clock); + + zorro_config_board(key, 0); + num++; + } +#endif + + called = 1; + return num; +} diff -u --recursive --new-file v2.1.39/linux/drivers/scsi/amiga7xx.h linux/drivers/scsi/amiga7xx.h --- v2.1.39/linux/drivers/scsi/amiga7xx.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/amiga7xx.h Sun May 18 17:10:37 1997 @@ -0,0 +1,52 @@ +#ifndef AMIGA7XX_H + +#include + +int amiga7xx_detect(Scsi_Host_Template *); +const char *NCR53c7x0_info(void); +int NCR53c7xx_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); +int NCR53c7xx_abort(Scsi_Cmnd *); +int NCR53c7x0_release (Scsi_Host_Template *); +int NCR53c7xx_reset(Scsi_Cmnd *, unsigned int); +void NCR53c7x0_intr(int irq, void *dev_id, struct pt_regs * regs); + +#ifndef NULL +#define NULL 0 +#endif + +#ifndef CMD_PER_LUN +#define CMD_PER_LUN 3 +#endif + +#ifndef CAN_QUEUE +#define CAN_QUEUE 24 +#endif + +#if defined(HOSTS_C) || defined(MODULE) +#include + +extern struct proc_dir_entry proc_scsi_amiga7xx; + +#define AMIGA7XX_SCSI {/* next */ NULL, \ + /* usage_count */ NULL, \ + /* proc_dir_entry */ NULL, \ + /* proc_info */ NULL, \ + /* name */ "Amiga NCR53c710 SCSI", \ + /* detect */ amiga7xx_detect, \ + /* release */ NULL, \ + /* info */ NULL, \ + /* command */ NULL, \ + /* queuecommand */ NCR53c7xx_queue_command, \ + /* abort */ NCR53c7xx_abort, \ + /* reset */ NCR53c7xx_reset, \ + /* slave_attach */ NULL, \ + /* bios_param */ scsicam_bios_param, \ + /* can_queue */ 24, \ + /* this_id */ 7, \ + /* sg_tablesize */ 127, \ + /* cmd_per_lun */ 3, \ + /* present */ 0, \ + /* unchecked_isa_dma */ 0, \ + /* use_clustering */ DISABLE_CLUSTERING } +#endif +#endif /* AMIGA7XX_H */ diff -u --recursive --new-file v2.1.39/linux/drivers/scsi/atari_scsi.c linux/drivers/scsi/atari_scsi.c --- v2.1.39/linux/drivers/scsi/atari_scsi.c Tue May 13 22:41:13 1997 +++ linux/drivers/scsi/atari_scsi.c Sun May 18 17:10:38 1997 @@ -1026,6 +1026,10 @@ { unsigned long possible_len, limit; + if (is_hades) + /* Hades has no SCSI DMA at all :-( Always force use of PIO */ + return( 0 ); + if (IS_A_TT()) /* TT SCSI DMA can transfer arbitrary #bytes */ return( wanted_len ); diff -u --recursive --new-file v2.1.39/linux/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c --- v2.1.39/linux/drivers/scsi/hosts.c Tue May 13 22:41:13 1997 +++ linux/drivers/scsi/hosts.c Sun May 18 17:10:38 1997 @@ -42,6 +42,10 @@ #include "hosts.h" +#if defined(CONFIG_A4000T_SCSI) || defined(CONFIG_WARPENGINE_SCSI) || defined(CONFIG_A4091_SCSI) +#include "amiga7xx.h" +#endif + #ifdef CONFIG_A3000_SCSI #include "a3000.h" #endif @@ -221,6 +225,9 @@ static Scsi_Host_Template builtin_scsi_hosts[] = { #ifdef CONFIG_AMIGA +#if defined(CONFIG_WARPENGINE_SCSI) || defined(CONFIG_A4000T_SCSI) || defined(CONFIG_A4091_SCSI) + AMIGA7XX_SCSI, +#endif #ifdef CONFIG_A3000_SCSI A3000_SCSI, #endif diff -u --recursive --new-file v2.1.39/linux/drivers/scsi/scsi.h linux/drivers/scsi/scsi.h --- v2.1.39/linux/drivers/scsi/scsi.h Mon Apr 14 16:28:16 1997 +++ linux/drivers/scsi/scsi.h Wed May 21 16:16:17 1997 @@ -218,7 +218,13 @@ #include +#ifdef __mc68000__ +#include +#define CONTIGUOUS_BUFFERS(X,Y) \ + (VTOP((X)->b_data+(X)->b_size-1)+1 == VTOP((Y)->b_data)) +#else #define CONTIGUOUS_BUFFERS(X,Y) ((X->b_data+X->b_size) == Y->b_data) +#endif /* diff -u --recursive --new-file v2.1.39/linux/fs/autofs/root.c linux/fs/autofs/root.c --- v2.1.39/linux/fs/autofs/root.c Thu May 15 16:48:03 1997 +++ linux/fs/autofs/root.c Wed May 21 17:10:23 1997 @@ -376,6 +376,8 @@ struct autofs_packet_expire pkt; struct autofs_dirhash *dh = &(sbi->dirhash); + memset(&pkt,0,sizeof pkt); + pkt.hdr.proto_version = AUTOFS_PROTO_VERSION; pkt.hdr.type = autofs_ptype_expire; diff -u --recursive --new-file v2.1.39/linux/fs/autofs/waitq.c linux/fs/autofs/waitq.c --- v2.1.39/linux/fs/autofs/waitq.c Thu May 15 16:48:03 1997 +++ linux/fs/autofs/waitq.c Wed May 21 17:10:23 1997 @@ -77,6 +77,8 @@ DPRINTK(("autofs_wait: wait id = 0x%08lx, name = ", wq->wait_queue_token)); autofs_say(wq->name,wq->len); + memset(&pkt,0,sizeof pkt); /* For security reasons */ + pkt.hdr.proto_version = AUTOFS_PROTO_VERSION; pkt.hdr.type = autofs_ptype_missing; pkt.wait_queue_token = wq->wait_queue_token; @@ -96,7 +98,7 @@ for ( wq = sbi->queues ; wq ; wq = wq->next ) { if ( wq->hash == hash && wq->len == len && - !memcmp(wq->name,name,len) ) + wq->name && !memcmp(wq->name,name,len) ) break; } @@ -115,12 +117,13 @@ init_waitqueue(&wq->queue); wq->hash = hash; wq->len = len; + wq->status = -EINTR; /* Status return if interrupted */ memcpy(wq->name, name, len); wq->next = sbi->queues; sbi->queues = wq; /* autofs_notify_daemon() may block */ - wq->wait_ctr++; + wq->wait_ctr = 1; autofs_notify_daemon(sbi,wq); } else wq->wait_ctr++; @@ -132,7 +135,8 @@ DPRINTK(("autofs_wait: skipped sleeping\n")); } - status = (current->signal & ~current->blocked) ? -EINTR : wq->status; + status = wq->status; + if ( ! --wq->wait_ctr ) /* Are we the last process to need status? */ kfree(wq); diff -u --recursive --new-file v2.1.39/linux/fs/binfmt_elf.c linux/fs/binfmt_elf.c --- v2.1.39/linux/fs/binfmt_elf.c Mon May 19 12:57:39 1997 +++ linux/fs/binfmt_elf.c Mon May 19 12:39:25 1997 @@ -47,6 +47,11 @@ extern unsigned long get_unmapped_area(unsigned long addr, unsigned long len); #endif +#ifndef elf_addr_t +#define elf_addr_t unsigned long +#define elf_caddr_t char * +#endif + /* * If we don't support core dumping, then supply a NULL so we * don't even try. @@ -59,6 +64,7 @@ #define ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(ELF_EXEC_PAGESIZE-1)) #define ELF_PAGEOFFSET(_v) ((_v) & (ELF_EXEC_PAGESIZE-1)) +#define ELF_PAGEALIGN(_v) (((_v) + ELF_EXEC_PAGESIZE - 1) & ~(ELF_EXEC_PAGESIZE - 1)) static struct linux_binfmt elf_format = { #ifndef MODULE @@ -70,8 +76,8 @@ static void set_brk(unsigned long start, unsigned long end) { - start = PAGE_ALIGN(start); - end = PAGE_ALIGN(end); + start = ELF_PAGEALIGN(start); + end = ELF_PAGEALIGN(end); if (end <= start) return; do_mmap(NULL, start, end - start, @@ -90,28 +96,30 @@ { unsigned long nbyte; - nbyte = elf_bss & (PAGE_SIZE-1); + nbyte = ELF_PAGEOFFSET(elf_bss); if (nbyte) { - nbyte = PAGE_SIZE - nbyte; + nbyte = ELF_EXEC_PAGESIZE - nbyte; clear_user((void *) elf_bss, nbyte); } } -unsigned long * create_elf_tables(char *p, int argc, int envc, - struct elfhdr * exec, - unsigned long load_addr, - unsigned long interp_load_addr, int ibcs) +static elf_addr_t * +create_elf_tables(char *p, int argc, int envc, + struct elfhdr * exec, + unsigned long load_addr, + unsigned long interp_load_addr, int ibcs) { - char **argv, **envp; - unsigned long *sp; + elf_caddr_t *argv; + elf_caddr_t *envp; + elf_addr_t *sp; /* * Force 16 byte alignment here for generality. */ - sp = (unsigned long *) (~15UL & (unsigned long) p); + sp = (elf_addr_t *) (~15UL & (unsigned long) p); #ifdef __sparc__ { - unsigned long *csp; + elf_addr_t *csp; csp = sp; csp -= exec ? DLINFO_ITEMS*2 : 2; csp -= envc+1; @@ -137,36 +145,36 @@ NEW_AUX_ENT (0, AT_PHDR, load_addr + exec->e_phoff); NEW_AUX_ENT (1, AT_PHENT, sizeof (struct elf_phdr)); NEW_AUX_ENT (2, AT_PHNUM, exec->e_phnum); - NEW_AUX_ENT (3, AT_PAGESZ, PAGE_SIZE); + NEW_AUX_ENT (3, AT_PAGESZ, ELF_EXEC_PAGESIZE); NEW_AUX_ENT (4, AT_BASE, interp_load_addr); NEW_AUX_ENT (5, AT_FLAGS, 0); - NEW_AUX_ENT (6, AT_ENTRY, (unsigned long) exec->e_entry); - NEW_AUX_ENT (7, AT_UID, (unsigned long) current->uid); - NEW_AUX_ENT (8, AT_EUID, (unsigned long) current->euid); - NEW_AUX_ENT (9, AT_GID, (unsigned long) current->gid); - NEW_AUX_ENT (10, AT_EGID, (unsigned long) current->egid); + NEW_AUX_ENT (6, AT_ENTRY, (elf_addr_t) exec->e_entry); + NEW_AUX_ENT (7, AT_UID, (elf_addr_t) current->uid); + NEW_AUX_ENT (8, AT_EUID, (elf_addr_t) current->euid); + NEW_AUX_ENT (9, AT_GID, (elf_addr_t) current->gid); + NEW_AUX_ENT (10, AT_EGID, (elf_addr_t) current->egid); } #undef NEW_AUX_ENT sp -= envc+1; - envp = (char **) sp; + envp = (elf_caddr_t *) sp; sp -= argc+1; - argv = (char **) sp; + argv = (elf_caddr_t *) sp; if (!ibcs) { - __put_user((unsigned long) envp,--sp); - __put_user((unsigned long) argv,--sp); + __put_user((elf_addr_t)(unsigned long) envp,--sp); + __put_user((elf_addr_t)(unsigned long) argv,--sp); } - __put_user((unsigned long)argc,--sp); + __put_user((elf_addr_t)argc,--sp); current->mm->arg_start = (unsigned long) p; while (argc-->0) { - __put_user(p,argv++); + __put_user((elf_caddr_t)(unsigned long)p,argv++); p += strlen_user(p); } __put_user(NULL, argv); current->mm->arg_end = current->mm->env_start = (unsigned long) p; while (envc-->0) { - __put_user(p,envp++); + __put_user((elf_caddr_t)(unsigned long)p,envp++); p += strlen_user(p); } __put_user(NULL, envp); @@ -210,7 +218,7 @@ /* Now read in all of the header information */ - if (sizeof(struct elf_phdr) * interp_elf_ex->e_phnum > PAGE_SIZE) { + if (sizeof(struct elf_phdr) * interp_elf_ex->e_phnum > ELF_EXEC_PAGESIZE) { return ~0UL; } @@ -579,8 +587,8 @@ /* Do this so that we can load the interpreter, if need be. We will change some of these later */ current->mm->rss = 0; -#ifdef __sparc_v9__ - current->tss.flags &= ~(SPARC_FLAG_32BIT); +#ifdef ELF_FLAGS_INIT + ELF_FLAGS_INIT; #endif bprm->p = setup_arg_pages(bprm->p, bprm); current->mm->start_stack = bprm->p; @@ -799,7 +807,7 @@ /* Now read in all of the header information */ - if (sizeof(struct elf_phdr) * elf_ex.e_phnum > PAGE_SIZE) + if (sizeof(struct elf_phdr) * elf_ex.e_phnum > ELF_EXEC_PAGESIZE) return -ENOEXEC; elf_phdata = (struct elf_phdr *) @@ -903,6 +911,10 @@ { if (!(vma->vm_flags & (VM_READ|VM_WRITE|VM_EXEC))) return 0; + + /* Do not dump I/O mapped devices! -DaveM */ + if(vma->vm_flags & VM_IO) + return 0; #if 1 if (vma->vm_flags & (VM_WRITE|VM_GROWSUP|VM_GROWSDOWN)) return 1; @@ -1012,7 +1024,7 @@ elf_fpregset_t fpu; /* NT_PRFPREG */ struct elf_prpsinfo psinfo; /* NT_PRPSINFO */ - if (!current->dumpable || limit < PAGE_SIZE || current->mm->count != 1) + if (!current->dumpable || limit < ELF_EXEC_PAGESIZE || current->mm->count != 1) return 0; current->dumpable = 0; @@ -1114,14 +1126,14 @@ psinfo.pr_ppid = prstatus.pr_ppid = current->p_pptr->pid; psinfo.pr_pgrp = prstatus.pr_pgrp = current->pgrp; psinfo.pr_sid = prstatus.pr_sid = current->session; - prstatus.pr_utime.tv_sec = CT_TO_SECS(current->utime); - prstatus.pr_utime.tv_usec = CT_TO_USECS(current->utime); - prstatus.pr_stime.tv_sec = CT_TO_SECS(current->stime); - prstatus.pr_stime.tv_usec = CT_TO_USECS(current->stime); - prstatus.pr_cutime.tv_sec = CT_TO_SECS(current->cutime); - prstatus.pr_cutime.tv_usec = CT_TO_USECS(current->cutime); - prstatus.pr_cstime.tv_sec = CT_TO_SECS(current->cstime); - prstatus.pr_cstime.tv_usec = CT_TO_USECS(current->cstime); + prstatus.pr_utime.tv_sec = CT_TO_SECS(current->times.tms_utime); + prstatus.pr_utime.tv_usec = CT_TO_USECS(current->times.tms_utime); + prstatus.pr_stime.tv_sec = CT_TO_SECS(current->times.tms_stime); + prstatus.pr_stime.tv_usec = CT_TO_USECS(current->times.tms_stime); + prstatus.pr_cutime.tv_sec = CT_TO_SECS(current->times.tms_cutime); + prstatus.pr_cutime.tv_usec = CT_TO_USECS(current->times.tms_cutime); + prstatus.pr_cstime.tv_sec = CT_TO_SECS(current->times.tms_cstime); + prstatus.pr_cstime.tv_usec = CT_TO_USECS(current->times.tms_cstime); /* * This transfers the registers from regs into the standard @@ -1133,7 +1145,7 @@ if (sizeof(elf_gregset_t) != sizeof(struct pt_regs)) { printk("sizeof(elf_gregset_t) (%ld) != sizeof(struct pt_regs) (%ld)\n", - sizeof(elf_gregset_t), sizeof(struct pt_regs)); + (long)sizeof(elf_gregset_t), (long)sizeof(struct pt_regs)); } else *(struct pt_regs *)&prstatus.pr_reg = *regs; @@ -1215,7 +1227,7 @@ } /* Page-align dumped data */ - dataoff = offset = roundup(offset, PAGE_SIZE); + dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE); /* Write program headers for segments dump */ for(vma = current->mm->mmap, i = 0; @@ -1237,7 +1249,7 @@ phdr.p_flags = vma->vm_flags & VM_READ ? PF_R : 0; if (vma->vm_flags & VM_WRITE) phdr.p_flags |= PF_W; if (vma->vm_flags & VM_EXEC) phdr.p_flags |= PF_X; - phdr.p_align = PAGE_SIZE; + phdr.p_align = ELF_EXEC_PAGESIZE; DUMP_WRITE(&phdr, sizeof(phdr)); } diff -u --recursive --new-file v2.1.39/linux/fs/locks.c linux/fs/locks.c --- v2.1.39/linux/fs/locks.c Tue May 13 22:41:14 1997 +++ linux/fs/locks.c Sun May 18 17:10:38 1997 @@ -366,8 +366,7 @@ do { if (vma->vm_flags & VM_MAYSHARE) return (-EAGAIN); - vma = vma->vm_next_share; - } while (vma != inode->i_mmap); + } while ((vma = vma->vm_next_share) != NULL); } if (copy_from_user(&flock, l, sizeof(flock))) diff -u --recursive --new-file v2.1.39/linux/fs/proc/array.c linux/fs/proc/array.c --- v2.1.39/linux/fs/proc/array.c Tue May 13 22:41:15 1997 +++ linux/fs/proc/array.c Mon May 19 12:37:57 1997 @@ -268,7 +268,7 @@ unsigned long idle; uptime = jiffies; - idle = task[0]->utime + task[0]->stime; + idle = task[0]->times.tms_utime + task[0]->times.tms_stime; /* The formula for the fraction parts really is ((t * 100) / HZ) % 100, but that would overflow about every five days at HZ == 100. @@ -748,10 +748,10 @@ tsk->cmin_flt, tsk->maj_flt, tsk->cmaj_flt, - tsk->utime, - tsk->stime, - tsk->cutime, - tsk->cstime, + tsk->times.tms_utime, + tsk->times.tms_stime, + tsk->times.tms_cutime, + tsk->times.tms_cstime, priority, nice, tsk->timeout, diff -u --recursive --new-file v2.1.39/linux/include/asm-alpha/keyboard.h linux/include/asm-alpha/keyboard.h --- v2.1.39/linux/include/asm-alpha/keyboard.h Wed Dec 31 16:00:00 1969 +++ linux/include/asm-alpha/keyboard.h Wed May 21 09:57:45 1997 @@ -0,0 +1,44 @@ +/* + * linux/include/asm-alpha/keyboard.h + * + * Created 3 Nov 1996 by Geert Uytterhoeven + */ + +/* + * This file contains the alpha architecture specific keyboard definitions + */ + +#ifndef _ALPHA_KEYBOARD_H +#define _ALPHA_KEYBOARD_H + +#ifdef __KERNEL__ + +#define KEYBOARD_IRQ 1 +#define DISABLE_KBD_DURING_INTERRUPTS 0 + +#define KBD_REPORT_ERR +#define KBD_REPORT_UNKN +/* #define KBD_IS_FOCUS_9000 */ + +extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode); +extern int pckbd_getkeycode(unsigned int scancode); +extern int pckbd_pretranslate(unsigned char scancode, char raw_mode); +extern int pckbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode); +extern char pckbd_unexpected_up(unsigned char keycode); +extern void pckbd_leds(unsigned char leds); +extern void pckbd_init_hw(void); + +#define kbd_setkeycode pckbd_setkeycode +#define kbd_getkeycode pckbd_getkeycode +#define kbd_pretranslate pckbd_pretranslate +#define kbd_translate pckbd_translate +#define kbd_unexpected_up pckbd_unexpected_up +#define kbd_leds pckbd_leds +#define kbd_init_hw pckbd_init_hw + +#define INIT_KBD + +#endif /* __KERNEL__ */ + +#endif /* __ASMalpha_KEYBOARD_H */ diff -u --recursive --new-file v2.1.39/linux/include/asm-i386/keyboard.h linux/include/asm-i386/keyboard.h --- v2.1.39/linux/include/asm-i386/keyboard.h Wed Dec 31 16:00:00 1969 +++ linux/include/asm-i386/keyboard.h Wed May 21 09:57:45 1997 @@ -0,0 +1,42 @@ +/* + * linux/include/asm-i386/keyboard.h + * + * Created 3 Nov 1996 by Geert Uytterhoeven + */ + +/* + * This file contains the i386 architecture specific keyboard definitions + */ + +#ifndef _I386_KEYBOARD_H +#define _I386_KEYBOARD_H + +#ifdef __KERNEL__ + +#define KEYBOARD_IRQ 1 +#define DISABLE_KBD_DURING_INTERRUPTS 0 + +#define KBD_REPORT_ERR +#define KBD_REPORT_UNKN +/* #define KBD_IS_FOCUS_9000 */ + +extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode); +extern int pckbd_getkeycode(unsigned int scancode); +extern int pckbd_pretranslate(unsigned char scancode, char raw_mode); +extern int pckbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode); +extern char pckbd_unexpected_up(unsigned char keycode); +extern void pckbd_leds(unsigned char leds); +extern void pckbd_init_hw(void); + +#define kbd_setkeycode pckbd_setkeycode +#define kbd_getkeycode pckbd_getkeycode +#define kbd_pretranslate pckbd_pretranslate +#define kbd_translate pckbd_translate +#define kbd_unexpected_up pckbd_unexpected_up +#define kbd_leds pckbd_leds +#define kbd_init_hw pckbd_init_hw + +#endif /* __KERNEL__ */ + +#endif /* __ASMi386_KEYBOARD_H */ diff -u --recursive --new-file v2.1.39/linux/include/asm-m68k/atomic.h linux/include/asm-m68k/atomic.h --- v2.1.39/linux/include/asm-m68k/atomic.h Wed Apr 23 19:01:27 1997 +++ linux/include/asm-m68k/atomic.h Sun May 18 17:10:38 1997 @@ -11,7 +11,7 @@ */ typedef struct { int counter; } atomic_t; -#define ATOMIC_INIT { 0 } +#define ATOMIC_INIT(i) { (i) } #define atomic_read(v) ((v)->counter) #define atomic_set(v, i) (((v)->counter) = i) diff -u --recursive --new-file v2.1.39/linux/include/asm-m68k/bitops.h linux/include/asm-m68k/bitops.h --- v2.1.39/linux/include/asm-m68k/bitops.h Wed Sep 25 00:47:41 1996 +++ linux/include/asm-m68k/bitops.h Sun May 18 17:10:38 1997 @@ -14,7 +14,7 @@ * They use the standard big-endian m680x0 bit ordering. */ -extern __inline__ int set_bit(int nr,void * vaddr) +extern __inline__ int test_and_set_bit(int nr,void * vaddr) { char retval; @@ -24,7 +24,13 @@ return retval; } -extern __inline__ int clear_bit(int nr, void * vaddr) +extern __inline__ void set_bit(int nr, void * vaddr) +{ + __asm__ __volatile__ ("bfset %1@{%0:#1}" + : : "d" (nr^31), "a" (vaddr)); +} + +extern __inline__ int test_and_clear_bit(int nr, void * vaddr) { char retval; @@ -34,7 +40,13 @@ return retval; } -extern __inline__ int change_bit(int nr, void * vaddr) +extern __inline__ void clear_bit(int nr, void * vaddr) +{ + __asm__ __volatile__ ("bfclr %1@{%0:#1}" + : : "d" (nr^31), "a" (vaddr)); +} + +extern __inline__ int test_and_change_bit(int nr, void * vaddr) { char retval; @@ -42,6 +54,12 @@ : "=d" (retval) : "d" (nr^31), "a" (vaddr)); return retval; +} + +extern __inline__ void change_bit(int nr, void * vaddr) +{ + __asm__ __volatile__ ("bfchg %1@{%0:#1}" + : : "d" (nr^31), "a" (vaddr)); } extern __inline__ int test_bit(int nr, const void * vaddr) diff -u --recursive --new-file v2.1.39/linux/include/asm-m68k/current.h linux/include/asm-m68k/current.h --- v2.1.39/linux/include/asm-m68k/current.h Mon Dec 30 01:58:35 1996 +++ linux/include/asm-m68k/current.h Sun May 18 17:10:38 1997 @@ -1,12 +1,6 @@ #ifndef _M68K_CURRENT_H #define _M68K_CURRENT_H -/* Some architectures may want to do something "clever" here since - * this is the most frequently accessed piece of data in the entire - * kernel. For an example, see the Sparc implementation where an - * entire register is hard locked to contain the value of current. - */ -extern struct task_struct *current_set[NR_CPUS]; -#define current (current_set[smp_processor_id()]) /* Current on this processor */ +register struct task_struct *current __asm__("%a2"); #endif /* !(_M68K_CURRENT_H) */ diff -u --recursive --new-file v2.1.39/linux/include/asm-m68k/elf.h linux/include/asm-m68k/elf.h --- v2.1.39/linux/include/asm-m68k/elf.h Fri Nov 22 05:56:36 1996 +++ linux/include/asm-m68k/elf.h Sun May 18 17:10:38 1997 @@ -44,6 +44,7 @@ pr_reg[4] = regs->d5; \ pr_reg[7] = regs->a0; \ pr_reg[8] = regs->a1; \ + pr_reg[9] = regs->a2; \ pr_reg[14] = regs->d0; \ pr_reg[15] = rdusp(); \ pr_reg[16] = regs->orig_d0; \ @@ -54,7 +55,6 @@ struct switch_stack *sw = ((struct switch_stack *)regs) - 1; \ pr_reg[5] = sw->d6; \ pr_reg[6] = sw->d7; \ - pr_reg[9] = sw->a2; \ pr_reg[10] = sw->a3; \ pr_reg[11] = sw->a4; \ pr_reg[12] = sw->a5; \ diff -u --recursive --new-file v2.1.39/linux/include/asm-m68k/hardirq.h linux/include/asm-m68k/hardirq.h --- v2.1.39/linux/include/asm-m68k/hardirq.h Tue May 13 22:41:17 1997 +++ linux/include/asm-m68k/hardirq.h Sun May 18 17:10:38 1997 @@ -1,11 +1,13 @@ #ifndef __M68K_HARDIRQ_H #define __M68K_HARDIRQ_H +#include + extern unsigned int local_irq_count[NR_CPUS]; #define in_interrupt() (local_irq_count[smp_processor_id()] != 0) -#define hardirq_trylock(cpu) ((cpu)==0) /* always true */ -#define hardirq_endlock(cpu) do { } while (0) +#define hardirq_trylock(cpu) (++local_irq_count[cpu], (cpu) == 0) +#define hardirq_endlock(cpu) (--local_irq_count[cpu]) #define hardirq_enter(cpu) (local_irq_count[cpu]++) #define hardirq_exit(cpu) (local_irq_count[cpu]--) diff -u --recursive --new-file v2.1.39/linux/include/asm-m68k/keyboard.h linux/include/asm-m68k/keyboard.h --- v2.1.39/linux/include/asm-m68k/keyboard.h Fri Dec 20 01:20:02 1996 +++ linux/include/asm-m68k/keyboard.h Wed May 21 09:57:45 1997 @@ -13,9 +13,43 @@ #ifdef __KERNEL__ -#define TRANSLATE_SCANCODES 0 -#define USE_MACHDEP_ABSTRACTION 1 #include + +static __inline__ int kbd_setkeycode(unsigned int scancode, + unsigned int keycode) +{ + return -EOPNOTSUPP; +} + +static __inline__ int kbd_getkeycode(unsigned int scancode) +{ + return -EOPNOTSUPP; +} + +static __inline__ int kbd_pretranslate(unsigned char scancode, char raw_mode) +{ + return 1; +} + +static __inline__ int kbd_translate(unsigned char scancode, + unsigned char *keycode, char raw_mode) +{ + *keycode = scancode; + return 1; +} + +static __inline__ char kbd_unexpected_up(unsigned char keycode) +{ + return 0200; +} + +static __inline__ void kbd_leds(unsigned char leds) +{ + if (mach_kbd_leds) + mach_kbd_leds(leds); +} + +#define kbd_init_hw mach_keyb_init #endif /* __KERNEL__ */ diff -u --recursive --new-file v2.1.39/linux/include/asm-m68k/pgtable.h linux/include/asm-m68k/pgtable.h --- v2.1.39/linux/include/asm-m68k/pgtable.h Wed Apr 23 19:01:27 1997 +++ linux/include/asm-m68k/pgtable.h Sun May 18 17:10:38 1997 @@ -175,7 +175,7 @@ } /* - * flush all atc entries (user-space entries only for the 680[46]0). + * flush all user-space atc entries. */ static inline void __flush_tlb(void) { @@ -184,7 +184,7 @@ "pflushan\n\t" ".chip 68k"); else - __asm__ __volatile__("pflusha"); + __asm__ __volatile__("pflush #0,#4"); } static inline void __flush_tlb_one(unsigned long addr) diff -u --recursive --new-file v2.1.39/linux/include/asm-m68k/processor.h linux/include/asm-m68k/processor.h --- v2.1.39/linux/include/asm-m68k/processor.h Tue May 13 22:41:17 1997 +++ linux/include/asm-m68k/processor.h Sun May 18 17:10:38 1997 @@ -47,7 +47,7 @@ #define INIT_MMAP { &init_mm, 0, 0x40000000, __pgprot(_PAGE_PRESENT|_PAGE_ACCESSED), VM_READ | VM_WRITE | VM_EXEC, NULL, &init_mm.mmap } #define INIT_TSS { \ - sizeof(init_kernel_stack) + (long) init_kernel_stack, 0, \ + sizeof(init_stack) + (unsigned long) init_stack, 0, \ PS_S, KERNEL_DS, \ {0, 0}, 0, {0,}, {0, 0, 0}, {0,}, \ } @@ -93,9 +93,11 @@ } /* Allocation and freeing of basic task resources. */ -#define alloc_task_struct() kmalloc(sizeof(struct task_struct), GFP_KERNEL) -#define alloc_kernel_stack(p) __get_free_page(GFP_KERNEL) -#define free_task_struct(p) kfree(p) -#define free_kernel_stack(page) free_page((page)) +#define alloc_task_struct() \ + ((struct task_struct *) __get_free_pages(GFP_KERNEL,1,0)) +#define free_task_struct(p) free_pages((unsigned long)(p),1) + +#define init_task (init_task_union.task) +#define init_stack (init_task_union.stack) #endif diff -u --recursive --new-file v2.1.39/linux/include/asm-m68k/ptrace.h linux/include/asm-m68k/ptrace.h --- v2.1.39/linux/include/asm-m68k/ptrace.h Wed Mar 20 12:03:24 1996 +++ linux/include/asm-m68k/ptrace.h Sun May 18 17:10:38 1997 @@ -34,6 +34,7 @@ long d5; long a0; long a1; + long a2; long d0; long orig_d0; long stkadj; @@ -50,7 +51,6 @@ struct switch_stack { unsigned long d6; unsigned long d7; - unsigned long a2; unsigned long a3; unsigned long a4; unsigned long a5; diff -u --recursive --new-file v2.1.39/linux/include/asm-m68k/semaphore.h linux/include/asm-m68k/semaphore.h --- v2.1.39/linux/include/asm-m68k/semaphore.h Tue May 13 22:41:17 1997 +++ linux/include/asm-m68k/semaphore.h Sun May 18 17:10:38 1997 @@ -73,41 +73,26 @@ * "down_failed" is a special asm handler that calls the C * routine that actually waits. See arch/m68k/lib/semaphore.S */ -extern inline void down(struct semaphore * sem) +extern inline void do_down(struct semaphore * sem, void (*failed)(void)) { register struct semaphore *sem1 __asm__ ("%a1") = sem; __asm__ __volatile__( "| atomic down operation\n\t" - "lea %%pc@(1f),%%a0\n\t" "subql #1,%0@\n\t" - "jmi " SYMBOL_NAME_STR(__down_failed) "\n" - "1:" + "jmi 2f\n" + "1:\n" + ".section .text.lock,\"ax\"\n" + ".even\n" + "2:\tpea 1b\n\t" + "jbra %1\n" + ".previous" : /* no outputs */ - : "a" (sem1) - : "%a0", "memory"); + : "a" (sem1), "m" (*(unsigned char *)failed) + : "memory"); } -/* - * This version waits in interruptible state so that the waiting - * process can be killed. The down_failed_interruptible routine - * returns negative for signalled and zero for semaphore acquired. - */ -extern inline int down_interruptible(struct semaphore * sem) -{ - register int ret __asm__ ("%d0"); - register struct semaphore *sem1 __asm__ ("%a1") = sem; - __asm__ __volatile__( - "| atomic interruptible down operation\n\t" - "lea %%pc@(1f),%%a0\n\t" - "subql #1,%1@\n\t" - "jmi " SYMBOL_NAME_STR(__down_failed_interruptible) "\n\t" - "clrl %0\n" - "1:" - : "=d" (ret) - : "a" (sem1) - : "%d0", "%a0", "memory"); - return ret; -} +#define down(sem) do_down((sem),__down_failed) +#define down_interruptible(sem) do_down((sem),__down_failed_interruptible) /* * Note! This is subtle. We jump to wake people up only if @@ -120,13 +105,17 @@ register struct semaphore *sem1 __asm__ ("%a1") = sem; __asm__ __volatile__( "| atomic up operation\n\t" - "lea %%pc@(1f),%%a0\n\t" - "addql #1,%0\n\t" - "jle " SYMBOL_NAME_STR(__up_wakeup) "\n" - "1:" + "addql #1,%0@\n\t" + "jle 2f\n" + "1:\n" + ".section .text.lock,\"ax\"\n" + ".even\n" + "2:\tpea 1b\n\t" + "jbra %1\n" + ".previous" : /* no outputs */ - : "m" (sem->count), "a" (sem1) - : "%a0", "memory"); + : "a" (sem1), "m" (*(unsigned char *)__up_wakeup) + : "memory"); } #endif diff -u --recursive --new-file v2.1.39/linux/include/asm-m68k/softirq.h linux/include/asm-m68k/softirq.h --- v2.1.39/linux/include/asm-m68k/softirq.h Tue May 13 22:41:17 1997 +++ linux/include/asm-m68k/softirq.h Sun May 18 17:10:38 1997 @@ -4,6 +4,9 @@ /* * Software interrupts.. no SMP here either. */ + +#include + #define get_active_bhs() (bh_mask & bh_active) #define clear_active_bhs(x) atomic_clear_mask((x),&bh_active) @@ -14,12 +17,6 @@ bh_mask |= 1 << nr; } -extern inline void remove_bh(int nr) -{ - bh_base[nr] = NULL; - bh_mask &= ~(1 << nr); -} - extern inline void mark_bh(int nr) { set_bit(nr, &bh_active); @@ -39,6 +36,12 @@ { if (!--bh_mask_count[nr]) bh_mask |= 1 << nr; +} + +extern inline void remove_bh(int nr) +{ + bh_base[nr] = NULL; + bh_mask &= ~(1 << nr); } extern int __m68k_bh_counter; diff -u --recursive --new-file v2.1.39/linux/include/asm-m68k/spinlock.h linux/include/asm-m68k/spinlock.h --- v2.1.39/linux/include/asm-m68k/spinlock.h Tue May 13 22:41:17 1997 +++ linux/include/asm-m68k/spinlock.h Sun May 18 17:10:38 1997 @@ -5,8 +5,8 @@ * We don't do SMP on the m68k .... at least not yet. */ -typedef struct { } spinlock_t; -#define SPIN_LOCK_UNLOCKED { } +typedef struct { int dummy; } spinlock_t; +#define SPIN_LOCK_UNLOCKED { 0 } #define spin_lock_init(lock) do { } while(0) #define spin_lock(lock) do { } while(0) @@ -31,8 +31,8 @@ * irq-safe write-lock, but readers can get non-irqsafe * read-locks. */ -typedef struct { } rwlock_t; -#define RW_LOCK_UNLOCKED { } +typedef struct { int dummy; } rwlock_t; +#define RW_LOCK_UNLOCKED { 0 } #define read_lock(lock) do { } while(0) #define read_unlock(lock) do { } while(0) diff -u --recursive --new-file v2.1.39/linux/include/asm-m68k/system.h linux/include/asm-m68k/system.h --- v2.1.39/linux/include/asm-m68k/system.h Wed Apr 23 19:01:28 1997 +++ linux/include/asm-m68k/system.h Sun May 18 17:10:38 1997 @@ -83,6 +83,7 @@ #define sti() __sti() #define save_flags(x) __save_flags(x) #define restore_flags(x) __restore_flags(x) +#define save_and_cli(flags) do { save_flags(flags); cli(); } while(0) #ifndef CONFIG_RMW_INSNS static inline unsigned long __xchg(unsigned long x, volatile void * ptr, int size) diff -u --recursive --new-file v2.1.39/linux/include/asm-m68k/unistd.h linux/include/asm-m68k/unistd.h --- v2.1.39/linux/include/asm-m68k/unistd.h Wed Apr 23 19:01:28 1997 +++ linux/include/asm-m68k/unistd.h Sun May 18 17:10:38 1997 @@ -322,9 +322,11 @@ set_fs (KERNEL_DS); __asm__ __volatile__ - ("trap #0\n\t" /* Linux/m68k system call */ + ("clrl %%d2\n\t" + "trap #0\n\t" /* Linux/m68k system call */ "tstl %0\n\t" /* child or parent */ "jne 1f\n\t" /* parent - jump */ + "lea %%sp@(-8192),%6\n\t" /* reload current */ "movel %3,%%sp@-\n\t" /* push argument */ "jsr %4@\n\t" /* call fn */ "movel %0,%%d1\n\t" /* pass exit value */ @@ -333,8 +335,8 @@ "1:" : "=d" (retval) : "0" (__NR_clone), "i" (__NR_exit), - "r" (arg), "a" (fn), "d" (clone_arg) - : "d0"); + "r" (arg), "a" (fn), "d" (clone_arg), "r" (current) + : "d0", "d2"); set_fs (fs); return retval; diff -u --recursive --new-file v2.1.39/linux/include/asm-mips/bootinfo.h linux/include/asm-mips/bootinfo.h --- v2.1.39/linux/include/asm-mips/bootinfo.h Wed Dec 13 22:16:53 1995 +++ linux/include/asm-mips/bootinfo.h Sun May 18 17:10:38 1997 @@ -8,7 +8,7 @@ * Copyright (C) 1992 by Greg Harp * * This file is subject to the terms and conditions of the GNU General Public - * License. See the file README.legal in the main directory of this archive + * License. See the file COPYING in the main directory of this archive * for more details. */ #ifndef __ASM_MIPS_BOOTINFO_H diff -u --recursive --new-file v2.1.39/linux/include/asm-mips/keyboard.h linux/include/asm-mips/keyboard.h --- v2.1.39/linux/include/asm-mips/keyboard.h Wed Dec 31 16:00:00 1969 +++ linux/include/asm-mips/keyboard.h Wed May 21 09:57:46 1997 @@ -0,0 +1,44 @@ +/* + * linux/include/asm-mips/keyboard.h + * + * Created 3 Nov 1996 by Geert Uytterhoeven + */ + +/* + * This file contains the mips architecture specific keyboard definitions + */ + +#ifndef __ASMMIPS_KEYBOARD_H +#define __ASMMIPS_KEYBOARD_H + +#ifdef __KERNEL__ + +#define KEYBOARD_IRQ 1 +#define DISABLE_KBD_DURING_INTERRUPTS 0 + +#define KBD_REPORT_ERR +#define KBD_REPORT_UNKN +/* #define KBD_IS_FOCUS_9000 */ + +extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode); +extern int pckbd_getkeycode(unsigned int scancode); +extern int pckbd_pretranslate(unsigned char scancode, char raw_mode); +extern int pckbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode); +extern char pckbd_unexpected_up(unsigned char keycode); +extern void pckbd_leds(unsigned char leds); +extern void pckbd_init_hw(void); + +#define kbd_setkeycode pckbd_setkeycode +#define kbd_getkeycode pckbd_getkeycode +#define kbd_pretranslate pckbd_pretranslate +#define kbd_translate pckbd_translate +#define kbd_unexpected_up pckbd_unexpected_up +#define kbd_leds pckbd_leds +#define kbd_init_hw pckbd_init_hw + +#define INIT_KBD + +#endif /* __KERNEL__ */ + +#endif /* __ASMMIPS_KEYBOARD_H */ diff -u --recursive --new-file v2.1.39/linux/include/asm-ppc/keyboard.h linux/include/asm-ppc/keyboard.h --- v2.1.39/linux/include/asm-ppc/keyboard.h Wed Dec 31 16:00:00 1969 +++ linux/include/asm-ppc/keyboard.h Wed May 21 09:57:45 1997 @@ -0,0 +1,44 @@ +/* + * linux/include/asm-ppc/keyboard.h + * + * Created 3 Nov 1996 by Geert Uytterhoeven + */ + +/* + * This file contains the ppc architecture specific keyboard definitions + */ + +#ifndef __ASMPPC_KEYBOARD_H +#define __ASMPPC_KEYBOARD_H + +#ifdef __KERNEL__ + +#define KEYBOARD_IRQ 1 +#define DISABLE_KBD_DURING_INTERRUPTS 0 + +#define KBD_REPORT_ERR +#define KBD_REPORT_UNKN +/* #define KBD_IS_FOCUS_9000 */ + +extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode); +extern int pckbd_getkeycode(unsigned int scancode); +extern int pckbd_pretranslate(unsigned char scancode, char raw_mode); +extern int pckbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode); +extern char pckbd_unexpected_up(unsigned char keycode); +extern void pckbd_leds(unsigned char leds); +extern void pckbd_init_hw(void); + +#define kbd_setkeycode pckbd_setkeycode +#define kbd_getkeycode pckbd_getkeycode +#define kbd_pretranslate pckbd_pretranslate +#define kbd_translate pckbd_translate +#define kbd_unexpected_up pckbd_unexpected_up +#define kbd_leds pckbd_leds +#define kbd_init_hw pckbd_init_hw + +#define INIT_KBD + +#endif /* __KERNEL__ */ + +#endif /* __ASMPPC_KEYBOARD_H */ diff -u --recursive --new-file v2.1.39/linux/include/asm-sparc64/elf.h linux/include/asm-sparc64/elf.h --- v2.1.39/linux/include/asm-sparc64/elf.h Thu May 15 16:48:04 1997 +++ linux/include/asm-sparc64/elf.h Sun May 18 17:10:38 1997 @@ -1,4 +1,4 @@ -/* $Id: elf.h,v 1.4 1997/05/04 07:21:21 davem Exp $ */ +/* $Id: elf.h,v 1.6 1997/05/17 11:51:27 davem Exp $ */ #ifndef __ASM_SPARC64_ELF_H #define __ASM_SPARC64_ELF_H @@ -7,6 +7,7 @@ */ #include +#include typedef unsigned long elf_greg_t; @@ -19,9 +20,13 @@ * These are used to set parameters in the core dumps. */ #ifndef ELF_ARCH -#define ELF_ARCH EM_SPARC64 -#define ELF_CLASS ELFCLASS64 -#define ELF_DATA ELFDATA2MSB; +#define ELF_ARCH EM_SPARC64 +#define ELF_CLASS ELFCLASS64 +#define ELF_DATA ELFDATA2MSB; +#endif + +#ifndef ELF_FLAGS_INIT +#define ELF_FLAGS_INIT current->tss.flags &= ~SPARC_FLAG_32BIT #endif /* @@ -30,7 +35,6 @@ #define elf_check_arch(x) ((x) == ELF_ARCH) /* Might be EM_SPARC64 or EM_SPARC */ #define USE_ELF_CORE_DUMP -#define ELF_EXEC_PAGESIZE 4096 - +#define ELF_EXEC_PAGESIZE 8192 #endif /* !(__ASM_SPARC64_ELF_H) */ diff -u --recursive --new-file v2.1.39/linux/include/asm-sparc64/head.h linux/include/asm-sparc64/head.h --- v2.1.39/linux/include/asm-sparc64/head.h Tue May 13 22:41:18 1997 +++ linux/include/asm-sparc64/head.h Sun May 18 17:10:38 1997 @@ -1,4 +1,4 @@ -/* $Id: head.h,v 1.17 1997/04/28 14:57:13 davem Exp $ */ +/* $Id: head.h,v 1.19 1997/05/18 08:42:18 davem Exp $ */ #ifndef _SPARC64_HEAD_H #define _SPARC64_HEAD_H @@ -147,6 +147,9 @@ flushw; \ done; nop; nop; nop; nop; nop; nop; +/* Before touching these macros, you owe it to yourself to go and + * see how arch/sparc64/kernel/winfixup.S works... -DaveM + */ /* Normal kernel spill */ #define SPILL_0_NORMAL \ @@ -189,23 +192,25 @@ stxa %i6, [%sp + STACK_BIAS + 0x70] %asi; \ stxa %i7, [%sp + STACK_BIAS + 0x78] %asi; \ saved; retry; nop; nop; nop; nop; nop; nop; \ - nop; nop; nop; nop; nop; nop; nop; + nop; nop; nop; nop; nop; nop; \ + b,a,pt %xcc, spill_fixup; /* Normal 32bit spill */ #define SPILL_2_GENERIC(xxx) \ wr %g0, xxx, %asi; \ srl %sp, 0, %sp; \ stda %l0, [%sp + 0x00] %asi; \ - stda %l2, [%sp + 0x10] %asi; \ - stda %l4, [%sp + 0x20] %asi; \ - stda %l6, [%sp + 0x30] %asi; \ - stda %i0, [%sp + 0x40] %asi; \ - stda %i2, [%sp + 0x50] %asi; \ - stda %i4, [%sp + 0x60] %asi; \ - stda %i6, [%sp + 0x70] %asi; \ + stda %l2, [%sp + 0x08] %asi; \ + stda %l4, [%sp + 0x10] %asi; \ + stda %l6, [%sp + 0x18] %asi; \ + stda %i0, [%sp + 0x20] %asi; \ + stda %i2, [%sp + 0x28] %asi; \ + stda %i4, [%sp + 0x30] %asi; \ + stda %i6, [%sp + 0x38] %asi; \ saved; retry; nop; nop; nop; nop; \ nop; nop; nop; nop; nop; nop; nop; nop; \ - nop; nop; nop; nop; nop; nop; nop; nop; + nop; nop; nop; nop; nop; nop; nop; \ + b,a,pt %xcc, spill_fixup; #define SPILL_1_NORMAL SPILL_1_GENERIC(ASI_AIUP) #define SPILL_2_NORMAL SPILL_2_GENERIC(ASI_AIUP) @@ -265,23 +270,25 @@ ldxa [%sp + STACK_BIAS + 0x70] %asi, %i6; \ ldxa [%sp + STACK_BIAS + 0x78] %asi, %i7; \ restored; retry; nop; nop; nop; nop; nop; nop; \ - nop; nop; nop; nop; nop; nop; nop; + nop; nop; nop; nop; nop; nop; \ + b,a,pt %xcc, fill_fixup; /* Normal 32bit fill */ #define FILL_2_GENERIC(xxx) \ wr %g0, xxx, %asi; \ srl %sp, 0, %sp; \ ldda [%sp + 0x00] %asi, %l0; \ - ldda [%sp + 0x10] %asi, %l2; \ - ldda [%sp + 0x20] %asi, %l4; \ - ldda [%sp + 0x30] %asi, %l6; \ - ldda [%sp + 0x40] %asi, %i0; \ - ldda [%sp + 0x50] %asi, %i2; \ - ldda [%sp + 0x60] %asi, %i4; \ - ldda [%sp + 0x70] %asi, %i6; \ + ldda [%sp + 0x08] %asi, %l2; \ + ldda [%sp + 0x10] %asi, %l4; \ + ldda [%sp + 0x18] %asi, %l6; \ + ldda [%sp + 0x20] %asi, %i0; \ + ldda [%sp + 0x28] %asi, %i2; \ + ldda [%sp + 0x30] %asi, %i4; \ + ldda [%sp + 0x38] %asi, %i6; \ restored; retry; nop; nop; nop; nop; \ nop; nop; nop; nop; nop; nop; nop; nop; \ - nop; nop; nop; nop; nop; nop; nop; nop; + nop; nop; nop; nop; nop; nop; nop; \ + b,a,pt %xcc, fill_fixup; #define FILL_1_NORMAL FILL_1_GENERIC(ASI_AIUP) #define FILL_2_NORMAL FILL_2_GENERIC(ASI_AIUP) diff -u --recursive --new-file v2.1.39/linux/include/asm-sparc64/mmu_context.h linux/include/asm-sparc64/mmu_context.h --- v2.1.39/linux/include/asm-sparc64/mmu_context.h Mon Apr 14 16:28:23 1997 +++ linux/include/asm-sparc64/mmu_context.h Sun May 18 17:10:38 1997 @@ -1,4 +1,4 @@ -/* $Id: mmu_context.h,v 1.7 1997/04/04 00:50:23 davem Exp $ */ +/* $Id: mmu_context.h,v 1.8 1997/05/18 20:44:23 davem Exp $ */ #ifndef __SPARC64_MMU_CONTEXT_H #define __SPARC64_MMU_CONTEXT_H @@ -63,11 +63,21 @@ !(tsk->tss.flags & SPARC_FLAG_KTHREAD) && !(tsk->flags & PF_EXITING)) { unsigned long ctx = tlb_context_cache; + register unsigned long paddr asm("o5"); flushw_user(); if((mm->context ^ ctx) & CTX_VERSION_MASK) get_new_mmu_context(mm, ctx); spitfire_set_secondary_context(mm->context); + paddr = __pa(mm->pgd); + __asm__ __volatile__(" + rdpr %%pstate, %%o4 + wrpr %%o4, %1, %%pstate + mov %0, %%g7 + wrpr %%o4, 0x0, %%pstate + " : /* no outputs */ + : "r" (paddr), "i" (PSTATE_MG|PSTATE_IE) + : "o4"); } } diff -u --recursive --new-file v2.1.39/linux/include/asm-sparc64/pgtable.h linux/include/asm-sparc64/pgtable.h --- v2.1.39/linux/include/asm-sparc64/pgtable.h Wed Apr 23 19:01:28 1997 +++ linux/include/asm-sparc64/pgtable.h Sun May 18 17:10:38 1997 @@ -1,7 +1,7 @@ -/* $Id: pgtable.h,v 1.28 1997/04/14 17:05:19 jj Exp $ +/* $Id: pgtable.h,v 1.31 1997/05/18 21:11:42 davem Exp $ * pgtable.h: SpitFire page table operations. * - * Copyright 1996 David S. Miller (davem@caip.rutgers.edu) + * Copyright 1996,1997 David S. Miller (davem@caip.rutgers.edu) */ #ifndef _SPARC64_PGTABLE_H @@ -204,13 +204,8 @@ unsigned long addr; flushw_all(); - for(addr = 0; addr < (PAGE_SIZE << 1); addr += 32) { + for(addr = 0; addr < (PAGE_SIZE << 1); addr += 32) spitfire_put_icache_tag(addr, 0x0UL); - membar("#Sync"); - } - - /* Kill the pipeline. */ - flushi(PAGE_OFFSET); } extern __inline__ void flush_cache_mm(struct mm_struct *mm) @@ -219,13 +214,8 @@ unsigned long addr; flushw_user(); - for(addr = 0; addr < (PAGE_SIZE << 1); addr += 32) { + for(addr = 0; addr < (PAGE_SIZE << 1); addr += 32) spitfire_put_icache_tag(addr, 0x0UL); - membar("#Sync"); - } - - /* Kill the pipeline. */ - flushi(PAGE_OFFSET); } } @@ -236,13 +226,8 @@ unsigned long addr; flushw_user(); - for(addr = 0; addr < (PAGE_SIZE << 1); addr += 32) { + for(addr = 0; addr < (PAGE_SIZE << 1); addr += 32) spitfire_put_icache_tag(addr, 0x0UL); - membar("#Sync"); - } - - /* Kill the pipeline. */ - flushi(PAGE_OFFSET); } } @@ -254,13 +239,8 @@ unsigned long addr; flushw_user(); - for(addr = 0; addr < (PAGE_SIZE << 1); addr += 32) { + for(addr = 0; addr < (PAGE_SIZE << 1); addr += 32) spitfire_put_icache_tag(addr, 0x0UL); - membar("#Sync"); - } - - /* Kill the pipeline. */ - flushi(PAGE_OFFSET); } } @@ -290,15 +270,28 @@ extern __inline__ void flush_tlb_mm(struct mm_struct *mm) { if(mm->context != NO_CONTEXT) { - unsigned long orig_ctx = spitfire_get_secondary_context(); - unsigned long flags; - - save_and_cli(flags); - spitfire_set_secondary_context(mm->context); - spitfire_flush_dtlb_secondary_context(); - spitfire_flush_itlb_secondary_context(); - spitfire_set_secondary_context(orig_ctx); - restore_flags(flags); + __asm__ __volatile__(" + /* flush_tlb_mm() */ + rdpr %%pil, %%g1 + mov %1, %%g7 + wrpr %%g0, 15, %%pil + ldxa [%%g7] %2, %%g2 + cmp %%g2, %0 + be,pt %%icc, 1f + mov 0x50, %%g3 + stxa %0, [%%g7] %2 +1: + stxa %%g0, [%%g3] %3 + stxa %%g0, [%%g3] %4 + bne,a,pn %%icc, 1f + stxa %%g2, [%%g7] %2 +1: + flush %%g4 + wrpr %%g1, 0x0, %%pil +" : /* no outputs */ + : "r" (mm->context), "i" (SECONDARY_CONTEXT), "i" (ASI_DMMU), + "i" (ASI_DMMU_DEMAP), "i" (ASI_IMMU_DEMAP) + : "g1", "g2", "g3", "g7", "cc"); } } @@ -307,17 +300,21 @@ { if(mm->context != NO_CONTEXT) { unsigned long old_ctx = spitfire_get_secondary_context(); + unsigned long new_ctx = mm->context; unsigned long flags; start &= PAGE_MASK; save_and_cli(flags); - spitfire_set_secondary_context(mm->context); + if(new_ctx != old_ctx) + spitfire_set_secondary_context(mm->context); while(start < end) { spitfire_flush_dtlb_secondary_page(start); spitfire_flush_itlb_secondary_page(start); start += PAGE_SIZE; } - spitfire_set_secondary_context(old_ctx); + if(new_ctx != old_ctx) + spitfire_set_secondary_context(old_ctx); + __asm__ __volatile__("flush %g4"); restore_flags(flags); } } @@ -327,17 +324,31 @@ struct mm_struct *mm = vma->vm_mm; if(mm->context != NO_CONTEXT) { - unsigned long old_ctx = spitfire_get_secondary_context(); - unsigned long flags; - - page &= PAGE_MASK; - save_and_cli(flags); - spitfire_set_secondary_context(mm->context); - if(vma->vm_flags & VM_EXEC) - spitfire_flush_itlb_secondary_page(page); - spitfire_flush_dtlb_secondary_page(page); - spitfire_set_secondary_context(old_ctx); - restore_flags(flags); + __asm__ __volatile__(" + /* flush_tlb_page() */ + rdpr %%pil, %%g1 + mov %1, %%g7 + wrpr %%g0, 15, %%pil + ldxa [%%g7] %2, %%g2 + cmp %%g2, %0 + be,pt %%icc, 1f + or %5, 0x10, %5 + stxa %0, [%%g7] %2 +1: + stxa %%g0, [%5] %3 + brnz,a %6, 1f + stxa %%g0, [%5] %4 +1: + bne,a,pn %%icc, 1f + stxa %%g2, [%%g7] %2 +1: + flush %%g4 + wrpr %%g1, 0x0, %%pil +" : /* no outputs */ + : "r" (mm->context), "i" (SECONDARY_CONTEXT), "i" (ASI_DMMU), + "i" (ASI_DMMU_DEMAP), "i" (ASI_IMMU_DEMAP), "r" (page & PAGE_MASK), + "r" (vma->vm_flags & VM_EXEC) + : "g1", "g2", "g3", "g7", "cc"); } } @@ -649,7 +660,7 @@ start += PAGE_SIZE; } } - } while((vmaring = vmaring->vm_next_share) != inode->i_mmap); + } while((vmaring = vmaring->vm_next_share) != NULL); if(alias_found && (pte_val(pte) & _PAGE_CV)) { pgdp = pgd_offset(vma->vm_mm, address); diff -u --recursive --new-file v2.1.39/linux/include/asm-sparc64/processor.h linux/include/asm-sparc64/processor.h --- v2.1.39/linux/include/asm-sparc64/processor.h Thu May 15 16:48:04 1997 +++ linux/include/asm-sparc64/processor.h Sun May 18 17:10:38 1997 @@ -1,4 +1,4 @@ -/* $Id: processor.h,v 1.24 1997/05/04 07:21:21 davem Exp $ +/* $Id: processor.h,v 1.26 1997/05/17 05:59:10 davem Exp $ * include/asm-sparc64/processor.h * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -115,6 +115,7 @@ regs->tpc = ((pc & (~3)) - 4); \ regs->tnpc = regs->tpc + 4; \ regs->y = 0; \ + current->tss.flags &= ~SPARC_FLAG_32BIT; \ __asm__ __volatile__( \ "stx %%g0, [%0 + %2 + 0x00]\n\t" \ "stx %%g0, [%0 + %2 + 0x08]\n\t" \ @@ -132,7 +133,7 @@ "stx %%g0, [%0 + %2 + 0x68]\n\t" \ "stx %1, [%0 + %2 + 0x70]\n\t" \ "stx %%g0, [%0 + %2 + 0x78]\n\t" \ - "wrpr %%g0, 1, %%wstate\n\t" \ + "wrpr %%g0, (1 << 3), %%wstate\n\t" \ : \ : "r" (regs), "r" (sp - REGWIN_SZ), \ "i" ((const unsigned long)(&((struct pt_regs *)0)->u_regs[0]))); \ @@ -168,7 +169,7 @@ "stx %%g0, [%0 + %2 + 0x68]\n\t" \ "stx %1, [%0 + %2 + 0x70]\n\t" \ "stx %%g0, [%0 + %2 + 0x78]\n\t" \ - "wrpr %%g0, 2, %%wstate\n\t" \ + "wrpr %%g0, (2 << 3), %%wstate\n\t" \ : \ : "r" (regs), "r" (sp - REGWIN32_SZ), \ "i" ((const unsigned long)(&((struct pt_regs *)0)->u_regs[0])), \ diff -u --recursive --new-file v2.1.39/linux/include/asm-sparc64/spitfire.h linux/include/asm-sparc64/spitfire.h --- v2.1.39/linux/include/asm-sparc64/spitfire.h Mon Apr 14 16:28:24 1997 +++ linux/include/asm-sparc64/spitfire.h Sun May 18 17:10:38 1997 @@ -1,4 +1,4 @@ -/* $Id: spitfire.h,v 1.7 1997/04/04 00:50:29 davem Exp $ +/* $Id: spitfire.h,v 1.8 1997/05/18 04:16:56 davem Exp $ * spitfire.h: SpitFire/BlackBird/Cheetah inline MMU operations. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -209,28 +209,28 @@ { __asm__ __volatile__("stxa %%g0, [%0] %1" : /* No outputs */ - : "r" (0x41), "i" (ASI_DMMU_DEMAP)); + : "r" (0x50), "i" (ASI_DMMU_DEMAP)); } extern __inline__ void spitfire_flush_itlb_secondary_context(void) { __asm__ __volatile__("stxa %%g0, [%0] %1" : /* No outputs */ - : "r" (0x41), "i" (ASI_IMMU_DEMAP)); + : "r" (0x50), "i" (ASI_IMMU_DEMAP)); } extern __inline__ void spitfire_flush_dtlb_nucleus_context(void) { __asm__ __volatile__("stxa %%g0, [%0] %1" : /* No outputs */ - : "r" (0x42), "i" (ASI_DMMU_DEMAP)); + : "r" (0x60), "i" (ASI_DMMU_DEMAP)); } extern __inline__ void spitfire_flush_itlb_nucleus_context(void) { __asm__ __volatile__("stxa %%g0, [%0] %1" : /* No outputs */ - : "r" (0x42), "i" (ASI_IMMU_DEMAP)); + : "r" (0x60), "i" (ASI_IMMU_DEMAP)); } /* Page level flushes. */ diff -u --recursive --new-file v2.1.39/linux/include/asm-sparc64/string.h linux/include/asm-sparc64/string.h --- v2.1.39/linux/include/asm-sparc64/string.h Mon Apr 14 16:28:24 1997 +++ linux/include/asm-sparc64/string.h Sun May 18 17:10:38 1997 @@ -1,4 +1,4 @@ -/* $Id: string.h,v 1.4 1997/04/01 09:34:41 davem Exp $ +/* $Id: string.h,v 1.5 1997/05/18 04:16:57 davem Exp $ * string.h: External definitions for optimized assembly string * routines for the Linux Kernel. * @@ -41,14 +41,18 @@ if(n <= 32) { __builtin_memcpy(to, from, n); } else { +#if 0 switch(n) { case 8192: __copy_1page(to, from); break; default: +#endif __memcpy(to, from, n); +#if 0 break; } +#endif } } return to; @@ -74,9 +78,11 @@ extern __kernel_size_t __bzero(void *, __kernel_size_t); if(!c) { +#if 0 if(count == 8192) bzero_1page(s); else +#endif __bzero(s, count); } else { __memset(s, c, count); diff -u --recursive --new-file v2.1.39/linux/include/asm-sparc64/system.h linux/include/asm-sparc64/system.h --- v2.1.39/linux/include/asm-sparc64/system.h Thu May 15 16:48:04 1997 +++ linux/include/asm-sparc64/system.h Sun May 18 17:10:38 1997 @@ -1,4 +1,4 @@ -/* $Id: system.h,v 1.16 1997/05/14 20:48:07 davem Exp $ */ +/* $Id: system.h,v 1.19 1997/05/18 22:52:32 davem Exp $ */ #ifndef __SPARC64_SYSTEM_H #define __SPARC64_SYSTEM_H @@ -89,18 +89,24 @@ #ifndef __ASSEMBLY__ +extern void synchronize_user_stack(void); + extern __inline__ void flushw_user(void) { __asm__ __volatile__(" rdpr %%otherwin, %%g1 + brz,pt %%g1, 2f + clr %%g2 1: - rdpr %%otherwin, %%g2 - brnz,pn %%g2, 1b - save %%sp, %0, %%sp + save %%sp, %0, %%sp + rdpr %%otherwin, %%g1 + brnz,pt %%g1, 1b + add %%g2, 1, %%g2 1: - subcc %%g1, 1, %%g1 - bne,pn %%xcc, 1b + subcc %%g2, 1, %%g2 + bne,pt %%xcc, 1b restore %%g0, %%g0, %%g0 +2: " : : "i" (-REGWIN_SZ) : "g1", "g2", "cc"); } @@ -122,9 +128,9 @@ /* See what happens when you design the chip correctly? * NOTE NOTE NOTE this is extremely non-trivial what I - * am doing here. GCC needs only two registers to stuff - * things into ('next' and ¤t_set[cpu]) So I "claim" - * that I do not clobber them, when in fact I do. Please, + * am doing here. GCC needs only one register to stuff + * things into ('next' in particular) So I "claim" that + * I do not clobber it, when in fact I do. Please, * when modifying this code inspect output of sched.s very * carefully to make sure things still work. -DaveM */ @@ -132,7 +138,6 @@ do { \ __label__ switch_continue; \ register unsigned long task_pc asm("o7"); \ - extern struct task_struct *current_set[NR_CPUS]; \ SWITCH_DO_LAZY_FPU(next); \ task_pc = ((unsigned long) &&switch_continue) - 0x8; \ __asm__ __volatile__( \ @@ -142,14 +147,12 @@ "stx %%o6, [%%g6 + %3]\n\t" \ "rdpr %%wstate, %%o5\n\t" \ "stx %%o7, [%%g6 + %4]\n\t" \ - "mov %6, %%o4\n\t" \ "stx %%o5, [%%g6 + %2]\n\t" \ - "st %%o4, [%%g6 + %7]\n\t" \ "rdpr %%cwp, %%o5\n\t" \ - "stx %%o5, [%%g6 + %8]\n\t" \ - "mov %1, %%g6\n\t" \ - "stx %%g6, [%0]\n\t" \ - "ldx [%%g6 + %8], %%g1\n\t" \ + "stx %%o5, [%%g6 + %5]\n\t" \ + "mov %0, %%g6\n\t" \ + "wr %0, 0x0, %%pic\n\t" \ + "ldx [%%g6 + %5], %%g1\n\t" \ "wrpr %%g1, %%cwp\n\t" \ "ldx [%%g6 + %2], %%o5\n\t" \ "ldx [%%g6 + %3], %%o6\n\t" \ @@ -159,15 +162,13 @@ "jmpl %%o7 + 0x8, %%g0\n\t" \ " ldx [%%sp + 2047 + 0x78], %%i7\n\t" \ : /* No outputs */ \ - : "r" (&(current_set[smp_processor_id()])), "r" (next), \ + : "r" (next), "r" (task_pc), \ "i" ((const unsigned long)(&((struct task_struct *)0)->tss.wstate)), \ "i" ((const unsigned long)(&((struct task_struct *)0)->tss.ksp)), \ "i" ((const unsigned long)(&((struct task_struct *)0)->tss.kpc)), \ - "r" (task_pc), "i" (255), \ - "i" ((const unsigned long)(&((struct task_struct *)0)->processor)), \ "i" ((const unsigned long)(&((struct task_struct *)0)->tss.cwp)) \ : "cc", "g1", "g2", "g3", "g5", "g7", \ - "l2", "l3", "l4", "l5", "l6", "l7", \ + "l1", "l2", "l3", "l4", "l5", "l6", "l7", \ "i0", "i1", "i2", "i3", "i4", "i5", \ "o0", "o1", "o2", "o3", "o4", "o5"); \ switch_continue: } while(0) diff -u --recursive --new-file v2.1.39/linux/include/linux/elf.h linux/include/linux/elf.h --- v2.1.39/linux/include/linux/elf.h Thu Mar 27 14:40:10 1997 +++ linux/include/linux/elf.h Sun May 18 17:10:38 1997 @@ -3,11 +3,20 @@ #include -typedef unsigned long Elf32_Addr; -typedef unsigned short Elf32_Half; -typedef unsigned long Elf32_Off; -typedef long Elf32_Sword; -typedef unsigned long Elf32_Word; +/* 32-bit ELF base types. */ +typedef __u32 Elf32_Addr; +typedef __u16 Elf32_Half; +typedef __u32 Elf32_Off; +typedef __s32 Elf32_Sword; +typedef __u32 Elf32_Word; + +/* 64-bit ELF base types. */ +typedef __u64 Elf64_Addr; +typedef __u16 Elf64_Half; +typedef __s16 Elf64_SHalf; +typedef __u64 Elf64_Off; +typedef __s64 Elf64_Sword; +typedef __u64 Elf64_Word; /* These constants are for the segment types stored in the image headers */ #define PT_NULL 0 @@ -128,10 +137,10 @@ } Elf32_Dyn; typedef struct { - unsigned long long d_tag; /* entry tag value */ + Elf64_Word d_tag; /* entry tag value */ union { - unsigned long long d_val; - unsigned long long d_ptr; + Elf64_Word d_val; + Elf64_Word d_ptr; } d_un; } Elf64_Dyn; @@ -226,8 +235,8 @@ } Elf32_Rel; typedef struct elf64_rel { - unsigned long long r_offset; /* Location at which to apply the action */ - unsigned long long r_info; /* index and type of relocation */ + Elf64_Addr r_offset; /* Location at which to apply the action */ + Elf64_Word r_info; /* index and type of relocation */ } Elf64_Rel; typedef struct elf32_rela{ @@ -237,9 +246,9 @@ } Elf32_Rela; typedef struct elf64_rela { - unsigned long long r_offset; /* Location at which to apply the action */ - unsigned long long r_info; /* index and type of relocation */ - unsigned long long r_addend; /* Constant addend used to compute value */ + Elf64_Addr r_offset; /* Location at which to apply the action */ + Elf64_Word r_info; /* index and type of relocation */ + Elf64_Word r_addend; /* Constant addend used to compute value */ } Elf64_Rela; typedef struct elf32_sym{ @@ -252,12 +261,12 @@ } Elf32_Sym; typedef struct elf64_sym { - unsigned int st_name; /* Symbol name, index in string tbl */ - unsigned char st_info; /* Type and binding attributes */ - unsigned char st_other; /* No defined meaning, 0 */ - unsigned short st_shndx; /* Associated section index */ - unsigned long long st_value; /* Value of the symbol */ - unsigned long long st_size; /* Associated symbol size */ + Elf32_Word st_name; /* Symbol name, index in string tbl (yes, Elf32) */ + unsigned char st_info; /* Type and binding attributes */ + unsigned char st_other; /* No defined meaning, 0 */ + Elf64_Half st_shndx; /* Associated section index */ + Elf64_Addr st_value; /* Value of the symbol */ + Elf64_Word st_size; /* Associated symbol size */ } Elf64_Sym; @@ -282,19 +291,19 @@ typedef struct elf64_hdr { unsigned char e_ident[16]; /* ELF "magic number" */ - short int e_type; - short unsigned int e_machine; - int e_version; - unsigned long long e_entry; /* Entry point virtual address */ - unsigned long long e_phoff; /* Program header table file offset */ - unsigned long long e_shoff; /* Section header table file offset */ - int e_flags; - short int e_ehsize; - short int e_phentsize; - short int e_phnum; - short int e_shentsize; - short int e_shnum; - short int e_shstrndx; + Elf64_SHalf e_type; + Elf64_Half e_machine; + __s32 e_version; + Elf64_Addr e_entry; /* Entry point virtual address */ + Elf64_Off e_phoff; /* Program header table file offset */ + Elf64_Off e_shoff; /* Section header table file offset */ + __s32 e_flags; + Elf64_SHalf e_ehsize; + Elf64_SHalf e_phentsize; + Elf64_SHalf e_phnum; + Elf64_SHalf e_shentsize; + Elf64_SHalf e_shnum; + Elf64_SHalf e_shstrndx; } Elf64_Ehdr; /* These constants define the permissions on sections in the program @@ -315,14 +324,14 @@ } Elf32_Phdr; typedef struct elf64_phdr { - int p_type; - int p_flags; - unsigned long long p_offset; /* Segment file offset */ - unsigned long long p_vaddr; /* Segment virtual address */ - unsigned long long p_paddr; /* Segment physical address */ - unsigned long long p_filesz; /* Segment size in file */ - unsigned long long p_memsz; /* Segment size in memory */ - unsigned long long p_align; /* Segment alignment, file & memory */ + __s32 p_type; + __s32 p_flags; + Elf64_Off p_offset; /* Segment file offset */ + Elf64_Addr p_vaddr; /* Segment virtual address */ + Elf64_Addr p_paddr; /* Segment physical address */ + Elf64_Word p_filesz; /* Segment size in file */ + Elf64_Word p_memsz; /* Segment size in memory */ + Elf64_Word p_align; /* Segment alignment, file & memory */ } Elf64_Phdr; /* sh_type */ @@ -373,16 +382,16 @@ } Elf32_Shdr; typedef struct elf64_shdr { - unsigned int sh_name; /* Section name, index in string tbl */ - unsigned int sh_type; /* Type of section */ - unsigned long long sh_flags; /* Miscellaneous section attributes */ - unsigned long long sh_addr; /* Section virtual addr at execution */ - unsigned long long sh_offset; /* Section file offset */ - unsigned long long sh_size; /* Size of section in bytes */ - unsigned int sh_link; /* Index of another section */ - unsigned int sh_info; /* Additional section information */ - unsigned long long sh_addralign; /* Section alignment */ - unsigned long long sh_entsize; /* Entry size if section holds table */ + Elf32_Word sh_name; /* Section name, index in string tbl (yes Elf32) */ + Elf32_Word sh_type; /* Type of section (yes Elf32) */ + Elf64_Word sh_flags; /* Miscellaneous section attributes */ + Elf64_Addr sh_addr; /* Section virtual addr at execution */ + Elf64_Off sh_offset; /* Section file offset */ + Elf64_Word sh_size; /* Size of section in bytes */ + Elf32_Word sh_link; /* Index of another section (yes Elf32) */ + Elf32_Word sh_info; /* Additional section information (yes Elf32) */ + Elf64_Word sh_addralign; /* Section alignment */ + Elf64_Word sh_entsize; /* Entry size if section holds table */ } Elf64_Shdr; #define EI_MAG0 0 /* e_ident[] indexes */ @@ -434,9 +443,9 @@ * is only 32 bits. */ typedef struct elf64_note { - unsigned int n_namesz; /* Name size */ - unsigned int n_descsz; /* Content size */ - unsigned int n_type; /* Content type */ + Elf32_Word n_namesz; /* Name size */ + Elf32_Word n_descsz; /* Content size */ + Elf32_Word n_type; /* Content type */ } Elf64_Nhdr; #define ELF_START_MMAP 0x80000000 diff -u --recursive --new-file v2.1.39/linux/include/linux/keyboard.h linux/include/linux/keyboard.h --- v2.1.39/linux/include/linux/keyboard.h Fri Apr 4 08:52:25 1997 +++ linux/include/linux/keyboard.h Wed May 21 09:57:45 1997 @@ -302,7 +302,7 @@ #define K_F243 K(KT_FN,252) #define K_F244 K(KT_FN,253) #define K_F245 K(KT_FN,254) -#define K_F246 K(KT_FN,255) +#define K_UNDO K(KT_FN,255) #define K_HOLE K(KT_SPEC,0) @@ -347,8 +347,10 @@ #define K_PCOMMA K(KT_PAD,15) /* key-pad comma: kludge... */ #define K_PDOT K(KT_PAD,16) /* key-pad dot (period): kludge... */ #define K_PPLUSMINUS K(KT_PAD,17) /* key-pad plus/minus */ +#define K_PPARENL K(KT_PAD,18) /* key-pad left parenthesis */ +#define K_PPARENR K(KT_PAD,19) /* key-pad right parenthesis */ -#define NR_PAD 18 +#define NR_PAD 20 #define K_DGRAVE K(KT_DEAD,0) #define K_DACUTE K(KT_DEAD,1) diff -u --recursive --new-file v2.1.39/linux/include/linux/pci.h linux/include/linux/pci.h --- v2.1.39/linux/include/linux/pci.h Wed Apr 23 19:01:29 1997 +++ linux/include/linux/pci.h Mon May 19 12:49:29 1997 @@ -331,6 +331,7 @@ #define PCI_VENDOR_ID_HP 0x103c #define PCI_DEVICE_ID_HP_J2585A 0x1030 +#define PCI_DEVICE_ID_HP_J2585B 0x1031 #define PCI_VENDOR_ID_PCTECH 0x1042 #define PCI_DEVICE_ID_PCTECH_RZ1000 0x1000 @@ -408,6 +409,9 @@ #define PCI_DEVICE_ID_VISION_QD8500 0x0001 #define PCI_DEVICE_ID_VISION_QD8580 0x0002 +#define PCI_VENDOR_ID_BROOKTREE 0x109e +#define PCI_DEVICE_ID_BT848 0x0350 /* 0x350 = 848 */ + #define PCI_VENDOR_ID_SIERRA 0x10a8 #define PCI_DEVICE_ID_SIERRA_STB 0x0000 @@ -595,6 +599,7 @@ #define PCI_DEVICE_ID_INTEL_82437VX 0x7030 #define PCI_DEVICE_ID_INTEL_82371AB 0x7111 #define PCI_DEVICE_ID_INTEL_P6 0x84c4 +#define PCI_DEVICE_ID_INTEL_P6_2 0x84c5 #define PCI_VENDOR_ID_KTI 0x8e2e #define PCI_DEVICE_ID_KTI_ET32P2 0x3000 diff -u --recursive --new-file v2.1.39/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h --- v2.1.39/linux/include/linux/proc_fs.h Tue May 13 22:41:19 1997 +++ linux/include/linux/proc_fs.h Wed May 21 16:14:44 1997 @@ -169,6 +169,7 @@ PROC_SCSI_PPA, PROC_SCSI_ESP, PROC_SCSI_QLOGICPTI, + PROC_SCSI_AMIGA7XX, PROC_SCSI_A3000, PROC_SCSI_A2091, PROC_SCSI_GVP11, diff -u --recursive --new-file v2.1.39/linux/include/linux/sched.h linux/include/linux/sched.h --- v2.1.39/linux/include/linux/sched.h Mon May 19 12:57:39 1997 +++ linux/include/linux/sched.h Wed May 21 16:14:44 1997 @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include #include @@ -223,7 +225,8 @@ unsigned long it_real_value, it_prof_value, it_virt_value; unsigned long it_real_incr, it_prof_incr, it_virt_incr; struct timer_list real_timer; - long utime, stime, cutime, cstime, start_time; + struct tms times; + unsigned long start_time; /* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */ unsigned long min_flt, maj_flt, nswap, cmin_flt, cmaj_flt, cnswap; int swappable:1; @@ -319,7 +322,7 @@ /* uid etc */ 0,0,0,0,0,0,0,0, \ /* timeout */ 0,SCHED_OTHER,0,0,0,0,0,0,0, \ /* timer */ { NULL, NULL, 0, 0, it_real_fn }, \ -/* utime */ 0,0,0,0,0, \ +/* utime */ {0,0,0,0},0, \ /* flt */ 0,0,0,0,0,0, \ /* swp */ 0,0,0,0,0, \ /* rlimits */ INIT_RLIMITS, \ diff -u --recursive --new-file v2.1.39/linux/include/linux/sysctl.h linux/include/linux/sysctl.h --- v2.1.39/linux/include/linux/sysctl.h Thu May 15 16:48:05 1997 +++ linux/include/linux/sysctl.h Sun May 18 17:10:38 1997 @@ -148,6 +148,8 @@ NET_IPV4_SECURE_REDIRECTS, NET_IPV4_RFC1620_REDIRECTS, NET_TCP_SYN_RETRIES, + NET_IPFRAG_HIGH_THRESH, + NET_IPFRAG_LOW_THRESH, }; diff -u --recursive --new-file v2.1.39/linux/include/linux/zorro.h linux/include/linux/zorro.h --- v2.1.39/linux/include/linux/zorro.h Tue May 13 22:41:19 1997 +++ linux/include/linux/zorro.h Sun May 18 17:10:38 1997 @@ -301,9 +301,6 @@ #define PROD_MASTER_CARD_SCSI (0x04) /* Master Card SCSI Controller */ #define PROD_MVD_819 (0x07) /* MVD 819 */ -#define MANUF_DELACOMP (0x0873) /* DelaComp */ -#define PROD_DELACOMP_RAM_2000 (0x01) /* RAM Expansion 2000 */ - #define MANUF_VILLAGE_TRONIC (0x0877) /* Village Tronic */ #define PROD_DOMINO_RAM (0x01) /* Domino Graphics Board */ #define PROD_DOMINO_REG (0x02) diff -u --recursive --new-file v2.1.39/linux/init/main.c linux/init/main.c --- v2.1.39/linux/init/main.c Thu May 15 16:48:05 1997 +++ linux/init/main.c Sun May 18 17:10:38 1997 @@ -193,6 +193,7 @@ #endif extern void wd33c93_setup (char *str, int *ints); extern void gvp11_setup (char *str, int *ints); +extern void ncr53c7xx_setup (char *str, int *ints); #ifdef CONFIG_CYCLADES extern void cy_setup(char *str, int *ints); @@ -476,6 +477,9 @@ #endif #ifdef CONFIG_ATARI_SCSI { "atascsi=", atari_scsi_setup }, +#endif +#if defined(CONFIG_A4000T_SCSI) || defined(CONFIG_WARPENGINE_SCSI) || defined(CONFIG_A4091_SCSI) + { "53c7xx=", ncr53c7xx_setup }, #endif #if defined(CONFIG_A3000_SCSI) || defined(CONFIG_A2091_SCSI) \ || defined(CONFIG_GVP11_SCSI) diff -u --recursive --new-file v2.1.39/linux/kernel/exit.c linux/kernel/exit.c --- v2.1.39/linux/kernel/exit.c Tue May 13 22:41:20 1997 +++ linux/kernel/exit.c Mon May 19 12:32:52 1997 @@ -625,8 +625,8 @@ retval = p->pid; goto end_wait4; case TASK_ZOMBIE: - current->cutime += p->utime + p->cutime; - current->cstime += p->stime + p->cstime; + current->times.tms_cutime += p->times.tms_utime + p->times.tms_cutime; + current->times.tms_cstime += p->times.tms_stime + p->times.tms_cstime; read_unlock(&tasklist_lock); if (ru != NULL) getrusage(p, RUSAGE_BOTH, ru); diff -u --recursive --new-file v2.1.39/linux/kernel/fork.c linux/kernel/fork.c --- v2.1.39/linux/kernel/fork.c Thu May 15 16:48:05 1997 +++ linux/kernel/fork.c Mon May 19 12:34:30 1997 @@ -395,8 +395,8 @@ p->real_timer.data = (unsigned long) p; p->leader = 0; /* session leadership doesn't inherit */ p->tty_old_pgrp = 0; - p->utime = p->stime = 0; - p->cutime = p->cstime = 0; + p->times.tms_utime = p->times.tms_stime = 0; + p->times.tms_cutime = p->times.tms_cstime = 0; #ifdef __SMP__ p->has_cpu = 0; p->processor = NO_PROC_ID; diff -u --recursive --new-file v2.1.39/linux/kernel/ksyms.c linux/kernel/ksyms.c --- v2.1.39/linux/kernel/ksyms.c Thu May 15 16:48:05 1997 +++ linux/kernel/ksyms.c Sun May 18 17:10:38 1997 @@ -52,10 +52,6 @@ #include #include -#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_HD) || defined(CONFIG_BLK_DEV_IDE_MODULE) || defined(CONFIG_BLK_DEV_HD_MODULE) -extern struct drive_info_struct drive_info; -#endif - extern unsigned char aux_device_present, kbd_read_mask; #ifdef CONFIG_PCI @@ -217,10 +213,6 @@ EXPORT_SYMBOL(gendisk_head); EXPORT_SYMBOL(resetup_one_dev); EXPORT_SYMBOL(unplug_device); - -#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_HD) || defined(CONFIG_BLK_DEV_IDE_MODULE) || defined(CONFIG_BLK_DEV_HD_MODULE) -EXPORT_SYMBOL(drive_info); -#endif /* tty routines */ EXPORT_SYMBOL(tty_hangup); diff -u --recursive --new-file v2.1.39/linux/kernel/sched.c linux/kernel/sched.c --- v2.1.39/linux/kernel/sched.c Tue May 13 22:41:20 1997 +++ linux/kernel/sched.c Mon May 19 11:38:12 1997 @@ -1000,16 +1000,14 @@ { long psecs; - p->utime += user; - p->stime += system; - - psecs = (p->stime + p->utime) / HZ; - if (psecs > p->rlim[RLIMIT_CPU].rlim_cur) { + psecs = (p->times.tms_utime += user); + psecs += (p->times.tms_stime += system); + if (psecs / HZ > p->rlim[RLIMIT_CPU].rlim_cur) { /* Send SIGXCPU every second.. */ - if (psecs * HZ == p->stime + p->utime) + if (!(psecs % HZ)) send_sig(SIGXCPU, p, 1); /* and SIGKILL when we go over max.. */ - if (psecs > p->rlim[RLIMIT_CPU].rlim_max) + if (psecs / HZ > p->rlim[RLIMIT_CPU].rlim_max) send_sig(SIGKILL, p, 1); } } diff -u --recursive --new-file v2.1.39/linux/kernel/sys.c linux/kernel/sys.c --- v2.1.39/linux/kernel/sys.c Tue May 13 22:41:20 1997 +++ linux/kernel/sys.c Mon May 19 12:27:22 1997 @@ -370,8 +370,8 @@ if (acct_active) { strncpy(ac.ac_comm, current->comm, ACCT_COMM); ac.ac_comm[ACCT_COMM-1] = '\0'; - ac.ac_utime = current->utime; - ac.ac_stime = current->stime; + ac.ac_utime = current->times.tms_utime; + ac.ac_stime = current->times.tms_stime; ac.ac_btime = CT_TO_SECS(current->start_time) + (xtime.tv_sec - (jiffies / HZ)); ac.ac_etime = CURRENT_TIME - ac.ac_btime; ac.ac_uid = current->uid; @@ -695,16 +695,9 @@ * atomically safe type this is just fine. Conceptually its * as if the syscall took an instant longer to occur. */ - if (tbuf) - { - /* ?? use copy_to_user() */ - if(!access_ok(VERIFY_READ, tbuf, sizeof(struct tms)) || - __put_user(current->utime,&tbuf->tms_utime)|| - __put_user(current->stime,&tbuf->tms_stime) || - __put_user(current->cutime,&tbuf->tms_cutime) || - __put_user(current->cstime,&tbuf->tms_cstime)) + if (tbuf) + if (copy_to_user(tbuf, ¤t->times, sizeof(struct tms))) return -EFAULT; - } return jiffies; } @@ -1030,28 +1023,28 @@ memset((char *) &r, 0, sizeof(r)); switch (who) { case RUSAGE_SELF: - r.ru_utime.tv_sec = CT_TO_SECS(p->utime); - r.ru_utime.tv_usec = CT_TO_USECS(p->utime); - r.ru_stime.tv_sec = CT_TO_SECS(p->stime); - r.ru_stime.tv_usec = CT_TO_USECS(p->stime); + r.ru_utime.tv_sec = CT_TO_SECS(p->times.tms_utime); + r.ru_utime.tv_usec = CT_TO_USECS(p->times.tms_utime); + r.ru_stime.tv_sec = CT_TO_SECS(p->times.tms_stime); + r.ru_stime.tv_usec = CT_TO_USECS(p->times.tms_stime); r.ru_minflt = p->min_flt; r.ru_majflt = p->maj_flt; r.ru_nswap = p->nswap; break; case RUSAGE_CHILDREN: - r.ru_utime.tv_sec = CT_TO_SECS(p->cutime); - r.ru_utime.tv_usec = CT_TO_USECS(p->cutime); - r.ru_stime.tv_sec = CT_TO_SECS(p->cstime); - r.ru_stime.tv_usec = CT_TO_USECS(p->cstime); + r.ru_utime.tv_sec = CT_TO_SECS(p->times.tms_cutime); + r.ru_utime.tv_usec = CT_TO_USECS(p->times.tms_cutime); + r.ru_stime.tv_sec = CT_TO_SECS(p->times.tms_cstime); + r.ru_stime.tv_usec = CT_TO_USECS(p->times.tms_cstime); r.ru_minflt = p->cmin_flt; r.ru_majflt = p->cmaj_flt; r.ru_nswap = p->cnswap; break; default: - r.ru_utime.tv_sec = CT_TO_SECS(p->utime + p->cutime); - r.ru_utime.tv_usec = CT_TO_USECS(p->utime + p->cutime); - r.ru_stime.tv_sec = CT_TO_SECS(p->stime + p->cstime); - r.ru_stime.tv_usec = CT_TO_USECS(p->stime + p->cstime); + r.ru_utime.tv_sec = CT_TO_SECS(p->times.tms_utime + p->times.tms_cutime); + r.ru_utime.tv_usec = CT_TO_USECS(p->times.tms_utime + p->times.tms_cutime); + r.ru_stime.tv_sec = CT_TO_SECS(p->times.tms_stime + p->times.tms_cstime); + r.ru_stime.tv_usec = CT_TO_USECS(p->times.tms_stime + p->times.tms_cstime); r.ru_minflt = p->min_flt + p->cmin_flt; r.ru_majflt = p->maj_flt + p->cmaj_flt; r.ru_nswap = p->nswap + p->cnswap; diff -u --recursive --new-file v2.1.39/linux/mm/memory.c linux/mm/memory.c --- v2.1.39/linux/mm/memory.c Mon May 19 12:57:39 1997 +++ linux/mm/memory.c Tue May 20 10:17:15 1997 @@ -833,10 +833,6 @@ } /* - * The above separate functions for the no-page and wp-page - * cases will go away (they mostly do the same thing anyway), - * and we'll instead use only a general "handle_mm_fault()". - * * These routines also need to handle stuff like marking pages dirty * and/or accessed for architectures that don't do it in hardware (most * RISC architectures). The early dirtying is also good on the i386. diff -u --recursive --new-file v2.1.39/linux/mm/slab.c linux/mm/slab.c --- v2.1.39/linux/mm/slab.c Thu May 15 16:48:05 1997 +++ linux/mm/slab.c Sun May 18 17:10:38 1997 @@ -105,10 +105,10 @@ #include #include #include +#include #include #include -#include #include #include diff -u --recursive --new-file v2.1.39/linux/net/core/dev.c linux/net/core/dev.c --- v2.1.39/linux/net/core/dev.c Wed Apr 23 19:01:29 1997 +++ linux/net/core/dev.c Sun May 18 16:38:35 1997 @@ -986,6 +986,9 @@ /* * Loop over the interfaces, and write an info block for each. */ + + dev_lock_wait(); + dev_lock_list(); for (dev = dev_base; dev != NULL; dev = dev->next) { @@ -1013,6 +1016,8 @@ len -= sizeof(struct ifreq); } + dev_unlock_list(); + /* * All done. Write the updated control block back to the caller. */ diff -u --recursive --new-file v2.1.39/linux/net/ipv4/ip_fragment.c linux/net/ipv4/ip_fragment.c --- v2.1.39/linux/net/ipv4/ip_fragment.c Thu May 15 16:48:06 1997 +++ linux/net/ipv4/ip_fragment.c Sun May 18 17:10:38 1997 @@ -5,7 +5,7 @@ * * The IP fragmentation functionality. * - * Version: $Id: ip_fragment.c,v 1.21 1997/05/13 07:45:08 davem Exp $ + * Version: $Id: ip_fragment.c,v 1.22 1997/05/17 05:21:56 freitag Exp $ * * Authors: Fred N. van Kempen * Alan Cox @@ -13,6 +13,7 @@ * Fixes: * Alan Cox : Split from ip.c , see ip_input.c for history. * David S. Miller : Begin massive cleanup... + * Andi Kleen : Add sysctls. */ #include @@ -37,8 +38,8 @@ * even the most extreme cases without allowing an attacker to measurably * harm machine performance. */ -#define IPFRAG_HIGH_THRESH (256*1024) -#define IPFRAG_LOW_THRESH (192*1024) +int sysctl_ipfrag_high_thresh = 256*1024; +int sysctl_ipfrag_low_thresh = 192*1024; /* Describe an IP fragment. */ struct ipfrag { @@ -203,7 +204,7 @@ */ static void ip_evictor(void) { - while(atomic_read(&ip_frag_mem)>IPFRAG_LOW_THRESH) { + while(atomic_read(&ip_frag_mem)>sysctl_ipfrag_low_thresh) { int i; /* FIXME: Make LRU queue of frag heads. -DaveM */ @@ -382,7 +383,7 @@ ip_statistics.IpReasmReqds++; /* Start by cleaning up the memory. */ - if(atomic_read(&ip_frag_mem)>IPFRAG_HIGH_THRESH) + if(atomic_read(&ip_frag_mem)>sysctl_ipfrag_high_thresh) ip_evictor(); /* Find the entry of this IP datagram in the "incomplete datagrams" queue. */ diff -u --recursive --new-file v2.1.39/linux/net/ipv4/sysctl_net_ipv4.c linux/net/ipv4/sysctl_net_ipv4.c --- v2.1.39/linux/net/ipv4/sysctl_net_ipv4.c Thu May 15 16:48:06 1997 +++ linux/net/ipv4/sysctl_net_ipv4.c Sun May 18 17:10:38 1997 @@ -35,6 +35,10 @@ extern int sysctl_arp_confirm_interval; extern int sysctl_arp_confirm_timeout; +/* From ip_fragment.c */ +extern int sysctl_ipfrag_low_thresh; +extern int sysctl_ipfrag_high_thresh; + extern int sysctl_tcp_cong_avoidance; extern int sysctl_tcp_hoe_retransmits; extern int sysctl_tcp_sack; @@ -147,6 +151,10 @@ &proc_dointvec}, {NET_TCP_SYN_RETRIES, "tcp_syn_retries", &sysctl_syn_retries, sizeof(int), 0644, NULL, &proc_dointvec}, + {NET_IPFRAG_HIGH_THRESH, "ipfrag_high_thresh", + &sysctl_ipfrag_high_thresh, sizeof(int), 0644, NULL, &proc_dointvec}, + {NET_IPFRAG_LOW_THRESH, "ipfrag_low_thresh", + &sysctl_ipfrag_low_thresh, sizeof(int), 0644, NULL, &proc_dointvec}, {0} }; diff -u --recursive --new-file v2.1.39/linux/net/ipv4/udp.c linux/net/ipv4/udp.c --- v2.1.39/linux/net/ipv4/udp.c Wed Apr 23 19:01:30 1997 +++ linux/net/ipv4/udp.c Wed May 21 18:21:45 1997 @@ -154,7 +154,7 @@ return retval; } -static inline int udp_lport_inuse(int num) +static inline int udp_lport_inuse(u16 num) { struct sock *sk = udp_hash[num & (UDP_HTABLE_SIZE - 1)]; @@ -168,36 +168,42 @@ /* Shared by v4/v6 tcp. */ unsigned short udp_good_socknum(void) { - static int start = 0; - unsigned short base; - int i, best = 0, size = 32767; /* a big num. */ int result; - - base = PROT_SOCK + (start & 1023) + 1; + static int start = 0; + int i, best, best_size_so_far; SOCKHASH_LOCK(); - for(i = 0; i < UDP_HTABLE_SIZE; i++) { - struct sock *sk = udp_hash[i]; - if(!sk) { - start = (i + 1 + start) & 1023; - result = i + base + 1; + + /* Select initial not-so-random "best" */ + best = PROT_SOCK + 1 + (start & 1023); + best_size_so_far = 32767; /* "big" num */ + result = best; + for (i = 0; i < UDP_HTABLE_SIZE; i++, result++) { + struct sock *sk; + int size; + + sk = udp_hash[result & (UDP_HTABLE_SIZE - 1)]; + + /* No clashes - take it */ + if (!sk) goto out; - } else { - int j = 0; - do { - if(++j >= size) - goto next; - } while((sk = sk->next)); - best = i; - size = j; - } - next: + + /* Is this one better than our best so far? */ + size = 0; + do { + if(++size >= best_size_so_far) + goto next; + } while((sk = sk->next) != NULL); + best_size_so_far = size; + best = result; +next: } - while(udp_lport_inuse(base + best + 1)) + while (udp_lport_inuse(best)) best += UDP_HTABLE_SIZE; - result = (best + base + 1); + result = best; out: + start = result; SOCKHASH_UNLOCK(); return result; }