S: Kanata, Ontario
S: CANADA K2L 1S2
+N: Andrew Tridgell
+E: Andrew.Tridgell@anu.edu.au
+D: dosemu, networking, samba
+S: 3 Ballow Crescent
+S: MacGregor A.C.T
+S: 2615 Australia
+
N: Theodore Ts'o
E: tytso@mit.edu
D: Random Linux hacker
VERSION = 1
PATCHLEVEL = 3
-SUBLEVEL = 3
+SUBLEVEL = 4
ARCH = i386
.quad sys_umask, do_entSys, do_entSys, sys_getpgrp, sys_getpagesize
.quad do_entSys, osf_vfork, sys_newstat, sys_newlstat, do_entSys
.quad do_entSys, osf_mmap, do_entSys, sys_munmap, sys_mprotect
- .quad sys_madvise, do_entSys, do_entSys, do_entSys, sys_getgroups
+ .quad sys_madvise, sys_vhangup, do_entSys, do_entSys, sys_getgroups
/* map BSD's setpgrp to sys_setpgid for binary compatibility: */
.quad sys_setgroups, do_entSys, sys_setpgid, sys_setitimer, do_entSys
.quad do_entSys, sys_getitimer, sys_gethostname, sys_sethostname, sys_getdtablesize
.quad sys_dup2, sys_newfstat, sys_fcntl, sys_select, do_entSys
- .quad sys_fsync, sys_setpriority, sys_socket, do_entSys, do_entSys
-/*100*/ .quad do_entSys, do_entSys, do_entSys, sys_sigreturn, sys_bind
- .quad do_entSys, sys_listen, do_entSys, do_entSys, do_entSys
+ .quad sys_fsync, sys_setpriority, sys_socket, sys_connect, sys_accept
+/*100*/ .quad sys_getpriority, sys_send, sys_recv, sys_sigreturn, sys_bind
+ .quad sys_setsockopt, sys_listen, do_entSys, do_entSys, do_entSys
.quad do_entSys, sys_sigsuspend, do_entSys, do_entSys, do_entSys
- .quad do_entSys, sys_gettimeofday, sys_getrusage, do_entSys, do_entSys
+ .quad do_entSys, sys_gettimeofday, sys_getrusage, sys_getsockopt, do_entSys
.quad do_entSys, do_entSys, sys_settimeofday, sys_fchown, sys_fchmod
- .quad do_entSys, sys_setreuid, sys_setregid, sys_rename, sys_truncate
- .quad sys_ftruncate, do_entSys, sys_setgid, do_entSys, do_entSys
- .quad do_entSys, sys_mkdir, sys_rmdir, sys_utimes, do_entSys
- .quad do_entSys, do_entSys, do_entSys, do_entSys, sys_getrlimit
+ .quad sys_recvfrom, sys_setreuid, sys_setregid, sys_rename, sys_truncate
+ .quad sys_ftruncate, do_entSys, sys_setgid, sys_sendto, sys_shutdown
+ .quad sys_socketpair, sys_mkdir, sys_rmdir, sys_utimes, do_entSys
+ .quad do_entSys, sys_getpeername, do_entSys, do_entSys, sys_getrlimit
.quad sys_setrlimit, do_entSys, sys_setsid, do_entSys, do_entSys
-/*150*/ .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
+/*150*/ .quad sys_getsockname, do_entSys, do_entSys, do_entSys, do_entSys
.quad do_entSys, sys_sigaction, do_entSys, do_entSys, osf_getdirentries
.quad osf_statfs, osf_fstatfs, do_entSys, do_entSys, do_entSys
.quad osf_getdomainname, do_entSys, do_entSys, do_entSys, do_entSys
.quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
.quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
.quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
- .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
+ .quad do_entSys, do_entSys, do_entSys, do_entSys, osf_proplist_syscall
.quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
/*250*/ .quad do_entSys, osf_usleep_thread, do_entSys, do_entSys, do_entSys
.quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
.quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
/* linux-specific system calls start at 300 */
/*300*/ .quad sys_bdflush, sys_sethae, sys_mount, sys_adjtimex, sys_swapoff
- .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
+ .quad sys_getdents, sys_create_module, sys_init_module, sys_delete_module, sys_get_kernel_syms
int error;
};
-static int osf_filldir(void * __buf, char * name, int namlen, off_t offset, ino_t ino)
+static int osf_filldir(void * __buf, const char * name, int namlen, off_t offset, ino_t ino)
{
struct osf_dirent * dirent;
struct osf_dirent_callback * buf = (struct osf_dirent_callback *) __buf;
*/
return raddr;
}
+
+
+/*
+ * The following stuff should move into a header file should it ever
+ * be labeled "officially supported." Right now, there is just enough
+ * support to avoid applications (such as tar) printing error
+ * messages. The attributes are not really implemented.
+ */
+
+/*
+ * Values for Property list entry flag
+ */
+#define PLE_PROPAGATE_ON_COPY 0x1 /* cp(1) will copy entry
+ by default */
+#define PLE_FLAG_MASK 0x1 /* Valid flag values */
+#define PLE_FLAG_ALL -1 /* All flag value */
+
+struct proplistname_args {
+ unsigned int pl_mask;
+ unsigned int pl_numnames;
+ char **pl_names;
+};
+
+union pl_args {
+ struct setargs {
+ char *path;
+ long follow;
+ long nbytes;
+ char *buf;
+ } set;
+ struct fsetargs {
+ long fd;
+ long nbytes;
+ char *buf;
+ } fset;
+ struct getargs {
+ char *path;
+ long follow;
+ struct proplistname_args *name_args;
+ long nbytes;
+ char *buf;
+ int *min_buf_size;
+ } get;
+ struct fgetargs {
+ long fd;
+ struct proplistname_args *name_args;
+ long nbytes;
+ char *buf;
+ int *min_buf_size;
+ } fget;
+ struct delargs {
+ char *path;
+ long follow;
+ struct proplistname_args *name_args;
+ } del;
+ struct fdelargs {
+ long fd;
+ struct proplistname_args *name_args;
+ } fdel;
+};
+
+enum pl_code {
+ PL_SET = 1, PL_FSET = 2,
+ PL_GET = 3, PL_FGET = 4,
+ PL_DEL = 5, PL_FDEL = 6
+};
+
+asmlinkage long osf_proplist_syscall (enum pl_code code, union pl_args *args)
+{
+ long error;
+ int *min_buf_size_ptr;
+
+ switch (code) {
+ case PL_SET:
+ error = verify_area(VERIFY_READ, &args->set.nbytes,
+ sizeof(args->set.nbytes));
+ if (error)
+ return error;
+ return args->set.nbytes;
+
+ case PL_FSET:
+ error = verify_area(VERIFY_READ, &args->fset.nbytes,
+ sizeof(args->fset.nbytes));
+ if (error)
+ return error;
+ return args->fset.nbytes;
+
+ case PL_GET:
+ error = verify_area(VERIFY_READ, &args->get.min_buf_size,
+ sizeof(args->get.min_buf_size));
+ if (error)
+ return error;
+ min_buf_size_ptr = get_user(&args->get.min_buf_size);
+ error = verify_area(VERIFY_WRITE, min_buf_size_ptr,
+ sizeof(*min_buf_size_ptr));
+ if (error)
+ return error;
+ put_user(0, min_buf_size_ptr);
+ return 0;
+
+ case PL_FGET:
+ error = verify_area(VERIFY_READ, &args->fget.min_buf_size,
+ sizeof(args->fget.min_buf_size));
+ if (error)
+ return error;
+ min_buf_size_ptr = get_user(&args->fget.min_buf_size);
+ error = verify_area(VERIFY_WRITE, min_buf_size_ptr,
+ sizeof(*min_buf_size_ptr));
+ if (error)
+ return error;
+ put_user(0, min_buf_size_ptr);
+ return 0;
+
+ case PL_DEL:
+ case PL_FDEL:
+ return 0;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+}
struct hwrpb_struct *hwrpb;
-unsigned char aux_device_present;
+unsigned char aux_device_present = 0xaa;
/*
* This is setup by the secondary bootstrap loader. Because
/*
* The format of "screen_info" is strange, and due to early
* i386-setup code. This is just enough to make the console
- * code think we're on a EGA+ colour display.
+ * code think we're on a VGA color display.
*/
struct screen_info screen_info = {
0, 0, /* orig-x, orig-y */
80, /* orig-video-cols */
0,0,0, /* ega_ax, ega_bx, ega_cx */
25, /* orig-video-lines */
+ 1, /* orig-video-isVGA */
16 /* orig-video-points */
};
set_hae(hae.cache); /* sync HAE register w/hae_cache */
ROOT_DEV = 0x0802; /* sda2 */
- aux_device_present = 0xaa;
command_line[COMMAND_LINE_SIZE - 1] = '\0';
strcpy(command_line, COMMAND_LINE);
bool 'Adaptec AHA152X support' CONFIG_SCSI_AHA152X y
bool 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 n
bool 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 y
-bool 'Adaptec AHA274X/284X support' CONFIG_SCSI_AHA274X n
+if [ "$CONFIG_PCI" = "y" ]; then
+ bool 'Adaptec AHA274X/284X/294X support' CONFIG_SCSI_AIC7XXX n
+fi
bool 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC n
bool 'EATA-DMA (DPT,NEC&ATT for ISA,EISA,PCI) support' CONFIG_SCSI_EATA_DMA n
bool 'UltraStor 14F/34F support' CONFIG_SCSI_U14_34F n
bool 'EtherWorks 3 support' CONFIG_EWRK3 n
if [ "$CONFIG_NET_ALPHA" = "y" ]; then
bool 'AT1700 support' CONFIG_AT1700 n
-# bool 'EtherExpressPro support' CONFIG_EEXPRESS_PRO n
+ bool 'EtherExpressPro support' CONFIG_EEXPRESS_PRO n
bool 'EtherExpress support' CONFIG_EEXPRESS n
bool 'NI5210 support' CONFIG_NI52 n
bool 'NI6510 support' CONFIG_NI65 n
bool 'xiafs filesystem support' CONFIG_XIA_FS n
bool 'msdos fs support' CONFIG_MSDOS_FS y
if [ "$CONFIG_MSDOS_FS" = "y" ]; then
-#bool 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS n
-comment 'Umsdos is not supported in 1.3.0: wait for 1.3.1'
+bool 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS n
fi
bool '/proc filesystem support' CONFIG_PROC_FS y
if [ "$CONFIG_INET" = "y" ]; then
elf_ppnt->p_offset & 0xfffff000);
#ifdef LOW_ELF_STACK
- if(elf_ppnt->p_vaddr & 0xfffff000 < elf_stack)
+ if((elf_ppnt->p_vaddr & 0xfffff000) < elf_stack)
elf_stack = elf_ppnt->p_vaddr & 0xfffff000;
#endif
if(k > elf_bss) elf_bss = k;
sys_close(fd);
- if (error != elf_phdata->p_vaddr & 0xfffff000) {
+ if (error != (elf_phdata->p_vaddr & 0xfffff000)) {
kfree(elf_phdata);
return error;
}
.long _sys_swapon
.long _sys_reboot
.long _old_readdir
- .long _sys_mmap /* 90 */
+ .long _old_mmap /* 90 */
.long _sys_munmap
.long _sys_truncate
.long _sys_ftruncate
.long _sys_getdents
.long _sys_select
.long _sys_flock
- .space (NR_syscalls-140)*4
+ .space (NR_syscalls-143)*4
.globl _empty_bad_page
.globl _empty_bad_page_table
.globl _empty_zero_page
-.globl _floppy_track_buffer
#define __ASSEMBLY__
#include <linux/tasks.h>
_empty_zero_page:
.org 0x6000
-/*
- * floppy_track_buffer is used to buffer one track of floppy data: it
- * has to be separate from the tmp_floppy area, as otherwise a single-
- * sector read/write can mess it up. It can contain one full cylinder (sic) of
- * data (36*2*512 bytes).
- */
-_floppy_track_buffer:
- .fill 512*2*MAX_BUFFER_SECTORS,1,0
-
+
stack_start:
.long _init_user_stack+4096
.long KERNEL_DS
hlt_counter--;
}
-asmlinkage int sys_pipe(unsigned long * fildes)
-{
- int fd[2];
- int error;
-
- error = verify_area(VERIFY_WRITE,fildes,8);
- if (error)
- return error;
- error = do_pipe(fd);
- if (error)
- return error;
- put_fs_long(fd[0],0+fildes);
- put_fs_long(fd[1],1+fildes);
- return 0;
-}
-
/*
* The idle loop on a i386..
*/
#include <linux/msg.h>
#include <linux/shm.h>
#include <linux/stat.h>
+#include <linux/mman.h>
#include <asm/segment.h>
/*
- * Perform the select(nd, in, out, ex, tv) system call.
- * Linux/i386 didn't use to be able to handle 5 system call
- * parameters, so the old select used a memory block for
- * parameter passing..
+ * sys_pipe() is the normal C calling standard for creating
+ * a pipe. It's not the way unix tranditionally does this, though.
*/
+asmlinkage int sys_pipe(unsigned long * fildes)
+{
+ int fd[2];
+ int error;
+
+ error = verify_area(VERIFY_WRITE,fildes,8);
+ if (error)
+ return error;
+ error = do_pipe(fd);
+ if (error)
+ return error;
+ put_fs_long(fd[0],0+fildes);
+ put_fs_long(fd[1],1+fildes);
+ return 0;
+}
+
+/*
+ * Perform the select(nd, in, out, ex, tv) and mmap() system
+ * calls. Linux/i386 didn't use to be able to handle more than
+ * 4 system call parameters, so these system calls used a memory
+ * block for parameter passing..
+ */
+asmlinkage int old_mmap(unsigned long *buffer)
+{
+ int error;
+ unsigned long flags;
+ struct file * file = NULL;
+
+ error = verify_area(VERIFY_READ, buffer, 6*sizeof(long));
+ if (error)
+ return error;
+ flags = get_user(buffer+3);
+ if (!(flags & MAP_ANONYMOUS)) {
+ unsigned long fd = get_user(buffer+4);
+ if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
+ return -EBADF;
+ }
+ return do_mmap(file, get_user(buffer), get_user(buffer+1),
+ get_user(buffer+2), flags, get_user(buffer+5));
+}
+
+
extern asmlinkage int sys_select(int, fd_set *, fd_set *, fd_set *, struct timeval *);
asmlinkage int old_select(unsigned long *buffer)
unsigned long *stack, addr, module_start, module_end;
extern char start_kernel, etext;
- if (regs->cp0_status & (ST0_ERL|ST0_EXL) == 0)
+ if ((regs->cp0_status & (ST0_ERL|ST0_EXL)) == 0)
return;
sp = (unsigned long *)regs->reg29;
# I ignore it and eliminate those mappings during vm initialization and
# just leave the low mapping.
#
-LINKFLAGS = -N -Ttext 0x00004000
+LINKFLAGS = -N -Ttext 0xf0004000
CFLAGS := $(CFLAGS) -pipe
HEAD := arch/sparc/kernel/head.o
-SUBDIRS := $(SUBDIRS) arch/sparc/kernel arch/sparc/lib arch/sparc/mm
+SUBDIRS := $(SUBDIRS) arch/sparc/kernel arch/sparc/lib arch/sparc/mm \
+ arch/sparc/prom
+
ARCHIVES := arch/sparc/kernel/kernel.o arch/sparc/mm/mm.o $(ARCHIVES)
-LIBS := $(TOPDIR)/lib/lib.a $(LIBS) $(TOPDIR)/arch/sparc/lib/lib.a
+
+aoutimage: vmlinux
+ elftoaout -o aoutimage vmlinux
+
+
+LIBS := $(TOPDIR)/lib/lib.a $(LIBS) $(TOPDIR)/arch/sparc/lib/lib.a \
+ $(TOPDIR)/arch/sparc/prom/promlib.a
+
archclean:
+ rm -f $(TOPDIR)/arch/sparc/boot/boot
archdep:
--- /dev/null
+# Makefile for the Sparc low level /boot module.
+#
+# Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+
+OBJS =bare.o init_me.o ../kernel/promops.o ../lib/lib.a
+BOOTLINKFLAGS = -N -Ttext 0x200000 -e _first_adr_in_text
+
+.c.s:
+ $(CC) $(CFLAGS) -S -o $*.s $<
+.s.o:
+ $(AS) -o $*.o $<
+.c.o:
+ $(CC) $(CFLAGS) -c -o $*.o $<
+.S.s:
+ $(CC) -D__ASSEMBLY__ -D__KERNEL__ -ansi -E -o $*.o $<
+.S.o:
+ $(CC) -D__ASSEMBLY__ -D__KERNEL__ -ansi -c -o $*.o $<
+
+all: boot
+
+boot: $(OBJS)
+ $(LD) $(BOOTLINKFLAGS) $(OBJS) -o boot
+
+dep:
+ $(CPP) -M *.c > .depend
+ $(CPP) -M -D__ASSEMBLY__ -ansi *.S >>.depend
+
+dummy:
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
--- /dev/null
+This directory will contain the code necessary to compile and link the
+/boot program which is necessary to boot on the Sparc. This program
+is real ugly and it knows too much. It must be able to not only boot
+off of the root partition but also be able to netboot. This means
+that it knows about RPC and NFS (bleech, yuck, eeewwwww!!) so that it
+can remote mount the root directory to fetch the kernel. Also it must
+be able to ARP for it's IP address and who it's boot server is. I
+think I'm getting sick.
+
+Regardless for now I will concentrate on the low-level stuff necessary
+to get the thing going. This means the low-level entry code, etc.
+The prom knows how to get "us" if we have the proper boot blocks,
+actually the boot blocks live in our logical partition on a hard drive
+whereas over NFS this isn't applicable. We have the boot blocks in
+our data area either way because we can be dual purpose.
+
+More will come....
+
+Hopefully I can write this such that it will work on almost all SUN
+machines in existance. We'll see ;(
+
--- /dev/null
+/* base.S: Ugly low-level boot program entry code. The job of this
+ * module is to parse the boot flags, try to mount the remote
+ * root filesystem and load the kernel into virtual memory.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include "bare.h"
+
+ .data
+ .globl C_LABEL(romvec)
+ .globl C_LABEL(idp_ptr)
+
+C_LABEL(romvec):
+ .word 0
+C_LABEL(idp_ptr):
+ .word 0
+
+ .text
+ .align 8
+ .globl C_LABEL(first_adr_in_text)
+
+C_LABEL(first_adr_in_text):
+
+ /* Grrr, boot block, scratching my head... */
+ .globl C_LABEL(b_block) /* Start of actual boot block */
+ .globl C_LABEL(b_block_size) /* In bytes */
+ .globl C_LABEL(b_block_cksum) /* Checksum of boot block bytes */
+
+ b start_of_execution /* XXX Hack */
+ nop
+
+ .align 8
+C_LABEL(b_block):
+ .skip (BOOTBLOCK_NENTRIES * BOOTBLOCK_ENTSIZE)
+
+C_LABEL(b_block_size):
+ .word 0
+
+C_LABEL(b_block_cksum):
+ .word 0
+
+/* Ok, the prom has left in %o0 the PROM pointer. We leave it here
+ * for when we jump into the kernel. So save out of this window before
+ * you dick with %o0. As far as I know we could be loaded *anywhere*, so
+ * we relocate ourselves to the "linked" location. Self modifying code rules.
+ */
+
+start_of_execution:
+ sethi %hi(C_LABEL(first_adr_in_text)), %o1 ! This is our top
+ or %o1, %lo(C_LABEL(first_adr_in_text)), %o1 ! of stack too.
+ sub %o1, C_STACK, %o1
+ add %o1, 0x7, %o1
+ andn %o1, 0x7, %o1
+ save %o1, 0x0, %sp ! save is an add
+here:
+ call there
+ sethi %hi(here), %o4
+there:
+ sub %o7, here-C_LABEL(first_adr_in_text), %o5
+ or %o4, %lo(here), %o4
+ cmp %o4, %o7
+ be loaded_ok
+ nop
+
+ /* Gotta relocate, compute our size sans bss segment. */
+ set C_LABEL(edata)+4, %o3
+ set C_LABEL(first_adr_in_text), %o2
+ sub %o3, %o2, %o3
+rel_loop:
+ ld [%o5], %o4
+ add %o5, 0x4, %o5
+ st %o4, [%o2]
+ subcc %o3, 0x4, %o3
+ bg rel_loop
+ add %o2, 0x4, %o2
+
+ /* Pray that we are now in a sane place in memory */
+ sethi %hi(loaded_ok), %o2
+ or %o2, %lo(loaded_ok), %o2
+ jmp %o2
+ nop
+
+loaded_ok:
+ /* Save the PROM pointer */
+ sethi %hi(C_LABEL(romvec)), %o1
+ or %o1, %lo(C_LABEL(romvec)), %o1
+ st %i0, [%o1]
+
+ /* Build a PSR we can live with */
+ rd %psr, %o1
+
+#if 0
+ andn %o1, PSR_PIL, %o1
+ sethi %hi(SANE_PSR), %g4
+ or %g4, %lo(SANE_PSR), %g4
+ or %o1, %g4, %o1
+#endif
+
+ /* V8 book says this works to calculate num_windows */
+ sethi %hi(0xffffffff), %g2
+ rd %wim, %g3
+ or %g2, %lo(0xffffffff), %g2
+ wr %g2, 0x0, %wim
+ WRITE_PAUSE
+
+ rd %wim, %g4
+ WRITE_PAUSE
+
+ wr %g3, 0x0, %wim
+ WRITE_PAUSE
+
+ /* Restore old %psr */
+ wr %o1, 0x0, %psr
+ WRITE_PAUSE
+
+ or %g0, 0x0, %g3
+1:
+ srl %g4, 0x1, %g4
+ subcc %g4, 0x0, %g0
+ bne 1b
+ add %g3, 0x1, %g3
+
+ /* %g3 now contains nwindows */
+ sethi %hi(C_LABEL(nwindows)), %o4
+ st %g3, [%o4 + %lo(C_LABEL(nwindows))]
+
+ /* Now zero out our bss segment, lord knows the nasty prom monster
+ * didn't do it for us.
+ */
+ sethi %hi(C_LABEL(end)), %g1
+ or %g1, %lo(C_LABEL(end)), %g1
+ add %g1, 0x4, %g1
+ sethi %hi(C_LABEL(edata)), %g2
+ or %g2, %lo(C_LABEL(edata)), %g2
+
+ /* Slow, inefficient, who cares, this is messy boot code */
+bzero_bss_loop:
+ st %g0, [%g2]
+ add %g2, 0x4, %g2
+ cmp %g2, %g1
+ bl bzero_bss_loop
+ nop
+
+ call C_LABEL(init_me) ! Fun with imperical constants and prom
+ nop
+
+ /* Dump back into the prom */
+get_me_out_of_here:
+ set C_LABEL(romvec), %g2
+ ld [%g2], %g2
+ ld [%g2 + 0x74], %g2
+ restore
+ call %g2
+ nop
+
+
+
--- /dev/null
+/* bare.h: Defines for the low level entry code of the BOOT program.
+ * We include in the head.h stuff that the real kernel uses
+ * and this saves a lot of repetition here.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/head.h>
+#include <asm/psr.h>
+#include <asm/cprefix.h>
+
+#define SANE_PIL (0xd00) /* No interrupts except clock and unmaskable NMI's */
+#define SANE_PSR (SANE_PIL|PSR_S|PSR_ET)
+
+#define BOOTBLOCK_NENTRIES 0x40 /* Number of entries in the boot block */
+#define BOOTBLOCK_ENTSIZE 0x04 /* Size in bytes of each boot block entry */
+
--- /dev/null
+/* imperical.h: Nasty hacks....
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#define DEF_BOGO 25
+
--- /dev/null
+/* init_me.c: Initialize imperical constants and gather some info from
+ * the boot prom.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/openprom.h> /* For property declarations and the prom structs */
+#include <asm/oplib.h>
+#include <asm/vac-ops.h>
+
+#include "imperical.h" /* Don't ask... */
+
+#define DEBUG_INIT_ME /* Tell me whats goin on */
+
+unsigned int nwindows; /* Set in bare.S */
+unsigned int nwindowsm1;
+unsigned int pac_or_vac; /* 0 means "dunno" 1 means "VAC" 2 means "PAC" */
+unsigned int pvac_size; /* Use the same two variables for a PAC and VAC */
+unsigned int pvac_linesize;
+unsigned int pac_size;
+int num_segmaps;
+int num_contexts;
+unsigned int BOGOMIPS; /* bogosity without the VAC cache on */
+unsigned int BOGOMIPS_WCACHE; /* bogosity with the VAC cache */
+unsigned int delay_factor;
+
+extern int prom_node_root;
+void (*printk)(const char *str, ...);
+
+void init_me(void)
+{
+ unsigned int grrr;
+
+ printk = romvec->pv_printf;
+ prom_node_root = prom_nextnode(0);
+ prom_getprop(prom_node_root, "mmu-npmg", &num_segmaps,
+ sizeof(unsigned int));
+
+ pvac_size = prom_getint_default(prom_node_root, "vac-size", 65536);
+
+ pvac_linesize = prom_getint_default(prom_node_root, "vac-linesize", 16);
+
+ grrr = prom_getint_default(prom_node_root, "mips-on", 0);
+ if(!grrr) {
+ grrr = prom_getint_default(prom_node_root, "clock-frequency", 0);
+ if(grrr > 15000000 && grrr < 100000000) {
+ BOGOMIPS = 3;
+ BOGOMIPS_WCACHE = grrr / 1000000;
+ } else {
+ BOGOMIPS = DEF_BOGO;
+ BOGOMIPS_WCACHE = DEF_BOGO;
+ }
+ } else (BOGOMIPS_WCACHE = grrr,
+ BOGOMIPS = prom_getint(prom_node_root, "mips-off"));
+
+#ifdef DEBUG_INIT_ME
+ (*(romvec->pv_printf))("\nBOGOMIPS %d\n", (int) BOGOMIPS);
+ (*(romvec->pv_printf))("BOGOMIPS_WCACHE %d\n", (int) BOGOMIPS_WCACHE);
+ (*(romvec->pv_printf))("pvac_size %d\n", (int) pvac_size);
+ (*(romvec->pv_printf))("pvac_linesize %d\n", (int) pvac_linesize);
+ (*(romvec->pv_printf))("num_segmaps %d\n", (int) num_segmaps);
+#endif
+
+ delay_factor = (BOGOMIPS > 3) ? ((BOGOMIPS - 2) >> 1) : 11;
+
+ (*(romvec->pv_printf))("\nLILO: \n");
+ return;
+}
# see the Configure script.
#
+echo "#define CONFIG_SPARCDEVS 1" >> $CONFIG_H
+echo "CONFIG_SPARCDEVS=y" >> $CONFIG
+
comment 'Sparc Kernel setup'
-bool 'Sparc V8 kernel' CONFIG_SPARC_V8 y
-bool 'Sparc SMP support' CONFIG_LINUX_SMP n
-bool 'Sparc SUN4M support' CONFIG_SUN4M n
-bool 'Sparc Reference MMU' CONFIG_SRMMU n
+bool 'Sun floppy controller support' CONFIG_BLK_DEV_SUNFD n
bool 'Networking support' CONFIG_NET n
bool 'Limit memory to low 16MB' CONFIG_MAX_16M n
bool 'System V IPC' CONFIG_SYSVIPC y
comment 'SCSI low-level drivers'
-bool 'Adaptec AHA152X support' CONFIG_SCSI_AHA152X n
-bool 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 y
-bool 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 n
-bool 'Adaptec AHA274X/284X support' CONFIG_SCSI_AHA274X n
-bool 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC n
-bool 'UltraStor 14F/34F support' CONFIG_SCSI_U14_34F n
-bool 'Future Domain 16xx SCSI support' CONFIG_SCSI_FUTURE_DOMAIN n
-bool 'Generic NCR5380 SCSI support' CONFIG_SCSI_GENERIC_NCR5380 n
-if [ "$CONFIG_PCI" = "y" ]; then
- bool 'NCR53c7,8xx SCSI support' CONFIG_SCSI_NCR53C7xx n
-fi
-bool 'Always IN2000 SCSI support (test release)' CONFIG_SCSI_IN2000 n
-bool 'PAS16 SCSI support' CONFIG_SCSI_PAS16 n
-bool 'QLOGIC SCSI support' CONFIG_SCSI_QLOGIC n
-bool 'Seagate ST-02 and Future Domain TMC-8xx SCSI support' CONFIG_SCSI_SEAGATE n
-bool 'Trantor T128/T128F/T228 SCSI support' CONFIG_SCSI_T128 n
-bool 'UltraStor SCSI support' CONFIG_SCSI_ULTRASTOR n
-bool '7000FASST SCSI support' CONFIG_SCSI_7000FASST n
-bool 'EATA ISA/EISA (DPT PM2011/021/012/022/122/322) support' CONFIG_SCSI_EATA n
-#bool 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG n
+bool 'Sun ESP Scsi support' CONFIG_SCSI_SUN_ESP n
+
fi
bool 'PPP (point-to-point) support' CONFIG_PPP n
bool 'PLIP (parallel port) support' CONFIG_PLIP n
bool 'Load balancing support (experimental)' CONFIG_SLAVE_BALANCING n
-bool 'Do you want to be offered ALPHA test drivers' CONFIG_NET_ALPHA n
-bool 'Western Digital/SMC cards' CONFIG_NET_VENDOR_SMC n
-if [ "$CONFIG_NET_VENDOR_SMC" = "y" ]; then
- bool 'WD80*3 support' CONFIG_WD80x3 n
- bool 'SMC Ultra support' CONFIG_ULTRA n
-fi
-bool 'AMD LANCE and PCnet (AT1500 and NE2100) support' CONFIG_LANCE n
-bool '3COM cards' CONFIG_NET_VENDOR_3COM y
-if [ "$CONFIG_NET_VENDOR_3COM" = "y" ]; then
- bool '3c501 support' CONFIG_EL1 n
- bool '3c503 support' CONFIG_EL2 n
- if [ "$CONFIG_NET_ALPHA" = "y" ]; then
- bool '3c505 support' CONFIG_ELPLUS n
- bool '3c507 support' CONFIG_EL16 n
- fi
- bool '3c509/3c579 support' CONFIG_EL3 y
-fi
-bool 'Other ISA cards' CONFIG_NET_ISA n
-if [ "$CONFIG_NET_ISA" = "y" ]; then
- bool 'Cabletron E21xx support' CONFIG_E2100 n
- bool 'DEPCA support' CONFIG_DEPCA n
- bool 'EtherWorks 3 support' CONFIG_EWRK3 n
- if [ "$CONFIG_NET_ALPHA" = "y" ]; then
-# bool 'Arcnet support' CONFIG_ARCNET n
- bool 'AT1700 support' CONFIG_AT1700 n
-# bool 'EtherExpressPro support' CONFIG_EEXPRESS_PRO n
- bool 'EtherExpress support' CONFIG_EEXPRESS n
- bool 'NI5210 support' CONFIG_NI52 n
- bool 'NI6510 support' CONFIG_NI65 n
- fi
- bool 'HP PCLAN+ (27247B and 27252A) support' CONFIG_HPLAN_PLUS n
- bool 'HP PCLAN (27245 and other 27xxx series) support' CONFIG_HPLAN n
- bool 'NE2000/NE1000 support' CONFIG_NE2000 y
- bool 'SK_G16 support' CONFIG_SK_G16 n
-fi
-bool 'EISA, VLB, PCI and on board controllers' CONFIG_NET_EISA n
-if [ "$CONFIG_NET_EISA" = "y" ]; then
- if [ "$CONFIG_NET_ALPHA" = "y" ]; then
- bool 'Ansel Communications EISA 3200 support' CONFIG_AC3200 n
- fi
- bool 'Apricot Xen-II on board ethernet' CONFIG_APRICOT n
-# bool 'DEC 21040 PCI support' CONFIG_DEC_ELCP n
-# bool 'LPL T100V 100Mbs support' CONFIG_LPL_T100 n
-# bool 'PCnet32 (32 bit VLB and PCI LANCE) support' CONFIG_PCNET32 n
- bool 'Zenith Z-Note support' CONFIG_ZNET y
+bool 'Sun LANCE Ethernet support' CONFIG_SUN_LANCE n
+bool 'Sun Intel Ethernet support' CONFIG_SUN_INTEL n
fi
-bool 'Pocket and portable adaptors' CONFIG_NET_POCKET n
-if [ "$CONFIG_NET_POCKET" = "y" ]; then
- bool 'AT-LAN-TEC/RealTek pocket adaptor support' CONFIG_ATP n
- bool 'D-Link DE600 pocket adaptor support' CONFIG_DE600 n
- bool 'D-Link DE620 pocket adaptor support' CONFIG_DE620 n
-# bool 'Silicom pocket adaptor support' CONFIG_SILICOM_PEA n
-# bool 'WaveLAN PCMCIA support' CONFIG_WaveLAN n
-# bool '3 Com 3c589 PCMCIA support' CONFIG_3C589 n
fi
-fi
-fi
-
-comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)'
-
-bool 'Sony CDU31A/CDU33A CDROM driver support' CONFIG_CDU31A n
-bool 'Mitsumi (not IDE/ATAPI) CDROM driver support' CONFIG_MCD n
-bool 'Matsushita/Panasonic CDROM driver support' CONFIG_SBPCD n
-if [ "$CONFIG_SBPCD" = "y" ]; then
- bool 'Matsushita/Panasonic second CDROM controller support' CONFIG_SBPCD2 n
- if [ "$CONFIG_SBPCD2" = "y" ]; then
- bool 'Matsushita/Panasonic third CDROM controller support' CONFIG_SBPCD3 n
- if [ "$CONFIG_SBPCD3" = "y" ]; then
- bool 'Matsushita/Panasonic fourth CDROM controller support' CONFIG_SBPCD4 n
- fi
- fi
-fi
-bool 'Aztech/Orchid/Okano/Wearnes (non IDE) CDROM support' CONFIG_AZTCD n
comment 'Filesystems'
-bool 'Standard (minix) fs support' CONFIG_MINIX_FS y
-bool 'Extended fs support' CONFIG_EXT_FS y
+bool 'Standard (minix) fs support' CONFIG_MINIX_FS n
+bool 'Extended fs support' CONFIG_EXT_FS n
bool 'Second extended fs support' CONFIG_EXT2_FS y
-bool 'xiafs filesystem support' CONFIG_XIA_FS y
-bool 'msdos fs support' CONFIG_MSDOS_FS y
-if [ "$CONFIG_MSDOS_FS" = "y" ]; then
-bool 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS n
-fi
+bool 'xiafs filesystem support' CONFIG_XIA_FS n
+bool 'msdos fs support' CONFIG_MSDOS_FS n
bool '/proc filesystem support' CONFIG_PROC_FS n
if [ "$CONFIG_INET" = "y" ]; then
bool 'NFS filesystem support' CONFIG_NFS_FS n
fi
if [ "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_CDU31A" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_BLK_DEV_IDECD" = "y" ]; then
- bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS y
+ bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS n
else
bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS n
fi
bool 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS n
-bool 'System V and Coherent filesystem support' CONFIG_SYSV_FS y
+bool 'System V and Coherent filesystem support' CONFIG_SYSV_FS n
comment 'character devices'
-bool 'Cyclades async mux support' CONFIG_CYCLADES n
-bool 'Parallel printer support' CONFIG_PRINTER n
-bool 'Logitech busmouse support' CONFIG_BUSMOUSE n
-bool 'PS/2 mouse (aka "auxiliary device") support' CONFIG_PSMOUSE n
-if [ "$CONFIG_PSMOUSE" = "y" ]; then
-bool 'C&T 82C710 mouse port support (as on TI Travelmate)' CONFIG_82C710_MOUSE y
-fi
-bool 'Microsoft busmouse support' CONFIG_MS_BUSMOUSE n
-bool 'ATIXL busmouse support' CONFIG_ATIXL_BUSMOUSE n
-
-
-bool 'QIC-02 tape support' CONFIG_QIC02_TAPE n
-if [ "$CONFIG_QIC02_TAPE" = "y" ]; then
-bool 'Do you want runtime configuration for QIC-02' CONFIG_QIC02_DYNCONF y
-if [ "$CONFIG_QIC02_DYNCONF" != "y" ]; then
-
-comment '>>> Edit configuration parameters in ./include/linux/tpqic02.h!'
-
-else
-
-comment '>>> Setting runtime QIC-02 configuration is done with qic02conf'
-comment '>>> Which is available from ftp://ftp.funet.fi/pub/OS/Linux/BETA/QIC-02/'
-
-fi
-fi
-
-bool 'QIC-117 tape support' CONFIG_FTAPE n
-if [ "$CONFIG_FTAPE" = "y" ]; then
-int ' number of ftape buffers' NR_FTAPE_BUFFERS 3
-fi
+bool 'Zilog serial support' CONFIG_SUN_ZS n
comment 'Sound'
-bool 'Sound card support' CONFIG_SOUND n
+bool 'Sun Audio support' CONFIG_SUN_AUDIO n
comment 'Kernel hacking'
-#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC n
+bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC n
bool 'Kernel profiling support' CONFIG_PROFILE n
if [ "$CONFIG_PROFILE" = "y" ]; then
int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
.S.o:
$(CC) -D__ASSEMBLY__ -ansi -c $< -o $*.o
-OBJS = entry.o traps.o irq.o process.o promops.o signal.o ioport.o setup.o \
- idprom.o probe.o
+OBJS = entry.o traps.o irq.o process.o signal.o ioport.o setup.o \
+ idprom.o probe.o mp.o c_mp.o
all: kernel.o head.o
head.o: head.s
-head.s: head.S $(TOPDIR)/include/asm-sparc/head.h
- $(CPP) -D__ASSEMBLY__ -ansi -o $*.s $<
+head.s: head.S
+ $(CPP) -D__KERNEL__ -D__ASSEMBLY__ -ansi -o $*.s $<
kernel.o: $(OBJS)
$(LD) -r -o kernel.o $(OBJS)
dep:
$(CPP) -M *.c > .depend
+ $(CPP) -M -D__ASSEMBLY__ -ansi *.S >>.depend
dummy:
--- /dev/null
+/* V9head.S: Initial boot code for the V9 Sparc under Linux.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ *
+ */
+
+#include <asm/cprefix.h>
+
+ .text
+
+ .globl start
+ .globl _start
+ .globl C_LABEL(trapbase)
+_start:
+start:
+C_LABEL(trapbase):
--- /dev/null
+/* mp.c: SMP cpu idling and dispatch on the Sparc.
+ *
+ * Copyright (C) 1995 David S. Miller
+ */
+
+#include <asm/mp.h>
+#include <asm/mbus.h>
+
+struct sparc_percpu *percpu_table;
+
+void
+sparc_cpu_init(void)
+{
+ /* We now have our per-cpu mappings ok, and we should
+ * be good to go.
+ */
+
+ /* Do cache crap here. */
+
+ /* CPU initted, idle the puppy. */
+
+ return;
+}
+
+extern thiscpus_mid;
+
+void
+sparc_cpu_idle(void)
+{
+ int cpuid;
+
+/* cpuid = get_cpuid(); */
+ cpuid = (thiscpus_mid&(~8));
+/* printk("SMP: cpu%d has entered idle loop", cpuid); */
+
+ /* Say that we exist and set up. */
+ percpu_table[cpuid].cpuid = cpuid;
+ percpu_table[cpuid].cpu_is_alive = 0x1;
+ percpu_table[cpuid].cpu_is_idling = 0x1;
+
+ /* Let other cpus catch up. */
+ while(linux_smp_still_initting) ;
+ printk("cpu%d done spinning\n", get_cpuid());
+ for(;;) ; /* Do something useful here... */
+
+ return;
+}
* the trap table and how it works, this will show you how we get
* to these routines.
*
- * Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
*/
+
#include <asm/head.h>
#include <asm/asi.h>
+#include <asm/contregs.h>
#include <asm/psr.h>
#include <asm/cprefix.h>
#include <asm/vaddrs.h>
+#include <asm/memreg.h>
+#include <asm/page.h>
+
+#define NR_SYSCALLS 255 /* Each OS is different... */
+
+/* A debugging macro, it just prints a dot on the screen. For now
+ * it is only used in the context switch code since it is so delicate
+ * I need to use the prom putchar() routines and reload the pointers
+ * every time. This clobbers %l7 and %o0.
+ */
+#define DO_SPARC_DOT \
+ sethi %hi(C_LABEL(romvec)), %l7; \
+ ld [%l7 + %lo(C_LABEL(romvec))], %l7; \
+ ld [%l7 + 0x54], %l7; \
+ or %g0, '.', %o0; \
+ call %l7; \
+ nop; \
+ nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; \
+
+/* Another macro, the name speaks for itself. */
+#define DROP_THE_BOMB \
+ sethi %hi(C_LABEL(romvec)), %l7; \
+ ld [%l7 + %lo(C_LABEL(romvec))], %l7; \
+ ld [%l7 + 0x74], %l7; \
+ call %l7; \
+ nop; \
+
+/* Turn off interrupts while we change contexts. Clobbers %l7 */
+#define TRAPS_OFF \
+ rd %psr, %l7; \
+ andn %l7, (PSR_ET), %l7; \
+ wr %l7, 0x0, %psr; \
/* Here are macros for routines we do often, this allows me to inline this
- * without making the code look real ugly. Well, the macro looks ugly too but
- * makes the trap entry code easier to understand.
+ * without making the code look real ugly. Well, the macro looks ugly too
+ * but makes the trap entry code easier to understand.
*/
/* I really don't like synthetic instructions. So I avoid them like the
* have to check that the pages of memory that I am going to throw the window(s)
* onto are valid and are writable by the user (this is %sp to %sp + 64) before
* I start dumping stuff there. We always assume that kernels stack is ok.
+ * XXX Will change on MP's XXX
*
* If we have to save a kernel window, only one branch is taken. This should
* make trap handlers quicker in this scenario.
*
* Once 'current' is loaded into %g6, it stays there until we leave
* this macro.
- *
- * XXX must do some checking on the assumption that kernel stack is always ok
*/
/* I will document how this works real soon. TODO */
sll %g5, %g7, %g5; \
wr %g5, 0x0, %wim; /* update %wim to 'now' invalid */ \
and %g7, 0x1f, %g7; \
+ sethi %hi( C_LABEL(current) ), %g6; \
+ ld [%g6 + %lo( C_LABEL(current) )], %g6; \
st %g7, [%g6 + THREAD_WIM]; /* save 'this' threads mask */ \
restore %g0, %g0, %g0; \
or %g0, %l5, %g5; /* restore the globals we used */ \
b 8f; /* we are done */ \
or %g0, %l7, %g7; \
2: sub %g7, 0x1, %g7; \
+ sethi %hi( C_LABEL(current) ), %g6; \
+ ld [%g6 + %lo( C_LABEL(current) )], %g6; \
st %g7, [%g6 + THREAD_UWINDOWS]; /* There are user windows if we */ \
andcc %sp, 0x7, %g0; /* get here. Check for stack alignment. */ \
bne 5f; /* Stack is unaligned, yuck. */ \
* upon entry to most traps and interrupts. This is save away the current window
* if it is the trap window, clean it, and adjust the stack for the handler c-code
* to work.
+ *
+ * See asm-sparc/cprefix.h to see how the CONCAT macros work.
+ */
+/* NOTE: The system call entry code ASSUMES that the ENTER_TRAP macro
+ * does NOT touch register %l7 and leaves the same contents after
+ * the macro is done. Keep in mind if you change this code.
*/
-#define ENTER_TRAP \
+#define ENTER_TRAP(label) \
rd %wim, %l4; \
or %g0, 0x1, %l5; \
sll %l5, %l0, %l5; \
- andcc %l0, 0x40, %g0; \
- bz 1f; \
+ andcc %l0, PSR_PS, %g0; \
+ bz CONCAT1(label, 1); \
andcc %l4, %l5, %g0; \
- bz,a 3f; \
+ bz,a CONCAT1(label, 3); \
sub %fp, 0xb0, %sp; \
TRAP_WIN_CLEAN \
- b 3f; \
+ b CONCAT1(label, 3); \
sub %fp, 0xb0, %sp; \
-1: sethi %hi( C_LABEL(current) ), %l6; \
+CONCAT1(label, 1): \
+ sethi %hi( C_LABEL(current) ), %l6; \
ld [%l6 + %lo( C_LABEL(current) )], %l6; \
ld [%l6 + THREAD_WIM], %l5; \
and %l0, 0x1f, %l4; \
cmp %l5, %l3; \
- ble,a 4f; \
+ ble,a CONCAT1(label, 4); \
sethi %hi( C_LABEL(nwindowsm1) ), %l4; \
sub %l5, %l3, %l3; \
- b 5f; \
+ b CONCAT1(label, 5); \
sub %l3, 0x1, %l5; \
-4: ld [%l4 + %lo( C_LABEL(nwindowsm1) )], %l4; \
+CONCAT1(label, 4): \
+ ld [%l4 + %lo( C_LABEL(nwindowsm1) )], %l4; \
sub %l4, %l3, %l4; \
add %l5, %l4, %l5; \
-5: st %l5, [%l6 + THREAD_UWINDOWS]; \
- bz,a 2f; \
- sethi %hi(TASK_SIZE-176), %l5; \
- TRAP_WIN_CLEAN; \
- sethi %hi( C_LABEL(current) ), %l6; \
- ld [%l6 + %lo( C_LABEL(current) )], %l6; \
- sethi %hi(TASK_SIZE-176), %l5; \
-2: or %l5, %lo(TASK_SIZE-176), %l5; \
- add %l6, %l5, %sp; \
-3: \
+CONCAT1(label, 5): \
+ st %l5, [%l6 + THREAD_UWINDOWS]; \
+ bz,a CONCAT1(label, 2); \
+ sethi %hi(C_LABEL(current)), %l6; \
+ TRAP_WIN_CLEAN \
+ sethi %hi(C_LABEL(current)), %l6; \
+CONCAT1(label, 2): \
+ ld [%l6 + %lo(C_LABEL(current))], %l6; \
+ ld [%l6 + TASK_KSTACK_PG], %l6; \
+ add %l6, (PAGE_SIZE - 96 - 80), %l6; \
+ andn %l6, 0x7, %l6; \
+ or %g0, %l6, %sp; \
+CONCAT1(label, 3):
-#define ENTER_IRQ \
+/* What needs to be executed upon entry to an interrupt.
+ *
+ * See asm-sparc/cprefix.h to see how the CONCAT macros work.
+ */
+
+#define ENTER_IRQ(label) \
rd %wim, %l4; \
or %g0, 0x1, %l5; \
sll %l5, %l0, %l5; \
- andcc %l0, 0x40, %g0; \
- bz 1f; \
+ andcc %l0, PSR_PS, %g0; \
+ bz CONCAT1(label, 1); \
andcc %l4, %l5, %g0; \
+ bz,a CONCAT1(label, 0); \
sethi %hi( C_LABEL(eintstack) ), %l7; \
- or %l7, %lo( C_LABEL(eintstack) ), %l7; \
- bz 0f; \
- nop; \
TRAP_WIN_CLEAN \
sethi %hi( C_LABEL(eintstack) ), %l7; \
- or %l7, %lo( C_LABEL(eintstack) ), %l7; \
-0: subcc %fp, %l7, %g0; \
- bg,a 3f; \
+CONCAT1(label, 0): \
+ subcc %fp, %l7, %g0; \
+ bge,a CONCAT1(label, 3); \
sub %l7, 0xb0, %sp; \
- b 3f; \
+ b CONCAT1(label, 3); \
sub %fp, 0xb0, %sp; \
-1: sethi %hi( C_LABEL(current) ), %l6; \
+CONCAT1(label, 1): \
+ sethi %hi( C_LABEL(current) ), %l6; \
ld [%l6 + %lo( C_LABEL(current) )], %l6; \
ld [%l6 + THREAD_WIM], %l5; \
and %l0, 0x1f, %l7; \
cmp %l5, %l7; \
- ble,a 4f; \
+ ble,a CONCAT1(label, 4); \
sethi %hi( C_LABEL(nwindowsm1) ), %l4; \
sub %l5, %l7, %l7; \
- b 5f; \
+ b CONCAT1(label, 5); \
sub %l7, 0x1, %l5; \
-4: ld [%l4 + %lo( C_LABEL(nwindowsm1) )], %l4; \
+CONCAT1(label, 4): \
+ ld [%l4 + %lo( C_LABEL(nwindowsm1) )], %l4; \
sub %l4, %l7, %l4; \
add %l5, %l4, %l5; \
-5: st %l5, [%l6 + THREAD_UWINDOWS]; \
- bz,a 2f; \
+CONCAT1(label, 5): \
+ st %l5, [%l6 + THREAD_UWINDOWS]; \
+ bz,a CONCAT1(label, 2); \
sethi %hi( C_LABEL(eintstack) ), %l7; \
TRAP_WIN_CLEAN \
sethi %hi( C_LABEL(eintstack) ), %l7; \
-2: \
+CONCAT1(label, 2): \
sub %l7, 0xb0, %sp; \
-3:
+CONCAT1(label, 3):
.text
.align 4
-/* Default trap handler */
- .globl my_trap_handler
-my_trap_handler:
-#if 1
- jmp %l1
- rett %l2
- nop
-#else
- rd %wim, %l4
- or %g0, 0x1, %l5
- sll %l5, %l0, %l5
- cmp %l4, %l5 ! are we in the invalid window?
-
- TRAP_WIN_CLEAN
-
- nop
- or %g0, %l3, %o0
- call C_LABEL(do_hw_interrupt)
- or %g0, %g0, %o1
- wr %l0, 0x20, %psr ! re-enable traps and reset the condition codes
- nop
- nop
- nop ! click our heels three times, "no place like home"
- jmp %l1
- rett %l2
-#endif /* bogon */
+/* Bad trap handler */
+ .globl bad_trap_handler
+bad_trap_handler:
+ ENTER_TRAP(bad_trap_handler)
+
+ or %g0, %l3, %o0
+ or %g0, %l0, %o1
+ call C_LABEL(do_hw_interrupt)
+ or %g0, %l1, %o2
+
+ jmp %l1
+ rett % l2
.align 4
.globl sparc_timer
sparc_timer:
- sethi %hi(TIMER_VADDR), %l4
- or %l4, %lo(TIMER_VADDR), %l4 ! read the limit register
- ld [%l4 + 0xc], %l4 ! to clear the interrupt
- rd %wim, %l4
- or %g0, 0x1, %l5
- sll %l5, %l0, %l5
- andcc %l0, 0x40, %g0
- bz st1
- sethi %hi( C_LABEL(eintstack) ), %l7
- andcc %l4, %l5, %g0
- bz st0
- or %l7, %lo( C_LABEL(eintstack) ), %l7
- TRAP_WIN_CLEAN
- sethi %hi( C_LABEL(eintstack) ), %l7
- or %l7, %lo( C_LABEL(eintstack) ), %l7
-st0: subcc %fp, %l7, %g0
- bg,a st3
- sub %l7, 0xb0, %sp
- b st3
- sub %fp, 0xb0, %sp
-st1: sethi %hi( C_LABEL(current) ), %l6
- ld [%l6 + %lo( C_LABEL(current) )], %l6
- ld [%l6 + THREAD_WIM], %l5
- and %l0, 0x1f, %l7
- cmp %l5, %l7
- ble,a st4
- sethi %hi( C_LABEL(nwindowsm1) ), %l4
- sub %l5, %l7, %l7
- b st5
- sub %l7, 0x1, %l5
-st4: ld [%l4 + %lo( C_LABEL(nwindowsm1) )], %l4
- sub %l4, %l7, %l4
- add %l5, %l4, %l5
-st5: st %l5, [%l6 + THREAD_UWINDOWS]
- sethi %hi( C_LABEL(eintstack) ), %l7
- bz,a st2
- or %l7, %lo( C_LABEL(eintstack) ), %l7
- TRAP_WIN_CLEAN
- sethi %hi( C_LABEL(eintstack) ), %l7
- or %l7, %lo( C_LABEL(eintstack) ), %l7
-st2: sub %l7, 0xb0, %sp
-
-st3: std %g2, [%sp + 96 + 24]
+ ENTER_IRQ(sparc_timer)
+
+ sethi %hi(C_LABEL(master_l10_limit)), %l4
+ ld [%l4 + %lo(C_LABEL(master_l10_limit))], %l4
+ ld [%l4], %g0 ! read the limit register
+
+ std %g2, [%sp + C_STACK + PT_G2]
or %g0, %g1, %l7
rd %y, %l6
- std %g4, [%sp + 96 + 32]
+ std %g4, [%sp + C_STACK + PT_G4]
andn %l0, PSR_PIL, %l4
- sll %l3, 0x8, %l5
- std %g6, [%sp + 96 + 40]
- or %l5, %l4, %l4
+/* sll %l3, 0x8, %l5 */
+ std %g6, [%sp + C_STACK + PT_G6]
+/* or %l5, %l4, %l4 */
- wr %l4, 0x0, %psr
+ /* Magic, we can't increase PIL and set ET at the same
+ * time or the chip calls prom_panic().
+ */
+/* wr %l4, 0x0, %psr */
wr %l4, PSR_ET, %psr
- std %l0, [%sp + 96 + 0]
- std %l2, [%sp + 96 + 8]
- st %fp, [%sp + 96 + 16]
-
- or %g0, 14, %o0
- or %g0, %g0, %o1
- call C_LABEL(do_sparc_timer)
+ or %g0, 10, %o0
+ add %sp, C_STACK, %o1
+ call C_LABEL(do_IRQ)
nop
or %g0, %l7, %g1
wr %l6, 0x0, %y
- ldd [%sp + 96 + 24], %g2
- ldd [%sp + 96 + 32], %g4
- ldd [%sp + 96 + 40], %g6
- wr %l0, 0x0, %psr
- nop
- nop
- nop
-
- and %l0, 31, %l5
- sethi %hi(lnx_winmask), %l6
- or %l6, %lo(lnx_winmask), %l6
- ldub [%l6 + %l5], %l5
- andcc %l0, PSR_PS, %g0
- bnz 1f
- rd %wim, %l4
+ ldd [%sp + C_STACK + PT_G2], %g2
+ ldd [%sp + C_STACK + PT_G4], %g4
+ ldd [%sp + C_STACK + PT_G6], %g6
-1: andcc %l5, %l4, %g0
- bnz 2f
+ b ret_trap_entry
wr %l0, 0x0, %psr
- nop
- nop
- nop
-
- jmp %l1
- rett %l2
-2: wr %g0, 0x0, %wim
- nop
- nop
- nop
-
- restore
- restore %g0, 0x1, %l1
- rd %psr, %l0
- and %l0, 31, %l0
- sll %l1, %l0, %l1
- wr %l1, 0x0, %wim
- sethi %hi( C_LABEL(current) ), %l1
- ld [%l1 + %lo( C_LABEL(current) ) ], %l1
- st %l0, [%l1 + THREAD_WIM]
- save %g0, %g0, %g0
-
- ldd [%sp], %l0
- ldd [%sp + 0x8], %l2
- ldd [%sp + 0x10], %l4
- ldd [%sp + 0x18], %l6
- ldd [%sp + 0x20], %i0
- ldd [%sp + 0x28], %i2
- ldd [%sp + 0x30], %i4
- ldd [%sp + 0x38], %i6
-
- save %g0, %g0, %g0
-
- jmp %l1
- rett %l2
-
-
-/* For now all IRQ's not registered get sent here so I can see
- * what is poking the chip.
+/* For now all IRQ's not registered get sent here. handler_irq() will
+ * see if a routine is registered to handle this interrupt and if not
+ * it will say so on the console.
*/
.align 4
- .globl stray_irq_entry
-stray_irq_entry:
- rd %wim, %l4
- or %g0, 0x1, %l5
- sll %l5, %l0, %l5
- andcc %l0, 0x40, %g0
- bz tt1
- sethi %hi( C_LABEL(eintstack) ), %l7
- andcc %l4, %l5, %g0
- bz tt0
- or %l7, %lo( C_LABEL(eintstack) ), %l7
- TRAP_WIN_CLEAN
- sethi %hi( C_LABEL(eintstack) ), %l7
- or %l7, %lo( C_LABEL(eintstack) ), %l7
-tt0: subcc %fp, %l7, %g0
- bg,a tt3
- sub %l7, 0xb0, %sp
- b tt3
- sub %fp, 0xb0, %sp
-tt1: sethi %hi( C_LABEL(current) ), %l6
- ld [%l6 + %lo( C_LABEL(current) )], %l6
- ld [%l6 + THREAD_WIM], %l5
- and %l0, 0x1f, %l7
- cmp %l5, %l7
- ble,a tt4
- sethi %hi( C_LABEL(nwindowsm1) ), %l4
- sub %l5, %l7, %l7
- b tt5
- sub %l7, 0x1, %l5
-tt4: ld [%l4 + %lo( C_LABEL(nwindowsm1) )], %l4
- sub %l4, %l7, %l4
- add %l5, %l4, %l5
-tt5: st %l5, [%l6 + THREAD_UWINDOWS]
- sethi %hi( C_LABEL(eintstack) ), %l7
- bz,a tt2
- or %l7, %lo( C_LABEL(eintstack) ), %l7
- TRAP_WIN_CLEAN
- sethi %hi( C_LABEL(eintstack) ), %l7
- or %l7, %lo( C_LABEL(eintstack) ), %l7
-tt2: sub %l7, 0xb0, %sp
-
-tt3: std %g2, [%sp + 96 + 24]
+ .globl real_irq_entry
+real_irq_entry:
+ ENTER_IRQ(real_irq_entry)
+ std %g2, [%sp + C_STACK + PT_G2]
or %g0, %g1, %l7
rd %y, %l6
- std %g4, [%sp + 96 + 32]
+ std %g4, [%sp + C_STACK + PT_G4]
andn %l0, PSR_PIL, %l4
sll %l3, 0x8, %l5
- std %g6, [%sp + 96 + 40]
+ std %g6, [%sp + C_STACK + PT_G6]
or %l5, %l4, %l4
wr %l4, 0x0, %psr
wr %l4, PSR_ET, %psr
- std %l0, [%sp + 96 + 0]
- std %l2, [%sp + 96 + 8]
- st %fp, [%sp + 96 + 16]
+ std %l0, [%sp + C_STACK + PT_PSR]
+ std %l2, [%sp + C_STACK + PT_NPC]
or %g0, %l3, %o0
- or %g0, %g0, %o1
- call C_LABEL(unexpected_irq)
+ add %sp, C_STACK, %o1
+ call C_LABEL(handler_irq)
nop
or %g0, %l7, %g1
wr %l6, 0x0, %y
- ldd [%sp + 96 + 24], %g2
- ldd [%sp + 96 + 32], %g4
- ldd [%sp + 96 + 40], %g6
- wr %l0, 0x0, %psr
- nop
- nop
- nop
-
- and %l0, 31, %l5
- sethi %hi(lnx_winmask), %l6
- or %l6, %lo(lnx_winmask), %l6
- ldub [%l6 + %l5], %l5
- andcc %l0, PSR_PS, %g0
- bnz 1f
- rd %wim, %l4
-
-1: andcc %l5, %l4, %g0
- bnz 2f
+ ldd [%sp + C_STACK + PT_G2], %g2
+ ldd [%sp + C_STACK + PT_G4], %g4
+ ldd [%sp + C_STACK + PT_G6], %g6
+ b ret_trap_entry
wr %l0, 0x0, %psr
- nop
- nop
- nop
-
- jmp %l1
- rett %l2
-
-2: wr %g0, 0x0, %wim
- nop
- nop
- nop
-
- restore
- restore %g0, 0x1, %l1
- rd %psr, %l0
- and %l0, 31, %l0
- sll %l1, %l0, %l1
- wr %l1, 0x0, %wim
- sethi %hi( C_LABEL(current) ), %l1
- ld [%l1 + %lo( C_LABEL(current) ) ], %l1
- st %l0, [%l1 + THREAD_WIM]
- save %g0, %g0, %g0
-
- ldd [%sp], %l0
- ldd [%sp + 0x8], %l2
- ldd [%sp + 0x10], %l4
- ldd [%sp + 0x18], %l6
- ldd [%sp + 0x20], %i0
- ldd [%sp + 0x28], %i2
- ldd [%sp + 0x30], %i4
- ldd [%sp + 0x38], %i6
-
- save %g0, %g0, %g0
-
- jmp %l1
- rett %l2
-
/* This routine is optimized for kernel window fills. User fills take about two
*/
/* Don't use local labels, or if you do be REAL CAREFUL. TRAP_WIN_CLEAN is
- * full of them! If you think this routine is hairy, window spills are worse,
+ * full of them! If you think this routine is hairy, window fills are worse,
* see below.
*/
.align 4
.globl spill_window_entry
spill_window_entry:
- andcc %l0, 0x40, %g0 ! see if this is a user window fill
+ andcc %l0, PSR_PS, %g0 ! see if this is a user window fill
bz,a spill_from_user
nop
sub %l5, %l3, %l3
b 2f
sub %l3, 0x1, %l5
-1: ld [%l4 + %lo( C_LABEL(nwindowsm1) )], %l4
+1:
+ ld [%l4 + %lo( C_LABEL(nwindowsm1) )], %l4
sub %l4, %l3, %l4
add %l5, %l4, %l5
-2: st %l5, [%l6 + THREAD_UWINDOWS]
+2:
+ st %l5, [%l6 + THREAD_UWINDOWS]
TRAP_WIN_CLEAN /* danger, danger... */
sethi %hi( C_LABEL(current) ), %l6
cmp %l7, -1
bne fill_bad_stack
andn %sp, 0xfff, %l7
-1: lda [%l7] ASI_PTE, %l7
+1:
+ lda [%l7] ASI_PTE, %l7
srl %l7, 0x1d, %l7
andn %l7, 0x2, %l7
cmp %l7, 0x4
cmp %l7, -1
bne fill_bad_stack
andn %sp, 0xfff, %l7
-1: lda [%l7] ASI_PTE, %l7
+1:
+ lda [%l7] ASI_PTE, %l7
srl %l7, 0x1d, %l7
andn %l7, 0x2, %l7
cmp %l7, 0x4
sll %l4, %l5, %l4
wr %l4, 0x0, %wim
ld [%l6 + THREAD_KSP], %sp ! set to kernel stack pointer
- wr %l0, 0x20, %psr ! turn off traps
- std %l0, [%sp + C_STACK] ! set up thread_frame on stack
+ wr %l0, PSR_ET, %psr ! turn off traps
+ std %l0, [%sp + C_STACK] ! set up thread_frame XXX
rd %y, %l3
std %l2, [%sp + C_STACK + 0x8]
or %g0, 0x6, %o0 ! so _sparc_trap knows what to do
std %i0, [%sp + C_STACK + 0x30]
std %i2, [%sp + C_STACK + 0x38]
std %i4, [%sp + C_STACK + 0x40]
- call sparc_trap
+ nop /* SHould trap here */
std %i6, [%sp + C_STACK + 0x48]
ldd [%sp + C_STACK], %l0
cmp %l7, 0x0
bl,a 1f
wr %g0, 0x0, %wim
- b,a leave_trap
+ /* Should trap here */
-1: or %g0, %g6, %l3
+1:
+ or %g0, %g6, %l3
or %g0, %l6, %g6
st %g0, [%g6 + THREAD_W_SAVED]
restore %g0, %g0, %g0
jmp %l1
rett %l2
+ /* This routine handles illegal isntructions and privileged
+ * instruction attempts from user code.
+ */
.align 4
- .globl trap_entry
-trap_entry:
- TRAP_WIN_CLEAN
- jmp %l1
- rett %l2
+ .globl bad_instruction
+bad_instruction:
+ ENTER_TRAP(bad_instruction)
+ st %g1, [%sp + C_STACK + PT_G1]
+ rd %y, %l4
+ std %g2, [%sp + C_STACK + PT_G2]
+ std %g4, [%sp + C_STACK + PT_G4]
+ std %g6, [%sp + C_STACK + PT_G6]
+ wr %l0, PSR_ET, %psr ! re-enable traps
+ add %sp, C_STACK, %o0
+ mov %l1, %o1
+ mov %l2, %o2
+ cmp %l3, 0x2
+ bne 1f
+ call C_LABEL(do_illegal_instruction)
+ mov %l0, %o3
+ b 2f
+ ld [%sp + C_STACK + PT_G1], %g1
+
+1:
+ call C_LABEL(do_priv_instruction)
+ mov %l0, %o3
+ ld [%sp + C_STACK + PT_G1], %g1
+2:
+ wr %l0, 0x0, %psr
+ ldd [%sp + C_STACK + PT_G2], %g2
+ ldd [%sp + C_STACK + PT_G4], %g4
+ ldd [%sp + C_STACK + PT_G6], %g6
+ b ret_trap_entry
+ wr %l4, 0, %y
+
+ /* This routine handles unaligned data accesses.
+ */
+ .align 4
+ .globl mna_handler
+mna_handler:
+ ENTER_TRAP(mna_handler)
+ st %g1, [%sp + C_STACK + PT_G1]
+ rd %y, %l4
+ std %g2, [%sp + C_STACK + PT_G2]
+ std %g4, [%sp + C_STACK + PT_G4]
+ std %g6, [%sp + C_STACK + PT_G6]
+ wr %l0, PSR_ET, %psr ! re-enable traps
+ add %sp, C_STACK, %o0
+ mov %l1, %o1
+ mov %l2, %o2
+ call C_LABEL(do_memaccess_unaligned)
+ mov %l0, %o3
+
+ ld [%sp + C_STACK + PT_G1], %g1
+ wr %l0, 0x0, %psr
+ ldd [%sp + C_STACK + PT_G2], %g2
+ ldd [%sp + C_STACK + PT_G4], %g4
+ ldd [%sp + C_STACK + PT_G6], %g6
+ b ret_trap_entry
+ wr %l4, 0, %y
+
+ /* This routine handles floating point disabled traps.
+ */
+ .align 4
+ .globl fpd_trap_handler
+fpd_trap_handler:
+ ENTER_TRAP(fpd_trap_handler)
+ st %g1, [%sp + C_STACK + PT_G1]
+ rd %y, %l4
+ std %g2, [%sp + C_STACK + PT_G2]
+ std %g4, [%sp + C_STACK + PT_G4]
+ std %g6, [%sp + C_STACK + PT_G6]
+ wr %l0, PSR_ET, %psr ! re-enable traps
+ add %sp, C_STACK, %o0
+ mov %l1, %o1
+ mov %l2, %o2
+ call C_LABEL(do_fpd_trap)
+ mov %l0, %o3
+
+ ld [%sp + C_STACK + PT_G1], %g1
+ wr %l0, 0x0, %psr
+ ldd [%sp + C_STACK + PT_G2], %g2
+ ldd [%sp + C_STACK + PT_G4], %g4
+ ldd [%sp + C_STACK + PT_G6], %g6
+ b ret_trap_entry
+ wr %l4, 0, %y
+
+ /* This routine handles Floating Point Exceptions.
+ */
+ .align 4
+ .globl fpe_trap_handler
+fpe_trap_handler:
+ ENTER_TRAP(fpe_trap_handler)
+ st %g1, [%sp + C_STACK + PT_G1]
+ rd %y, %l4
+ std %g2, [%sp + C_STACK + PT_G2]
+ std %g4, [%sp + C_STACK + PT_G4]
+ std %g6, [%sp + C_STACK + PT_G6]
+ wr %l0, PSR_ET, %psr ! re-enable traps
+ add %sp, C_STACK, %o0
+ mov %l1, %o1
+ mov %l2, %o2
+ call C_LABEL(do_fpe_trap)
+ mov %l0, %o3
+
+ ld [%sp + C_STACK + PT_G1], %g1
+ wr %l0, 0x0, %psr
+ ldd [%sp + C_STACK + PT_G2], %g2
+ ldd [%sp + C_STACK + PT_G4], %g4
+ ldd [%sp + C_STACK + PT_G6], %g6
+ b ret_trap_entry
+ wr %l4, 0, %y
+
+ /* This routine handles Tag Overflow Exceptions.
+ */
+ .align 4
+ .globl do_tag_overflow
+do_tag_overflow:
+ ENTER_TRAP(do_tag_overflow)
+ st %g1, [%sp + C_STACK + PT_G1]
+ rd %y, %l4
+ std %g2, [%sp + C_STACK + PT_G2]
+ std %g4, [%sp + C_STACK + PT_G4]
+ std %g6, [%sp + C_STACK + PT_G6]
+ wr %l0, PSR_ET, %psr ! re-enable traps
+ add %sp, C_STACK, %o0
+ mov %l1, %o1
+ mov %l2, %o2
+ call C_LABEL(handle_tag_overflow)
+ mov %l0, %o3
+
+ ld [%sp + C_STACK + PT_G1], %g1
+ wr %l0, 0x0, %psr
+ ldd [%sp + C_STACK + PT_G2], %g2
+ ldd [%sp + C_STACK + PT_G4], %g4
+ ldd [%sp + C_STACK + PT_G6], %g6
+ b ret_trap_entry
+ wr %l4, 0, %y
+
+ /* This routine handles Watchpoint Exceptions.
+ */
+ .align 4
+ .globl do_watchpoint
+do_watchpoint:
+ ENTER_TRAP(do_watchpoint)
+ st %g1, [%sp + C_STACK + PT_G1]
+ rd %y, %l4
+ std %g2, [%sp + C_STACK + PT_G2]
+ std %g4, [%sp + C_STACK + PT_G4]
+ std %g6, [%sp + C_STACK + PT_G6]
+ wr %l0, PSR_ET, %psr ! re-enable traps
+ add %sp, C_STACK, %o0
+ mov %l1, %o1
+ mov %l2, %o2
+ call C_LABEL(handle_watchpoint)
+ mov %l0, %o3
+
+ ld [%sp + C_STACK + PT_G1], %g1
+ wr %l0, 0x0, %psr
+ ldd [%sp + C_STACK + PT_G2], %g2
+ ldd [%sp + C_STACK + PT_G4], %g4
+ ldd [%sp + C_STACK + PT_G6], %g6
+ b ret_trap_entry
+ wr %l4, 0, %y
+
+ /* This routine handles Register Access Exceptions.
+ */
+ .align 4
+ .globl do_reg_access
+do_reg_access:
+ ENTER_TRAP(do_reg_access)
+ st %g1, [%sp + C_STACK + PT_G1]
+ rd %y, %l4
+ std %g2, [%sp + C_STACK + PT_G2]
+ std %g4, [%sp + C_STACK + PT_G4]
+ std %g6, [%sp + C_STACK + PT_G6]
+ wr %l0, PSR_ET, %psr ! re-enable traps
+ add %sp, C_STACK, %o0
+ mov %l1, %o1
+ mov %l2, %o2
+ call C_LABEL(handle_reg_access)
+ mov %l0, %o3
+
+ ld [%sp + C_STACK + PT_G1], %g1
+ wr %l0, 0x0, %psr
+ ldd [%sp + C_STACK + PT_G2], %g2
+ ldd [%sp + C_STACK + PT_G4], %g4
+ ldd [%sp + C_STACK + PT_G6], %g6
+ b ret_trap_entry
+ wr %l4, 0, %y
+
+ /* This routine handles Instruction Access Errors.
+ */
+ .align 4
+ .globl do_iacc_error
+do_iacc_error:
+ ENTER_TRAP(do_iacc_error)
+ st %g1, [%sp + C_STACK + PT_G1]
+ rd %y, %l4
+ std %g2, [%sp + C_STACK + PT_G2]
+ std %g4, [%sp + C_STACK + PT_G4]
+ std %g6, [%sp + C_STACK + PT_G6]
+ wr %l0, PSR_ET, %psr ! re-enable traps
+ add %sp, C_STACK, %o0
+ mov %l1, %o1
+ mov %l2, %o2
+ call C_LABEL(handle_iacc_error)
+ mov %l0, %o3
+
+ ld [%sp + C_STACK + PT_G1], %g1
+ wr %l0, 0x0, %psr
+ ldd [%sp + C_STACK + PT_G2], %g2
+ ldd [%sp + C_STACK + PT_G4], %g4
+ ldd [%sp + C_STACK + PT_G6], %g6
+ b ret_trap_entry
+ wr %l4, 0, %y
+
+ /* This routine handles Co-Processor Disabled Exceptions.
+ */
+ .align 4
+ .globl do_cp_disabled
+do_cp_disabled:
+ ENTER_TRAP(do_cp_disabled)
+ st %g1, [%sp + C_STACK + PT_G1]
+ rd %y, %l4
+ std %g2, [%sp + C_STACK + PT_G2]
+ std %g4, [%sp + C_STACK + PT_G4]
+ std %g6, [%sp + C_STACK + PT_G6]
+ wr %l0, PSR_ET, %psr ! re-enable traps
+ add %sp, C_STACK, %o0
+ mov %l1, %o1
+ mov %l2, %o2
+ call C_LABEL(handle_cp_disabled)
+ mov %l0, %o3
+
+ ld [%sp + C_STACK + PT_G1], %g1
+ wr %l0, 0x0, %psr
+ ldd [%sp + C_STACK + PT_G2], %g2
+ ldd [%sp + C_STACK + PT_G4], %g4
+ ldd [%sp + C_STACK + PT_G6], %g6
+ b ret_trap_entry
+ wr %l4, 0, %y
+
+ /* This routine handles Unimplemented FLUSH Exceptions.
+ */
+ .align 4
+ .globl do_bad_flush
+do_bad_flush:
+ ENTER_TRAP(do_bad_flush)
+ st %g1, [%sp + C_STACK + PT_G1]
+ rd %y, %l4
+ std %g2, [%sp + C_STACK + PT_G2]
+ std %g4, [%sp + C_STACK + PT_G4]
+ std %g6, [%sp + C_STACK + PT_G6]
+ wr %l0, PSR_ET, %psr ! re-enable traps
+ add %sp, C_STACK, %o0
+ mov %l1, %o1
+ mov %l2, %o2
+ call C_LABEL(handle_bad_flush)
+ mov %l0, %o3
+
+ ld [%sp + C_STACK + PT_G1], %g1
+ wr %l0, 0x0, %psr
+ ldd [%sp + C_STACK + PT_G2], %g2
+ ldd [%sp + C_STACK + PT_G4], %g4
+ ldd [%sp + C_STACK + PT_G6], %g6
+ b ret_trap_entry
+ wr %l4, 0, %y
+
+ /* This routine handles Co-Processor Exceptions.
+ */
+ .align 4
+ .globl do_cp_exception
+do_cp_exception:
+ ENTER_TRAP(do_cp_exception)
+ st %g1, [%sp + C_STACK + PT_G1]
+ rd %y, %l4
+ std %g2, [%sp + C_STACK + PT_G2]
+ std %g4, [%sp + C_STACK + PT_G4]
+ std %g6, [%sp + C_STACK + PT_G6]
+ wr %l0, PSR_ET, %psr ! re-enable traps
+ add %sp, C_STACK, %o0
+ mov %l1, %o1
+ mov %l2, %o2
+ call C_LABEL(handle_cp_exception)
+ mov %l0, %o3
+
+ ld [%sp + C_STACK + PT_G1], %g1
+ wr %l0, 0x0, %psr
+ ldd [%sp + C_STACK + PT_G2], %g2
+ ldd [%sp + C_STACK + PT_G4], %g4
+ ldd [%sp + C_STACK + PT_G6], %g6
+ b ret_trap_entry
+ wr %l4, 0, %y
+
+ /* This routine handles Data Access Errors.
+ */
+ .align 4
+ .globl do_dacc_error
+do_dacc_error:
+ ENTER_TRAP(do_dacc_error)
+ st %g1, [%sp + C_STACK + PT_G1]
+ rd %y, %l4
+ std %g2, [%sp + C_STACK + PT_G2]
+ std %g4, [%sp + C_STACK + PT_G4]
+ std %g6, [%sp + C_STACK + PT_G6]
+ wr %l0, PSR_ET, %psr ! re-enable traps
+ add %sp, C_STACK, %o0
+ mov %l1, %o1
+ mov %l2, %o2
+ call C_LABEL(handle_dacc_error)
+ mov %l0, %o3
+
+ ld [%sp + C_STACK + PT_G1], %g1
+ wr %l0, 0x0, %psr
+ ldd [%sp + C_STACK + PT_G2], %g2
+ ldd [%sp + C_STACK + PT_G4], %g4
+ ldd [%sp + C_STACK + PT_G6], %g6
+ b ret_trap_entry
+ wr %l4, 0, %y
+
+ /* This routine handles Hardware Divide By Zero Exceptions.
+ */
+ .align 4
+ .globl do_hw_divzero
+do_hw_divzero:
+ ENTER_TRAP(do_hw_divzero)
+ st %g1, [%sp + C_STACK + PT_G1]
+ rd %y, %l4
+ std %g2, [%sp + C_STACK + PT_G2]
+ std %g4, [%sp + C_STACK + PT_G4]
+ std %g6, [%sp + C_STACK + PT_G6]
+ wr %l0, PSR_ET, %psr ! re-enable traps
+ add %sp, C_STACK, %o0
+ mov %l1, %o1
+ mov %l2, %o2
+ call C_LABEL(handle_hw_divzero)
+ mov %l0, %o3
+
+ ld [%sp + C_STACK + PT_G1], %g1
+ wr %l0, 0x0, %psr
+ ldd [%sp + C_STACK + PT_G2], %g2
+ ldd [%sp + C_STACK + PT_G4], %g4
+ ldd [%sp + C_STACK + PT_G6], %g6
+ b ret_trap_entry
+ wr %l4, 0, %y
+
+ /* This routine handles Data Store Errors.
+ */
+ .align 4
+ .globl do_dstore_err
+do_dstore_err:
+ ENTER_TRAP(do_dstore_err)
+ st %g1, [%sp + C_STACK + PT_G1]
+ rd %y, %l4
+ std %g2, [%sp + C_STACK + PT_G2]
+ std %g4, [%sp + C_STACK + PT_G4]
+ std %g6, [%sp + C_STACK + PT_G6]
+ wr %l0, PSR_ET, %psr ! re-enable traps
+ add %sp, C_STACK, %o0
+ mov %l1, %o1
+ mov %l2, %o2
+ call C_LABEL(handle_dstore_error)
+ mov %l0, %o3
+
+ ld [%sp + C_STACK + PT_G1], %g1
+ wr %l0, 0x0, %psr
+ ldd [%sp + C_STACK + PT_G2], %g2
+ ldd [%sp + C_STACK + PT_G4], %g4
+ ldd [%sp + C_STACK + PT_G6], %g6
+ b ret_trap_entry
+ wr %l4, 0, %y
+
+ /* This routine handles Data Access MMU-Miss Exceptions.
+ */
+ .align 4
+ .globl do_dacc_mmu_miss
+do_dacc_mmu_miss:
+ ENTER_TRAP(do_dacc_mmu_miss)
+ st %g1, [%sp + C_STACK + PT_G1]
+ rd %y, %l4
+ std %g2, [%sp + C_STACK + PT_G2]
+ std %g4, [%sp + C_STACK + PT_G4]
+ std %g6, [%sp + C_STACK + PT_G6]
+ wr %l0, PSR_ET, %psr ! re-enable traps
+ add %sp, C_STACK, %o0
+ mov %l1, %o1
+ mov %l2, %o2
+ call C_LABEL(handle_dacc_mmu_miss)
+ mov %l0, %o3
+
+ ld [%sp + C_STACK + PT_G1], %g1
+ wr %l0, 0x0, %psr
+ ldd [%sp + C_STACK + PT_G2], %g2
+ ldd [%sp + C_STACK + PT_G4], %g4
+ ldd [%sp + C_STACK + PT_G6], %g6
+ b ret_trap_entry
+ wr %l4, 0, %y
+
+ /* This routine handles Instruction Access MMU-Miss Exceptions.
+ */
+ .align 4
+ .globl do_iacc_mmu_miss
+do_iacc_mmu_miss:
+ ENTER_TRAP(do_iacc_mmu_miss)
+ st %g1, [%sp + C_STACK + PT_G1]
+ rd %y, %l4
+ std %g2, [%sp + C_STACK + PT_G2]
+ std %g4, [%sp + C_STACK + PT_G4]
+ std %g6, [%sp + C_STACK + PT_G6]
+ wr %l0, PSR_ET, %psr ! re-enable traps
+ add %sp, C_STACK, %o0
+ mov %l1, %o1
+ mov %l2, %o2
+ call C_LABEL(handle_iacc_mmu_miss)
+ mov %l0, %o3
+
+ ld [%sp + C_STACK + PT_G1], %g1
+ wr %l0, 0x0, %psr
+ ldd [%sp + C_STACK + PT_G2], %g2
+ ldd [%sp + C_STACK + PT_G4], %g4
+ ldd [%sp + C_STACK + PT_G6], %g6
+ b ret_trap_entry
+ wr %l4, 0, %y
+
+ /* The getcc software trap. The user wants the condition codes from
+ * the %psr in register %g1.
+ */
+
+ .align 4
+ .globl getcc_trap_handler
+getcc_trap_handler:
+ /* Shit, one more instruction and I could do this inline. */
+ sll %l0, 0x8, %g1
+ srl %g1, 28, %g1
+ jmp %l2
+ rett %l2+0x4
+
+ /* The setcc software trap. The user has condition codes in %g1
+ * that it would like placed in the %psr. Be careful not to flip
+ * any unintention bits!
+ */
+
+ .align 4
+ .globl setcc_trap_handler
+setcc_trap_handler:
+ sll %g1, 0x14, %l4
+ set PSR_ICC, %l5
+ andn %l0, %l5, %l0
+ or %l4, %l0, %l4
+ wr %l4, 0x0, %psr
+ WRITE_PAUSE
+ jmp %l2
+ rett %l2+0x4
+
+ .align 4
+NMI_STRING: .asciz "NMI received, dazed and confused, halting...\n"
.align 4
.globl linux_trap_nmi
+ .globl C_LABEL(interrupt_enable)
linux_trap_nmi:
- TRAP_WIN_CLEAN
+ sethi %hi(C_LABEL(prom_vector_p)), %o0
+ ld [%o0 + %lo(C_LABEL(prom_vector_p))], %o0
+ ld [%o0 + 0x74], %o0
+ /* Ugh, until I figure out how to clear the IRQ line ;( */
+ call %o0
+ nop
+
+ .align 4
+ .globl sparc_text_fault
+sparc_text_fault:
+ ENTER_TRAP(sparc_text_fault)
+ st %g1, [%sp + C_STACK + PT_G1]
+ std %g2, [%sp + C_STACK + PT_G2]
+ std %g4, [%sp + C_STACK + PT_G4]
+ std %g6, [%sp + C_STACK + PT_G6]
+ rd %y, %l4
+ sethi %hi(AC_SYNC_ERR), %o0
+ lda [%o0] ASI_CONTROL, %o1
+ add %o0, 0x4, %o0 ! go to sync vaddr
+ lda [%o0] ASI_CONTROL, %o2
+ andcc %o1, SUN4C_SYNC_NOMEM, %g0
+ bz,a normal_page_fault
+ wr %l0, PSR_ET, %psr ! re-enable traps
+
+ add %o0, 0x4, %o0 ! go to async error register
+ lda [%o0] ASI_CONTROL, %o3
+ add %o0, 0x4, %o0 ! go to async vaddr
+ subcc %o4, %o2, %g0
+ be,a is_sync_fault ! not an async fault
+ wr %l0, PSR_ET, %psr
+
+ /* crap, an asynchronous error has occurred */
+ sethi %hi(C_LABEL(interrupt_enable)), %l5
+ ldub [%l5 + %lo(C_LABEL(interrupt_enable))], %o0
+ andn %o0, INTS_ENAB, %o0
+ stb %o0, [%l5 + %lo(C_LABEL(interrupt_enable))]
+ wr %l0, PSR_ET, %psr ! enable traps
+ call C_LABEL(sparc_txtmem_error) ! call high level c-code
+ or %g0, FAULT_ASYNC, %o0
+
+ ld [%sp + C_STACK + PT_G1], %g1
+ wr %l0, 0x0, %psr
+ ldd [%sp + C_STACK + PT_G2], %g2
+ ldd [%sp + C_STACK + PT_G4], %g4
+ ldd [%sp + C_STACK + PT_G6], %g6
+ ldub [%l5 + %lo(C_LABEL(interrupt_enable))], %o1
+ or %o1, INTS_ENAB, %o1
+ stb %o1, [%l5 + %lo(C_LABEL(interrupt_enable))]
+ b ret_trap_entry
+ wr %l4, 0, %y
+
+is_sync_fault:
+ call C_LABEL(sparc_txtmem_error) ! call high level c-code
+ or %g0, FAULT_SYNC, %o0
+
+ ld [%sp + C_STACK + PT_G1], %g1
+ ld [%sp + C_STACK + PT_G2], %g2
+ ld [%sp + C_STACK + PT_G4], %g4
+ ld [%sp + C_STACK + PT_G6], %g6
+ wr %l4, 0x0, %y
+ b ret_trap_entry
+ wr %l0, 0x0, %psr
+
+normal_page_fault:
+ std %l0, [%sp + C_STACK + PT_PSR]
+ or %g0, %l3, %o0
+ st %l2, [%sp + C_STACK + PT_NPC]
+ st %l4, [%sp + C_STACK + PT_Y]
+ or %g0, %l1, %o3
+ std %i0, [%sp + C_STACK + PT_I0]
+ std %i2, [%sp + C_STACK + PT_I2]
+ or %g0, %l0, %o4
+ std %i4, [%sp + C_STACK + PT_I4]
+ std %i6, [%sp + C_STACK + PT_I6]
+ call C_LABEL(sparc_text_access_fault)
+ add %sp, C_STACK, %o5
+
+ ldd [%sp + C_STACK + PT_PSR], %l0
+ ldd [%sp + C_STACK + PT_NPC], %l2
+ wr %l3, 0x0, %y
+ ld [%sp + C_STACK + PT_G1], %g1
+ ldd [%sp + C_STACK + PT_G2], %g2
+ ldd [%sp + C_STACK + PT_G4], %g4
+ ldd [%sp + C_STACK + PT_G6], %g6
+ ldd [%sp + C_STACK + PT_I0], %i0
+ ldd [%sp + C_STACK + PT_I2], %i2
+ ldd [%sp + C_STACK + PT_I4], %i4
+ ldd [%sp + C_STACK + PT_I6], %i6
+
+ b ret_trap_entry
+ wr %l0, 0x0, %psr
+
+ .align 4
+ .globl sparc_data_fault
+sparc_data_fault:
+ ENTER_TRAP(sparc_data_fault)
+ st %g1, [%sp + C_STACK + PT_G1]
+ std %g2, [%sp + C_STACK + PT_G2]
+ std %g4, [%sp + C_STACK + PT_G4]
+ std %g6, [%sp + C_STACK + PT_G6]
+ rd %y, %l4
+ sethi %hi(AC_SYNC_ERR), %o0
+ lda [%o0] ASI_CONTROL, %o1
+ add %o0, 0x4, %o0 ! go to sync vaddr
+ lda [%o0] ASI_CONTROL, %o2
+ andcc %o1, SUN4C_SYNC_NOMEM, %g0
+ bz,a normal_data_page_fault
+ wr %l0, PSR_ET, %psr
+
+ add %o0, 0x4, %o0 ! go to async error register
+ lda [%o0] ASI_CONTROL, %o3
+ add %o0, 0x4, %o0 ! go to async vaddr
+ subcc %o4, %o2, %g0
+ be,a is_data_sync_fault ! not an async fault
+ wr %l0, PSR_ET, %psr
+
+ /* crap, an asynchronous error has occurred */
+ sethi %hi(C_LABEL(interrupt_enable)), %l5
+ ldub [%l5 + %lo(C_LABEL(interrupt_enable))], %o0
+ andn %o0, INTS_ENAB, %o0
+ stb %o0, [%l5 + %lo(C_LABEL(interrupt_enable))]
+ wr %l0, PSR_ET, %psr
+ call C_LABEL(sparc_datamem_error) ! call high level c-code
+ or %g0, FAULT_ASYNC, %o0
+
+ ld [%sp + C_STACK + PT_G1], %g1
+ wr %l0, 0x0, %psr
+ ldd [%sp + C_STACK + PT_G2], %g2
+ ldd [%sp + C_STACK + PT_G4], %g4
+ ldd [%sp + C_STACK + PT_G6], %g6
+ ldub [%l5 + %lo(C_LABEL(interrupt_enable))], %o1
+ or %o1, INTS_ENAB, %o1
+ stb %o1, [%l5 + %lo(C_LABEL(interrupt_enable))]
+ b ret_trap_entry
+ wr %l4, 0, %y
+
+is_data_sync_fault:
+ call C_LABEL(sparc_datamem_error) ! call high level c-code
+ or %g0, FAULT_SYNC, %o0
+
+ ld [%sp + C_STACK + PT_G1], %g1
+ ldd [%sp + C_STACK + PT_G2], %g2
+ ldd [%sp + C_STACK + PT_G4], %g4
+ ldd [%sp + C_STACK + PT_G6], %g6
+ wr %l4, 0x0, %y
+ b ret_trap_entry
+ wr %l0, 0x0, %psr
+
+normal_data_page_fault:
+ std %l0, [%sp + C_STACK + PT_PSR] ! store %psr and pc
+ or %g0, %l3, %o0
+ st %l2, [%sp + C_STACK + PT_NPC] ! store npc
+ st %l4, [%sp + C_STACK + PT_Y] ! store %y
+ or %g0, %l1, %o3
+
+ /* The globals have already been placed on the stack */
+ std %i0, [%sp + C_STACK + PT_I0] ! store ins
+ std %i2, [%sp + C_STACK + PT_I2]
+ or %g0, %l0, %o4
+ std %i4, [%sp + C_STACK + PT_I4]
+ std %i6, [%sp + C_STACK + PT_I6]
+ call C_LABEL(sparc_data_access_fault)
+ add %sp, C_STACK, %o5
+
+ ldd [%sp + C_STACK + PT_PSR], %l0
+ ldd [%sp + C_STACK + PT_NPC], %l2
+ wr %l3, 0x0, %y
+ ld [%sp + C_STACK + PT_G1], %g1
+ ldd [%sp + C_STACK + PT_G2], %g2
+ ldd [%sp + C_STACK + PT_G4], %g4
+ ldd [%sp + C_STACK + PT_G6], %g6
+ ldd [%sp + C_STACK + PT_I0], %i0
+ ldd [%sp + C_STACK + PT_I2], %i2
+ ldd [%sp + C_STACK + PT_I4], %i4
+ ldd [%sp + C_STACK + PT_I6], %i6
+
+ b ret_trap_entry
+ wr %l0, 0x0, %psr
+
+
+ .align 4
+ .globl C_LABEL(srmmu_text_fault)
+C_LABEL(srmmu_text_fault):
+ ENTER_TRAP(srmmu_text_fault)
+ st %g1, [%sp + C_STACK + PT_G1]
+ std %g2, [%sp + C_STACK + PT_G2]
+ std %g4, [%sp + C_STACK + PT_G4]
+ std %g6, [%sp + C_STACK + PT_G6]
+ rd %y, %l4
+ set 0x300, %o0
+ lda [%o0] ASI_M_MMUREGS, %o1 ! fault status
+ set 0x400, %o0
+ lda [%o0] ASI_M_MMUREGS, %o2 ! fault address
+ wr %l0, PSR_ET, %psr ! traps back on
+ WRITE_PAUSE
+ std %l0, [%sp + C_STACK + PT_PSR]
+ or %g0, %l3, %o0
+ st %l2, [%sp + C_STACK + PT_NPC]
+ st %l4, [%sp + C_STACK + PT_Y]
+ or %g0, %l1, %o3
+ std %i0, [%sp + C_STACK + PT_I0]
+ std %i2, [%sp + C_STACK + PT_I2]
+ or %g0, %l0, %o4
+ std %i4, [%sp + C_STACK + PT_I4]
+ std %i6, [%sp + C_STACK + PT_I6]
+ call C_LABEL(srmmu_text_access_fault)
+ add %sp, C_STACK, %o5
+
+ ldd [%sp + C_STACK + PT_PSR], %l0
+ ldd [%sp + C_STACK + PT_NPC], %l2
+ wr %l3, 0x0, %y
+ ld [%sp + C_STACK + PT_G1], %g1
+ ldd [%sp + C_STACK + PT_G2], %g2
+ ldd [%sp + C_STACK + PT_G4], %g4
+ ldd [%sp + C_STACK + PT_G6], %g6
+ ldd [%sp + C_STACK + PT_I0], %i0
+ ldd [%sp + C_STACK + PT_I2], %i2
+ ldd [%sp + C_STACK + PT_I4], %i4
+ ldd [%sp + C_STACK + PT_I6], %i6
+
+ b ret_trap_entry
+ wr %l0, 0x0, %psr
+
+ .align 4
+ .globl C_LABEL(srmmu_data_fault)
+C_LABEL(srmmu_data_fault):
+ ENTER_TRAP(srmmu_data_fault)
+ st %g1, [%sp + C_STACK + PT_G1]
+ std %g2, [%sp + C_STACK + PT_G2]
+ std %g4, [%sp + C_STACK + PT_G4]
+ std %g6, [%sp + C_STACK + PT_G6]
+ rd %y, %l4
+
+ set AC_M_SFSR, %o0
+ lda [%o0] ASI_M_MMUREGS, %o1 ! fault status
+ set AC_M_SFAR, %o0
+ lda [%o0] ASI_M_MMUREGS, %o2 ! fault address
+ set AC_M_AFSR, %o0
+ lda [%o0] ASI_M_MMUREGS, %o3
+ set AC_M_AFAR, %o0
+ lda [%o0] ASI_M_MMUREGS, %o4
+ wr %l0, PSR_ET, %psr ! traps back on
+ WRITE_PAUSE
+ std %l0, [%sp + C_STACK + PT_PSR]
+ or %g0, %l3, %o0
+ st %l2, [%sp + C_STACK + PT_NPC]
+ st %l4, [%sp + C_STACK + PT_Y]
+ std %i0, [%sp + C_STACK + PT_I0]
+ std %i2, [%sp + C_STACK + PT_I2]
+ std %i4, [%sp + C_STACK + PT_I4]
+ std %i6, [%sp + C_STACK + PT_I6]
+ call C_LABEL(srmmu_data_access_fault)
+ add %sp, C_STACK, %o5
+
+ ldd [%sp + C_STACK + PT_PSR], %l0
+ ldd [%sp + C_STACK + PT_NPC], %l2
+ wr %l3, 0x0, %y
+ ld [%sp + C_STACK + PT_G1], %g1
+ ldd [%sp + C_STACK + PT_G2], %g2
+ ldd [%sp + C_STACK + PT_G4], %g4
+ ldd [%sp + C_STACK + PT_G6], %g6
+ ldd [%sp + C_STACK + PT_I0], %i0
+ ldd [%sp + C_STACK + PT_I2], %i2
+ ldd [%sp + C_STACK + PT_I4], %i4
+ ldd [%sp + C_STACK + PT_I6], %i6
+
+ b ret_trap_entry
+ wr %l0, 0x0, %psr
+
+/* Normal Linux system calls enter here... */
+/* Trying to make this as generic and simple as possible. */
+
+ .align 4
+ .globl linux_sparc_syscall
+linux_sparc_syscall:
+ /* Don't dork with %l7, it holds the pointer to the
+ * system call vector table. ENTER_TRAP does not
+ * modify its value.
+ */
+ ENTER_TRAP(linux_sparc_syscall)
+
+ /* setup pt_regs stack frame, leave ints off...
+ * First save all but the current window onto the stack.
+ * This means nwindows-2 saves and nwindows-2 restores.
+ */
+ andn %l0, PSR_PIL, %l5
+ wr %l5, 0xf00, %psr
+ wr %l5, 0xf20, %psr ! no ints, traps on
+ WRITE_PAUSE
+
+ .globl nop7
+ /* Flush windows */
+ save %sp, -C_STACK, %sp
+ save %sp, -C_STACK, %sp
+ save %sp, -C_STACK, %sp
+ save %sp, -C_STACK, %sp
+ save %sp, -C_STACK, %sp
+nop7: save %sp, -C_STACK, %sp
+ restore
+ restore
+ restore
+ restore
+ restore
+ restore
+
+ rd %psr, %l6
+ and %l6, PSR_CWP, %l6 ! only care about CWP
+ andn %l0, PSR_CWP, %l0
+ or %l0, %l6, %l0 ! %l0 is now the new %psr
+
+ std %l0, [%sp + C_STACK + PT_PSR] ! save it away
+ rd %y, %l3
+ std %l2, [%sp + C_STACK + PT_NPC]
+
+ /* Put %wim in %g0 slot, a hack. This way we ensure that %wim
+ * sits right behind the current window in %psr, which is what
+ * we want.
+ */
+ rd %wim, %l4
+ st %l4, [%sp + C_STACK + PT_G0]
+ st %g1, [%sp + C_STACK + PT_G1]
+ std %g2, [%sp + C_STACK + PT_G2]
+ std %g4, [%sp + C_STACK + PT_G4]
+ std %g6, [%sp + C_STACK + PT_G6]
+ std %i0, [%sp + C_STACK + PT_I0]
+ std %i2, [%sp + C_STACK + PT_I2]
+ std %i4, [%sp + C_STACK + PT_I4]
+ std %i6, [%sp + C_STACK + PT_I6]
+
+ wr %l0, PSR_ET, %psr /* Turn on traps + interrupts */
+ WRITE_PAUSE
+
+ cmp %i0, NR_SYSCALLS
+ bgu,a C_LABEL(ret_sys_call)
+ or %g0, -1, %i0
+
+ cmp %i0, 0x2 ! fork? Same number for all OSs
+ bne not_fork
+ nop
+ call C_LABEL(sys_fork) ! yep, load pt_regs into first arg
+ add %sp, C_STACK, %o0
+ b C_LABEL(ret_sys_call)
+ nop
+not_fork:
+ /* Remember, trap table entry loaded syscall table ptr in %l7 */
+ sll %i0, 0x2, %o0
+ add %l7, %o0, %l7
+ ld [%l7], %o5 ! load up ptr to syscall handler
+
+ mov %i1, %o0 ! load up arguments
+ mov %i2, %o1
+ mov %i3, %o2
+ mov %i4, %o3
+ jmpl %o5, %o7 ! Make syscall
+ mov %i5, %o4
+
+ .globl C_LABEL(ret_sys_call) /* exported for copy_process() */
+C_LABEL(ret_sys_call): /* Child of a fork returns here */
+ /* dump the pt_regs back into their rightful places */
+ ldd [%sp + C_STACK + PT_PSR], %l0
+ ldd [%sp + C_STACK + PT_NPC], %l2
+ wr %l3, 0x0, %y
+
+ ld [%sp + C_STACK + PT_G1], %g1
+ ldd [%sp + C_STACK + PT_G2], %g2
+ ldd [%sp + C_STACK + PT_G4], %g4
+ ldd [%sp + C_STACK + PT_G6], %g6
+ ldd [%sp + C_STACK + PT_I0], %i0
+ ldd [%sp + C_STACK + PT_I2], %i2
+ ldd [%sp + C_STACK + PT_I4], %i4
+ ldd [%sp + C_STACK + PT_I6], %i6
+
+ /* %i6 is our frame pointer, the restore done by the rett
+ * instruction will automatically put us back on the users
+ * stack.
+ * Advance the pc and npc past the trap instruction, the copy_process()
+ * code for fork() depends on this being done right before trap return.
+ */
+ or %l2, 0x0, %l5
+
+ or %l5, 0x0, %l1 /* pc = npc */
+ add %l5, 0x4, %l2 /* npc= npc+4 */
+
+ wr %l0, 0x0, %psr
+ WRITE_PAUSE
+
+ /* Fall through to ret_trap_entry */
+
+/* Return from trap code. I realized that I was duplicating a lot
+ * of logic in the various trap handlers. Traps are off upon entry.
+ */
+
+ret_trap_entry:
+ and %l0, 0x1f, %l5
+ sethi %hi(lnx_winmask), %l6
+ or %l6, %lo(lnx_winmask), %l6
+ ldub [%l6 + %l5], %l5
+ andcc %l0, PSR_PS, %g0
+ bnz ret_trap_kernel
+ rd %wim, %l4
+
+ sethi %hi(C_LABEL(current)), %l6
+ ld [%l6 + %lo(C_LABEL(current))], %l6
+ ld [%l6 + THREAD_W_SAVED], %l7
+ subcc %g0, %l7, %g0
+ bz,a ret_trap_user
+ nop
+
+ wr %g0, 0, %wim
+ or %g0, %g6, %l3
+ or %g0, %l6, %g6
+ st %g0, [%g6 + THREAD_W_SAVED]
+ restore
+ restore %g0, 1, %l1
+ rd %psr, %l0
+ sll %l1, %l0, %l1
+ wr %l1, 0x0, %wim
+ and %l0, 0x1f, %l0
+ st %l0, [%g6 + THREAD_WIM]
+ nop
+ save %g0, %g0, %g0
+ add %g6, THREAD_REG_WINDOW, %g6
+ ldd [%g6], %l0
+ ldd [%g6 + 0x8], %l2
+ ldd [%g6 + 0x10], %l4
+ ldd [%g6 + 0x18], %l6
+ ldd [%g6 + 0x20], %i0
+ ldd [%g6 + 0x28], %i2
+ ldd [%g6 + 0x30], %i4
+ ldd [%g6 + 0x38], %i6
+
+ save %g0, %g0, %g0
+ wr %l0, 0x0, %psr
+ or %g0, %l3, %g6
jmp %l1
rett %l2
- .align 4
- .globl sparc_trap
-sparc_trap:
- TRAP_WIN_CLEAN
+ret_trap_kernel:
+ andcc %l4, %l5, %g0
+ bnz 1f
+ wr %l0, 0x0, %psr ! reset condition codes
+ nop
jmp %l1
rett %l2
- .align 4
- .globl leave_trap
-leave_trap:
+1:
+ wr %g0, 0x0, %wim
+ WRITE_PAUSE
+ restore
+ restore %g0, 0x1, %l1
+ rd %psr, %l0
+ and %l0, 0x1f, %l0
+ sll %l1, %l0, %l1
+ wr %l1, 0x0, %wim
+ sethi %hi(C_LABEL(current)), %l1
+ ld [%l1 + %lo(C_LABEL(current))], %l1
+ st %l0, [%l1 + THREAD_WIM]
+ save %g0, %g0, %g0
+ ldd [%sp], %l0
+ ldd [%sp + 0x8], %l2
+ ldd [%sp + 0x10], %l4
+ ldd [%sp + 0x18], %l6
+ ldd [%sp + 0x20], %i0
+ ldd [%sp + 0x28], %i2
+ ldd [%sp + 0x30], %i4
+ ldd [%sp + 0x38], %i6
+
+ save %g0, %g0, %g0
jmp %l1
rett %l2
+ret_trap_user:
+ andcc %l4, %l5, %g0
+ bnz 1f
+ wr %l0, 0x0, %psr
+ nop
+ jmp %l1
+ rett %l2
+
+1:
+ wr %g0, 0x0, %wim
+ wr %l0, 0x0, %psr
+ WRITE_PAUSE
+ restore
+ restore %g0, 0x1, %l1
+ rd %psr, %l0
+ sll %l1, %l0, %l1
+ wr %l1, 0x0, %wim
+ sethi %hi(C_LABEL(current)), %l1
+ ld [%l1 + %lo(C_LABEL(current))], %l1
+ and %l0, 0x1f, %l0
+ st %l0, [%l1 + THREAD_WIM]
+ save %g0, %g0, %g0
+ ldd [%sp], %l0
+ ldd [%sp + 0x8], %l2
+ ldd [%sp + 0x10], %l4
+ ldd [%sp + 0x18], %l6
+ ldd [%sp + 0x20], %i0
+ ldd [%sp + 0x28], %i2
+ ldd [%sp + 0x30], %i4
+ ldd [%sp + 0x38], %i6
+ save %g0, %g0, %g0
+ jmp %l1
+ rett %l2
+
+/* Context switch code. I don't feel like playing around with
+ * inline gcc-assembly to do this right, so here it is. The new
+ * process's task_struct ptr is passed in %o0.
+ *
+ * This is still work in progress.
+ * ONLY MAKE PROM CALLS FOR DIAGNOSTICS WHEN TRAPS ARE ON!!!!!
+ *
+ * First successful task switch 05/13/95 21:52:37
+ *
+ */
+ .align 4
+ .globl C_LABEL(sparc_switch_to)
+C_LABEL(sparc_switch_to):
+ or %g0, %o0, %l5
+ sethi %hi(C_LABEL(current)), %l6
+ ld [%l6 + %lo(C_LABEL(current))], %l6
+ rd %psr, %l0
+
+ or %g0, %l0, %l4
+ andn %l0, PSR_PIL, %l0 /* turn off IRQ level bits leave PSR_ET on */
+
+ wr %l0, 0xf00, %psr /* NO interrupts */
+ WRITE_PAUSE
+
+ /* Save state of old process */
+ .globl rnop7
+ save %sp, -C_STACK, %sp
+ save %sp, -C_STACK, %sp
+ save %sp, -C_STACK, %sp
+ save %sp, -C_STACK, %sp
+ save %sp, -C_STACK, %sp
+rnop7: save %sp, -C_STACK, %sp
+ restore
+ restore
+ restore
+ restore
+ restore
+ restore
+
+ rd %psr, %l3
+ and %l3, PSR_CWP, %l3 ! only care about CWP bits now
+ andn %l0, PSR_CWP, %l0 ! integrate with old %psr
+ or %l3, %l0, %l0
+
+ st %l0, [%sp + C_STACK + PT_PSR] ! save new %psr
+ /* ??? We backtrack the PC two instructions due to retl's semantics ??? */
+ /*sub %o7, 0x8, %o7 */
+ st %o7, [%sp + C_STACK + PT_PC] ! save return PC
+ add %o7, 0x4, %l3
+ st %l3, [%sp + C_STACK + PT_NPC] ! and NPC
+
+ rd %y, %l3
+ st %l3, [%sp + C_STACK + PT_Y] ! save %y
+
+ /* Save the %wim into %g0 slot, ensures that it sits behind CWP */
+ rd %wim, %l3
+ st %l3, [%sp + C_STACK + PT_G0] ! save new %wim
+ st %g1, [%sp + C_STACK + PT_G1]
+ std %g2, [%sp + C_STACK + PT_G2]
+ std %g4, [%sp + C_STACK + PT_G4]
+ std %g6, [%sp + C_STACK + PT_G6]
+ std %i0, [%sp + C_STACK + PT_I0]
+ std %i2, [%sp + C_STACK + PT_I2]
+ std %i4, [%sp + C_STACK + PT_I4]
+ std %i6, [%sp + C_STACK + PT_I6]
+
+ wr %l0, (0xf20), %psr ! no traps, no intrs
+ WRITE_PAUSE
+
+ /* TRAPS ARE OFF, NO PROM CALLS WHATSOEVER FOR DEBUGGING!!! */
+ /* SO what we do is we put an imperical constant in %g2 and
+ * a 'counter' in %g1 which we increment after every instruction
+ * so we can figure out where the thing prom panics. Then at the
+ * prom prompt we print out the saved registers. To drop into the
+ * prom and look at the registers just execute 7 saves since that
+ * will induce a window trap before the last one and traps are off,
+ * thus a watchdog reset will occur.
+ */
+
+ /* Grrr, this is hairy... be careful, again NO PROM CALLS YET! */
+ /* Load up the new 'current' */
+ sethi %hi(C_LABEL(current)), %g1
+ st %l5, [%g1 + %lo(C_LABEL(current))]
+
+ /* Load up new processes stack, we always switch onto the kernel stack */
+ /* Be CAREFUL, use globals for temporaries, because after we change the
+ * %psr the window could change and you will most likely be accessing
+ * different outs, ins, and locals than you origionally were.
+ */
+ or %g0, %l5, %g6
+ ld [%l5 + THREAD_KSP], %g3
+
+ /* New procs %psr */
+ ld [%g3 + C_STACK + PT_PSR], %g4
+ wr %g4, 0xf00, %psr /* No ints, no traps */
+ WRITE_PAUSE
+
+ /* We could be in a different window NOW. Assume nothing about the
+ * current set of in, out and local registers.
+ */
+
+ /* New procs %wim */
+ ld [%g3 + C_STACK + PT_G0], %l4 /* %wim is here */
+ st %l4, [%g6 + THREAD_WIM] /* Update tss */
+ wr %l4, 0x0, %wim /* Use it */
+ WRITE_PAUSE
+
+ /* Finally, load the stack */
+ or %g0, %g3, %sp
+
+ /* We are now sane, we have a good stack and our state is reflected
+ * properly in 'current'. Let it rip.
+ */
+ /* Remember, you can't increase PIL and turn on traps at the
+ * same time.
+ */
+ wr %g4, 0xf00, %psr /* Traps on, no interrupts. */
+ wr %g4, 0xf20, %psr
+ WRITE_PAUSE
+
+ sethi %hi(C_LABEL(current)), %o0
+ ld [%o0 + %lo(C_LABEL(current))], %o0
+ ld [%o0 + THREAD_PC], %o7 /* Setup return address */
+
+ /* cross fingers */
+ retl
+ nop
+
/* The following two things point to window management tables. The first
- one is used to quickly look up how many user windows there are from
- trap-land. The second is used in a trap handler to determine if a rett
- instruction will land us smack inside the invalid window that possibly
- the trap was called to fix-up.
-*/
+ * one is used to quickly look up how many user windows there are from
+ * trap-land. The second is used in a trap handler to determine if a rett
+ * instruction will land us smack inside the invalid window that possibly
+ * the trap was called to fix-up.
+ */
+
+/* For now these are static tables geared for a 7 window sparc.
+ * But in head.S after we calculate this table based upon the
+ * nwindows value. This table is big enough for a 16 window sparc.
+ */
-/* For now these are static tables geared for a 7 window sparc. */
.data
.align 4
-lnx_winmask: .byte 2, 4, 8, 16, 32, 64, 128, 1 ! lnx_winmask[0..7]
-
+ .globl lnx_winmask
+lnx_winmask:
+ .byte 2, 4, 8, 16, 32, 64, 1, 0
+ .byte 0, 0, 0, 0, 0, 0, 0, 0
.align 4
.globl C_LABEL(sys_call_table)
.long C_LABEL(sys_getpgid)
.long C_LABEL(sys_fchdir)
.long C_LABEL(sys_bdflush)
- .long C_LABEL(sys_sysfs) /* 135 */
+ .long C_LABEL(sys_sysfs) /* 135 */
.long C_LABEL(sys_personality)
- .long 0 /* for afs_syscall */
+ .long C_LABEL(sys_ni_syscall) /* for afs_syscall */
.long C_LABEL(sys_setfsuid)
.long C_LABEL(sys_setfsgid)
.long C_LABEL(sys_llseek) /* 140 */
+ .long C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+
+ /* 150 */
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+
+ /* 160 */
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+
+ /* 170 */
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+
+ /* 180 */
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+
+ /* 190 */
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+
+ /* 200 */
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+
+ /* 210 */
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+
+ /* 220 */
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+
+ /* 230 */
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+
+ /* 240 */
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+
+ /* 250 */
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall), C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_ni_syscall) /* 255 */
.align 4
-/* boot.S: The initial boot code for the Sparc port of Linux.
-
- Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
-
- This file has to serve three purposes.
-
- 1) determine the prom-version and cpu/architecture
- 2) print enough useful info before we start to execute
- c-code that I can possibly begin to debug things
- 3) Hold the vector of trap entry points
-
- The Sparc offers many challenges to kernel design. Here I will
- document those I have come across thus far. Upon bootup the boot
- prom loads your a.out image into memory. This memory the prom has
- already mapped for you in two places, however as far as I can tell
- the virtual address cache is not turned on although the MMU is
- translating things. You get loaded at 0x4000 exactly and you are
- aliased to 0xf8004000 with the appropriate mmu entries. So, when
- you link a boot-loadable object you want to do something like:
-
- ld -e start -Ttext 4000 -o mykernel myobj1.o myobj2.o ....
-
- to produce a proper image.
-
- At boot time you are given (as far as I can tell at this time)
- one key to figure out what machine you are one and what devices
- are available. The prom when it loads you leaves a pointer to
- the 'rom vector' in register %o0 right before it jumps to your
- starting address. This is a pointer to a struct that is full of
- pointer to functions (ie. printf, halt, reboot), pointers to
- linked lists (ie. memory mappings), and pointer to empirical
- constants (ie. stdin and stdout magic cookies + rom version).
- Starting with this piece of information you can figure out
- just about anything you want about the machine you are on.
-
- Although I don't use it now, if you are on a Multiprocessor and
- therefore a v3 or above prom, register %o2 at boot contains a
- function pointer you must call before you proceed to invoke the
- other cpu's on the machine. I have no idea what kind of magic this
- is, give me time.
-*/
+/* head.S: The initial boot code for the Sparc port of Linux.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ *
+ * This file has to serve three purposes.
+ *
+ * 1) determine the prom-version and cpu/architecture
+ * 2) print enough useful info before we start to execute
+ * c-code that I can possibly begin to debug things
+ * 3) Hold the vector of trap entry points
+ *
+ * The Sparc offers many challenges to kernel design. Here I will
+ * document those I have come across thus far. Upon bootup the boot
+ * prom loads your a.out image into memory. This memory the prom has
+ * already mapped for you in two places, however as far as I can tell
+ * the virtual address cache is not turned on although the MMU is
+ * translating things. You get loaded at 0x4000 exactly and you are
+ * aliased to 0xf8004000 with the appropriate mmu entries. So, when
+ * you link a boot-loadable object you want to do something like
+ *
+ * ld -e start -Ttext 4000 -o mykernel myobj1.o myobj2.o ....
+ *
+ * to produce a proper image.
+ *
+ * At boot time you are given (as far as I can tell at this time)
+ * one key to figure out what machine you are one and what devices
+ * are available. The prom when it loads you leaves a pointer to
+ * the 'rom vector' in register %o0 right before it jumps to your
+ * starting address. This is a pointer to a struct that is full of
+ * pointer to functions (ie. printf, halt, reboot), pointers to
+ * linked lists (ie. memory mappings), and pointer to empirical
+ * constants (ie. stdin and stdout magic cookies + rom version).
+ * Starting with this piece of information you can figure out
+ * just about anything you want about the machine you are on.
+ *
+ * Although I don't use it now, if you are on a Multiprocessor and
+ * therefore a v3 or above prom, register %o2 at boot contains a
+ * function pointer you must call before you proceed to invoke the
+ * other cpu's on the machine. I have no idea what kind of magic this
+ * is, give me time.
+ */
#include <asm/cprefix.h>
#include <asm/head.h>
-#include <asm/version.h>
+#include <linux/version.h>
#include <asm/asi.h>
#include <asm/contregs.h>
#include <asm/psr.h>
#include <asm/page.h>
+#include <asm/kdebug.h>
.data
.globl C_LABEL(intstack)
.globl C_LABEL(eintstack)
+ .align 4
C_LABEL(intstack):
.skip 4 * PAGE_SIZE ! 16k = 128 128-byte stack frames
C_LABEL(eintstack):
/*
- The following are used with the prom_vector node-ops to figure out
- the cpu-type
-*/
+ * The following are used with the prom_vector node-ops to figure out
+ * the cpu-type
+ */
+ .align 4
.globl C_LABEL(cputyp)
-
C_LABEL(cputyp):
.word 1
+ .align 4
+ .globl C_LABEL(cputypval)
C_LABEL(cputypval):
.asciz "sun4c"
.ascii " "
+C_LABEL(cputypvalend):
+C_LABEL(cputypvallen) = C_LABEL(cputypvar) - C_LABEL(cputypval)
+
.align 4
/*
* Sun people can't spell worth damn. "compatability" indeed.
C_LABEL(cputypvar):
.asciz "compatability"
-C_LABEL(cputypvallen) = C_LABEL(cputypvar) - C_LABEL(cputypval)
-
-/* This hold the prom-interface-version number for either v0 or v2. */
-
+/* Tested on SS-5, SS-10. Probably someone at Sun applied a spell-checker. --P3 */
.align 4
- .globl C_LABEL(prom_iface_vers)
-
-C_LABEL(prom_iface_vers): .skip 4
+C_LABEL(cputypvar_sun4m):
+ .asciz "compatible"
/* WARNING: evil messages follow */
.align 4
sun4_notsup:
- .asciz "Sparc-Linux: sun4 support not implemented yet\n\n"
- .align 4
-
-sun4m_notsup:
- .asciz "Sparc-Linux: sun4m support does not exist\n\n"
+ .asciz "Sparc-Linux sun4 support not implemented yet\n\n"
.align 4
sun4d_notsup:
- .asciz "Sparc-Linux: sun4d support does not exist\n\n"
+ .asciz "Sparc-Linux sun4d support does not exist\n\n"
.align 4
-you_lose:
- .asciz "You lose..... Thanks for playing...\n"
+sun4e_notsup:
+ .asciz "Sparc-Linux sun4e support does not exist\n\n"
.align 4
-
- .globl boot_msg
-
-/* memory descriptor property strings, v2 = yuk yuk yuk */
-/* XXX how to figure out vm mapped by prom? May have to scan magic addresses */
-
-mem_prop_physavail: .asciz "available"
-
- .align 4
-mem_prop_phystot: .asciz "reg"
-
-/* v2_memory descriptor struct kludged here for assembly, if it ain't broke */
-
- .align 4
-v2_mem_struct: .skip 0xff
-
- .align 4
-v2_printf_physavail: .asciz "Physical Memory Available: 0x%x bytes"
-
- .align 4
-v2_printf_phystot: .asciz "Physical Memory: 0x%x bytes"
-
-/* A place to store property strings returned from the prom 'node' funcs */
-
- .align 4
-prop_string_buf: .skip 32
-
- .align 4
-prop_name: .asciz "name"
-
- .align 4
-current_node: .skip 4
-
-
-/* nice little boot message */
-
- .align 4
-boot_msg:
- .ascii "Booting Sparc-Linux V0.00PRE-ALPHA "
- .ascii WHO_COMPILED_ME
- .ascii "\r\n"
- .align 4
-
- .globl boot_msg2
-
-boot_msg2:
- .asciz "Booting Sparclinux V0.00 PRE-ALPHA on a (SUN4C)\r\n\n"
-
+sun4u_notsup:
+ .asciz "Sparc-Linux sun4u support does not exist\n\n"
.align 4
-pstring1:
- .asciz "Prom Magic Cookie: 0x%x \n"
- .align 4
+/* Ok, things start to get interesting. We get linked such that 'start'
+ * is the entry symbol. However, it is real low in kernel address space
+ * and as such a nifty place to place the trap table. We achieve this goal
+ * by just jumping to 'gokernel' for the first trap's entry as the sparc
+ * never receives the zero trap as it is real special (hw reset).
+ *
+ * Each trap entry point is the size of 4 sparc instructions (or 4 bytes
+ * * 4 insns = 16 bytes). There are 128 hardware traps (some undefined
+ * or unimplemented) and 128 software traps (sys-calls, etc.).
+ *
+ * One of the instructions must be a branch. More often than not this
+ * will be to a trap handler entry point because it is completely
+ * impossible to handle any trap in 4 insns. I welcome anyone to
+ * challenge this theory. :-)
+ *
+ * On entry into this table the hardware has loaded the program counter
+ * at which the trap occurred into register %l1 and the next program
+ * counter into %l2, this way we can return from the trap with a simple
+ *
+ * jmp %l1; rett %l2 ! poof...
+ *
+ * after properly servicing the trap. It wouldn't be a bad idea to load
+ * some more information into the local regs since we have technically
+ * 2 or 3 instructions to play with besides the jmp to the 'real' trap
+ * handler (one can even go in the delay slot). For now I am going to put
+ * the %psr (processor status register) and the trap-type value in %l0
+ * and %l3 respectively. Also, for IRQ's I'll put the level in %l4.
+ */
-pstring2:
- .asciz "Interface Version: v%d\n"
- .align 4
+ .text
-pstring3:
- .asciz "Prom Revision: V%d\n\n"
- .align 4
+ .globl start
+ .globl _start /* warning, solaris hack */
+ .globl C_LABEL(trapbase)
+_start: /* danger danger */
+start:
+C_LABEL(trapbase):
+/* XXX Grrr, this table is basically sun4c specific, sort of... XXX */
+/* We get control passed to us here at t_zero. */
+t_zero: b gokernel; nop; nop; nop;
+
+t_tflt: TRAP_ENTRY(0x1, sparc_text_fault) /* Inst. Access Exception */
+t_bins: TRAP_ENTRY(0x2, bad_instruction) /* Illegal Instruction */
+t_pins: TRAP_ENTRY(0x3, bad_instruction) /* Privileged Instruction */
+t_fpd: TRAP_ENTRY(0x4, fpd_trap_handler) /* Floating Point Disabled */
+t_wovf: TRAP_ENTRY(0x5, spill_window_entry) /* Window Overflow */
+t_wunf: TRAP_ENTRY(0x6, fill_window_entry) /* Window Underflow */
+t_mna: TRAP_ENTRY(0x7, mna_handler) /* Memory Address Not Aligned */
+t_fpe: TRAP_ENTRY(0x8, fpe_trap_handler) /* Floating Point Exception */
+t_dflt: TRAP_ENTRY(0x9, sparc_data_fault) /* Data Miss Exception */
+t_tio: TRAP_ENTRY(0xa, do_tag_overflow) /* Tagged Instruction Ovrflw */
+t_wpt: TRAP_ENTRY(0xb, do_watchpoint) /* Watchpoint Detected */
+t_badc: TRAP_ENTRY(0xc, bad_trap_handler) /* Undefined... */
+t_badd: TRAP_ENTRY(0xd, bad_trap_handler) /* Undefined... */
+t_bade: TRAP_ENTRY(0xe, bad_trap_handler) /* Undefined... */
+t_badf: TRAP_ENTRY(0xf, bad_trap_handler) /* Undefined... */
+t_bad10:TRAP_ENTRY(0x10, bad_trap_handler) /* Undefined... */
+t_irq1: TRAP_ENTRY_INTERRUPT(1) /* IRQ Software/SBUS Level 1 */
+t_irq2: TRAP_ENTRY_INTERRUPT(2) /* IRQ SBUS Level 2 */
+t_irq3: TRAP_ENTRY_INTERRUPT(3) /* IRQ SCSI/DMA/SBUS Level 3 */
+t_irq4: TRAP_ENTRY_INTERRUPT(4) /* IRQ Software Level 4 */
+t_irq5: TRAP_ENTRY_INTERRUPT(5) /* IRQ SBUS/Ethernet Level 5 */
+t_irq6: TRAP_ENTRY_INTERRUPT(6) /* IRQ Software Level 6 */
+t_irq7: TRAP_ENTRY_INTERRUPT(7) /* IRQ Video/SBUS Level 5 */
+t_irq8: TRAP_ENTRY_INTERRUPT(8) /* IRQ SBUS Level 6 */
+t_irq9: TRAP_ENTRY_INTERRUPT(9) /* IRQ SBUS Level 7 */
+t_irq10:TRAP_ENTRY_TIMER /* IRQ Timer #1 (one we use) */
+t_irq11:TRAP_ENTRY_INTERRUPT(11) /* IRQ Floppy Intr. */
+t_irq12:TRAP_ENTRY_INTERRUPT(12) /* IRQ Zilog serial chip */
+t_irq13:TRAP_ENTRY_INTERRUPT(13) /* IRQ Audio Intr. */
+t_irq14:TRAP_ENTRY_INTERRUPT(14) /* IRQ Timer #2 */
+t_nmi: NMI_TRAP /* Level 15 (NMI) */
+t_racc: TRAP_ENTRY(0x20, do_reg_access) /* General Register Access Error */
+t_iacce:TRAP_ENTRY(0x21, do_iacc_error) /* Instruction Access Error */
+t_bad22:TRAP_ENTRY(0x22, bad_trap_handler) /* Undefined... */
+t_bad23:TRAP_ENTRY(0x23, bad_trap_handler) /* Undefined... */
+t_cpdis:TRAP_ENTRY(0x24, do_cp_disabled) /* Co-Processor Disabled */
+t_uflsh:TRAP_ENTRY(0x25, do_bad_flush) /* Unimplemented FLUSH inst. */
+t_bad26:TRAP_ENTRY(0x26, bad_trap_handler) /* Undefined... */
+t_bad27:TRAP_ENTRY(0x27, bad_trap_handler) /* Undefined... */
+t_cpexc:TRAP_ENTRY(0x28, do_cp_exception) /* Co-Processor Exception */
+t_dacce:TRAP_ENTRY(0x29, do_dacc_error) /* Data Access Error */
+t_hwdz: TRAP_ENTRY(0x2a, do_hw_divzero) /* Division by zero, you lose... */
+t_dserr:TRAP_ENTRY(0x2b, do_dstore_err) /* Data Store Error */
+t_daccm:TRAP_ENTRY(0x2c, do_dacc_mmu_miss) /* Data Access MMU-Miss */
+t_bad2d:TRAP_ENTRY(0x2d, bad_trap_handler) /* Undefined... */
+t_bad2e:TRAP_ENTRY(0x2e, bad_trap_handler) /* Undefined... */
+t_bad2f:TRAP_ENTRY(0x2f, bad_trap_handler) /* Undefined... */
+t_bad30:TRAP_ENTRY(0x30, bad_trap_handler) /* Undefined... */
+t_bad31:TRAP_ENTRY(0x31, bad_trap_handler) /* Undefined... */
+t_bad32:TRAP_ENTRY(0x32, bad_trap_handler) /* Undefined... */
+t_bad33:TRAP_ENTRY(0x33, bad_trap_handler) /* Undefined... */
+t_bad34:TRAP_ENTRY(0x34, bad_trap_handler) /* Undefined... */
+t_bad35:TRAP_ENTRY(0x35, bad_trap_handler) /* Undefined... */
+t_bad36:TRAP_ENTRY(0x36, bad_trap_handler) /* Undefined... */
+t_bad37:TRAP_ENTRY(0x37, bad_trap_handler) /* Undefined... */
+t_bad38:TRAP_ENTRY(0x38, bad_trap_handler) /* Undefined... */
+t_bad39:TRAP_ENTRY(0x39, bad_trap_handler) /* Undefined... */
+t_bad3a:TRAP_ENTRY(0x3a, bad_trap_handler) /* Undefined... */
+t_bad3b:TRAP_ENTRY(0x3b, bad_trap_handler) /* Undefined... */
+t_iaccm:TRAP_ENTRY(0x3c, do_iacc_mmu_miss) /* Instruction Access MMU-Miss */
+t_bad3d:TRAP_ENTRY(0x3d, bad_trap_handler) /* Undefined... */
+t_bad3e:TRAP_ENTRY(0x3e, bad_trap_handler) /* Undefined... */
+t_bad3f:TRAP_ENTRY(0x3f, bad_trap_handler) /* Undefined... */
+t_bad40:TRAP_ENTRY(0x40, bad_trap_handler) /* Undefined... */
+t_bad41:TRAP_ENTRY(0x41, bad_trap_handler) /* Undefined... */
+t_bad42:TRAP_ENTRY(0x42, bad_trap_handler) /* Undefined... */
+t_bad43:TRAP_ENTRY(0x43, bad_trap_handler) /* Undefined... */
+t_bad44:TRAP_ENTRY(0x44, bad_trap_handler) /* Undefined... */
+t_bad45:TRAP_ENTRY(0x45, bad_trap_handler) /* Undefined... */
+t_bad46:TRAP_ENTRY(0x46, bad_trap_handler) /* Undefined... */
+t_bad47:TRAP_ENTRY(0x47, bad_trap_handler) /* Undefined... */
+t_bad48:TRAP_ENTRY(0x48, bad_trap_handler) /* Undefined... */
+t_bad49:TRAP_ENTRY(0x49, bad_trap_handler) /* Undefined... */
+t_bad4a:TRAP_ENTRY(0x4a, bad_trap_handler) /* Undefined... */
+t_bad4b:TRAP_ENTRY(0x4b, bad_trap_handler) /* Undefined... */
+t_bad4c:TRAP_ENTRY(0x4c, bad_trap_handler) /* Undefined... */
+t_bad4d:TRAP_ENTRY(0x4d, bad_trap_handler) /* Undefined... */
+t_bad4e:TRAP_ENTRY(0x4e, bad_trap_handler) /* Undefined... */
+t_bad4f:TRAP_ENTRY(0x4f, bad_trap_handler) /* Undefined... */
+t_bad50:TRAP_ENTRY(0x50, bad_trap_handler) /* Undefined... */
+t_bad51:TRAP_ENTRY(0x51, bad_trap_handler) /* Undefined... */
+t_bad52:TRAP_ENTRY(0x52, bad_trap_handler) /* Undefined... */
+t_bad53:TRAP_ENTRY(0x53, bad_trap_handler) /* Undefined... */
+t_bad54:TRAP_ENTRY(0x54, bad_trap_handler) /* Undefined... */
+t_bad55:TRAP_ENTRY(0x55, bad_trap_handler) /* Undefined... */
+t_bad56:TRAP_ENTRY(0x56, bad_trap_handler) /* Undefined... */
+t_bad57:TRAP_ENTRY(0x57, bad_trap_handler) /* Undefined... */
+t_bad58:TRAP_ENTRY(0x58, bad_trap_handler) /* Undefined... */
+t_bad59:TRAP_ENTRY(0x59, bad_trap_handler) /* Undefined... */
+t_bad5a:TRAP_ENTRY(0x5a, bad_trap_handler) /* Undefined... */
+t_bad5b:TRAP_ENTRY(0x5b, bad_trap_handler) /* Undefined... */
+t_bad5c:TRAP_ENTRY(0x5c, bad_trap_handler) /* Undefined... */
+t_bad5d:TRAP_ENTRY(0x5d, bad_trap_handler) /* Undefined... */
+t_bad5e:TRAP_ENTRY(0x5e, bad_trap_handler) /* Undefined... */
+t_bad5f:TRAP_ENTRY(0x5f, bad_trap_handler) /* Undefined... */
+t_bad60:TRAP_ENTRY(0x60, bad_trap_handler) /* Impl-Dep Exception */
+t_bad61:TRAP_ENTRY(0x61, bad_trap_handler) /* Impl-Dep Exception */
+t_bad62:TRAP_ENTRY(0x62, bad_trap_handler) /* Impl-Dep Exception */
+t_bad63:TRAP_ENTRY(0x63, bad_trap_handler) /* Impl-Dep Exception */
+t_bad64:TRAP_ENTRY(0x64, bad_trap_handler) /* Impl-Dep Exception */
+t_bad65:TRAP_ENTRY(0x65, bad_trap_handler) /* Impl-Dep Exception */
+t_bad66:TRAP_ENTRY(0x66, bad_trap_handler) /* Impl-Dep Exception */
+t_bad67:TRAP_ENTRY(0x67, bad_trap_handler) /* Impl-Dep Exception */
+t_bad68:TRAP_ENTRY(0x68, bad_trap_handler) /* Impl-Dep Exception */
+t_bad69:TRAP_ENTRY(0x69, bad_trap_handler) /* Impl-Dep Exception */
+t_bad6a:TRAP_ENTRY(0x6a, bad_trap_handler) /* Impl-Dep Exception */
+t_bad6b:TRAP_ENTRY(0x6b, bad_trap_handler) /* Impl-Dep Exception */
+t_bad6c:TRAP_ENTRY(0x6c, bad_trap_handler) /* Impl-Dep Exception */
+t_bad6d:TRAP_ENTRY(0x6d, bad_trap_handler) /* Impl-Dep Exception */
+t_bad6e:TRAP_ENTRY(0x6e, bad_trap_handler) /* Impl-Dep Exception */
+t_bad6f:TRAP_ENTRY(0x6f, bad_trap_handler) /* Impl-Dep Exception */
+t_bad70:TRAP_ENTRY(0x70, bad_trap_handler) /* Impl-Dep Exception */
+t_bad71:TRAP_ENTRY(0x71, bad_trap_handler) /* Impl-Dep Exception */
+t_bad72:TRAP_ENTRY(0x72, bad_trap_handler) /* Impl-Dep Exception */
+t_bad73:TRAP_ENTRY(0x73, bad_trap_handler) /* Impl-Dep Exception */
+t_bad74:TRAP_ENTRY(0x74, bad_trap_handler) /* Impl-Dep Exception */
+t_bad75:TRAP_ENTRY(0x75, bad_trap_handler) /* Impl-Dep Exception */
+t_bad76:TRAP_ENTRY(0x76, bad_trap_handler) /* Impl-Dep Exception */
+t_bad77:TRAP_ENTRY(0x77, bad_trap_handler) /* Impl-Dep Exception */
+t_bad78:TRAP_ENTRY(0x78, bad_trap_handler) /* Impl-Dep Exception */
+t_bad79:TRAP_ENTRY(0x79, bad_trap_handler) /* Impl-Dep Exception */
+t_bad7a:TRAP_ENTRY(0x7a, bad_trap_handler) /* Impl-Dep Exception */
+t_bad7b:TRAP_ENTRY(0x7b, bad_trap_handler) /* Impl-Dep Exception */
+t_bad7c:TRAP_ENTRY(0x7c, bad_trap_handler) /* Impl-Dep Exception */
+t_bad7d:TRAP_ENTRY(0x7d, bad_trap_handler) /* Impl-Dep Exception */
+t_bad7e:TRAP_ENTRY(0x7e, bad_trap_handler) /* Impl-Dep Exception */
+t_bad7f:TRAP_ENTRY(0x7f, bad_trap_handler) /* Impl-Dep Exception */
+t_sunos:SUNOS_SYSCALL_TRAP /* SunOS System Call */
+t_sbkpt:TRAP_ENTRY(0x81, bad_trap_handler) /* Software Breakpoint */
+t_divz: TRAP_ENTRY(0x82, bad_trap_handler) /* Divide by zero trap */
+t_flwin:TRAP_ENTRY(0x83, bad_trap_handler) /* Flush Windows Trap */
+t_clwin:TRAP_ENTRY(0x84, bad_trap_handler) /* Clean Windows Trap */
+t_rchk: TRAP_ENTRY(0x85, bad_trap_handler) /* Range Check */
+t_funal:TRAP_ENTRY(0x86, bad_trap_handler) /* Fix Unaligned Access Trap */
+t_iovf: TRAP_ENTRY(0x87, bad_trap_handler) /* Integer Overflow Trap */
+t_slowl:SOLARIS_SYSCALL_TRAP /* Slowaris System Call */
+t_netbs:NETBSD_SYSCALL_TRAP /* Net-B.S. System Call */
+t_bad8a:TRAP_ENTRY(0x8a, bad_trap_handler) /* Software Trap */
+t_bad8b:TRAP_ENTRY(0x8b, bad_trap_handler) /* Software Trap */
+t_bad8c:TRAP_ENTRY(0x8c, bad_trap_handler) /* Software Trap */
+t_bad8d:TRAP_ENTRY(0x8d, bad_trap_handler) /* Software Trap */
+t_bad8e:TRAP_ENTRY(0x8e, bad_trap_handler) /* Software Trap */
+t_bad8f:TRAP_ENTRY(0x8f, bad_trap_handler) /* Software Trap */
+t_linux:LINUX_SYSCALL_TRAP /* Linux System Call */
+t_bad91:TRAP_ENTRY(0x91, bad_trap_handler) /* Software Trap */
+t_bad92:TRAP_ENTRY(0x92, bad_trap_handler) /* Software Trap */
+t_bad93:TRAP_ENTRY(0x93, bad_trap_handler) /* Software Trap */
+t_bad94:TRAP_ENTRY(0x94, bad_trap_handler) /* Software Trap */
+t_bad95:TRAP_ENTRY(0x95, bad_trap_handler) /* Software Trap */
+t_bad96:TRAP_ENTRY(0x96, bad_trap_handler) /* Software Trap */
+t_bad97:TRAP_ENTRY(0x97, bad_trap_handler) /* Software Trap */
+t_bad98:TRAP_ENTRY(0x98, bad_trap_handler) /* Software Trap */
+t_bad99:TRAP_ENTRY(0x99, bad_trap_handler) /* Software Trap */
+t_bad9a:TRAP_ENTRY(0x9a, bad_trap_handler) /* Software Trap */
+t_bad9b:TRAP_ENTRY(0x9b, bad_trap_handler) /* Software Trap */
+t_bad9c:TRAP_ENTRY(0x9c, bad_trap_handler) /* Software Trap */
+t_bad9d:TRAP_ENTRY(0x9d, bad_trap_handler) /* Software Trap */
+t_bad9e:TRAP_ENTRY(0x9e, bad_trap_handler) /* Software Trap */
+t_bad9f:TRAP_ENTRY(0x9f, bad_trap_handler) /* Software Trap */
+t_getcc:GETCC_TRAP /* Get Condition Codes */
+t_setcc:SETCC_TRAP /* Set Condition Codes */
+t_bada2:TRAP_ENTRY(0xa2, bad_trap_handler) /* Software Trap */
+t_bada3:TRAP_ENTRY(0xa3, bad_trap_handler) /* Software Trap */
+t_bada4:TRAP_ENTRY(0xa4, bad_trap_handler) /* Software Trap */
+t_bada5:TRAP_ENTRY(0xa5, bad_trap_handler) /* Software Trap */
+t_bada6:TRAP_ENTRY(0xa6, bad_trap_handler) /* Software Trap */
+t_bada7:TRAP_ENTRY(0xa7, bad_trap_handler) /* Software Trap */
+t_bada8:TRAP_ENTRY(0xa8, bad_trap_handler) /* Software Trap */
+t_bada9:TRAP_ENTRY(0xa9, bad_trap_handler) /* Software Trap */
+t_badaa:TRAP_ENTRY(0xaa, bad_trap_handler) /* Software Trap */
+t_badab:TRAP_ENTRY(0xab, bad_trap_handler) /* Software Trap */
+t_badac:TRAP_ENTRY(0xac, bad_trap_handler) /* Software Trap */
+t_badad:TRAP_ENTRY(0xad, bad_trap_handler) /* Software Trap */
+t_badae:TRAP_ENTRY(0xae, bad_trap_handler) /* Software Trap */
+t_badaf:TRAP_ENTRY(0xaf, bad_trap_handler) /* Software Trap */
+t_badb0:TRAP_ENTRY(0xb0, bad_trap_handler) /* Software Trap */
+t_badb1:TRAP_ENTRY(0xb1, bad_trap_handler) /* Software Trap */
+t_badb2:TRAP_ENTRY(0xb2, bad_trap_handler) /* Software Trap */
+t_badb3:TRAP_ENTRY(0xb3, bad_trap_handler) /* Software Trap */
+t_badb4:TRAP_ENTRY(0xb4, bad_trap_handler) /* Software Trap */
+t_badb5:TRAP_ENTRY(0xb5, bad_trap_handler) /* Software Trap */
+t_badb6:TRAP_ENTRY(0xb6, bad_trap_handler) /* Software Trap */
+t_badb7:TRAP_ENTRY(0xb7, bad_trap_handler) /* Software Trap */
+t_badb8:TRAP_ENTRY(0xb8, bad_trap_handler) /* Software Trap */
+t_badb9:TRAP_ENTRY(0xb9, bad_trap_handler) /* Software Trap */
+t_badba:TRAP_ENTRY(0xba, bad_trap_handler) /* Software Trap */
+t_badbb:TRAP_ENTRY(0xbb, bad_trap_handler) /* Software Trap */
+t_badbc:TRAP_ENTRY(0xbc, bad_trap_handler) /* Software Trap */
+t_badbd:TRAP_ENTRY(0xbd, bad_trap_handler) /* Software Trap */
+t_badbe:TRAP_ENTRY(0xbe, bad_trap_handler) /* Software Trap */
+t_badbf:TRAP_ENTRY(0xbf, bad_trap_handler) /* Software Trap */
+t_badc0:TRAP_ENTRY(0xc0, bad_trap_handler) /* Software Trap */
+t_badc1:TRAP_ENTRY(0xc1, bad_trap_handler) /* Software Trap */
+t_badc2:TRAP_ENTRY(0xc2, bad_trap_handler) /* Software Trap */
+t_badc3:TRAP_ENTRY(0xc3, bad_trap_handler) /* Software Trap */
+t_badc4:TRAP_ENTRY(0xc4, bad_trap_handler) /* Software Trap */
+t_badc5:TRAP_ENTRY(0xc5, bad_trap_handler) /* Software Trap */
+t_badc6:TRAP_ENTRY(0xc6, bad_trap_handler) /* Software Trap */
+t_badc7:TRAP_ENTRY(0xc7, bad_trap_handler) /* Software Trap */
+t_badc8:TRAP_ENTRY(0xc8, bad_trap_handler) /* Software Trap */
+t_badc9:TRAP_ENTRY(0xc9, bad_trap_handler) /* Software Trap */
+t_badca:TRAP_ENTRY(0xca, bad_trap_handler) /* Software Trap */
+t_badcb:TRAP_ENTRY(0xcb, bad_trap_handler) /* Software Trap */
+t_badcc:TRAP_ENTRY(0xcc, bad_trap_handler) /* Software Trap */
+t_badcd:TRAP_ENTRY(0xcd, bad_trap_handler) /* Software Trap */
+t_badce:TRAP_ENTRY(0xce, bad_trap_handler) /* Software Trap */
+t_badcf:TRAP_ENTRY(0xcf, bad_trap_handler) /* Software Trap */
+t_badd0:TRAP_ENTRY(0xd0, bad_trap_handler) /* Software Trap */
+t_badd1:TRAP_ENTRY(0xd1, bad_trap_handler) /* Software Trap */
+t_badd2:TRAP_ENTRY(0xd2, bad_trap_handler) /* Software Trap */
+t_badd3:TRAP_ENTRY(0xd3, bad_trap_handler) /* Software Trap */
+t_badd4:TRAP_ENTRY(0xd4, bad_trap_handler) /* Software Trap */
+t_badd5:TRAP_ENTRY(0xd5, bad_trap_handler) /* Software Trap */
+t_badd6:TRAP_ENTRY(0xd6, bad_trap_handler) /* Software Trap */
+t_badd7:TRAP_ENTRY(0xd7, bad_trap_handler) /* Software Trap */
+t_badd8:TRAP_ENTRY(0xd8, bad_trap_handler) /* Software Trap */
+t_badd9:TRAP_ENTRY(0xd9, bad_trap_handler) /* Software Trap */
+t_badda:TRAP_ENTRY(0xda, bad_trap_handler) /* Software Trap */
+t_baddb:TRAP_ENTRY(0xdb, bad_trap_handler) /* Software Trap */
+t_baddc:TRAP_ENTRY(0xdc, bad_trap_handler) /* Software Trap */
+t_baddd:TRAP_ENTRY(0xdd, bad_trap_handler) /* Software Trap */
+t_badde:TRAP_ENTRY(0xde, bad_trap_handler) /* Software Trap */
+t_baddf:TRAP_ENTRY(0xdf, bad_trap_handler) /* Software Trap */
+t_bade0:TRAP_ENTRY(0xe0, bad_trap_handler) /* Software Trap */
+t_bade1:TRAP_ENTRY(0xe1, bad_trap_handler) /* Software Trap */
+t_bade2:TRAP_ENTRY(0xe2, bad_trap_handler) /* Software Trap */
+t_bade3:TRAP_ENTRY(0xe3, bad_trap_handler) /* Software Trap */
+t_bade4:TRAP_ENTRY(0xe4, bad_trap_handler) /* Software Trap */
+t_bade5:TRAP_ENTRY(0xe5, bad_trap_handler) /* Software Trap */
+t_bade6:TRAP_ENTRY(0xe6, bad_trap_handler) /* Software Trap */
+t_bade7:TRAP_ENTRY(0xe7, bad_trap_handler) /* Software Trap */
+t_bade8:TRAP_ENTRY(0xe8, bad_trap_handler) /* Software Trap */
+t_bade9:TRAP_ENTRY(0xe9, bad_trap_handler) /* Software Trap */
+t_badea:TRAP_ENTRY(0xea, bad_trap_handler) /* Software Trap */
+t_badeb:TRAP_ENTRY(0xeb, bad_trap_handler) /* Software Trap */
+t_badec:TRAP_ENTRY(0xec, bad_trap_handler) /* Software Trap */
+t_baded:TRAP_ENTRY(0xed, bad_trap_handler) /* Software Trap */
+t_badee:TRAP_ENTRY(0xee, bad_trap_handler) /* Software Trap */
+t_badef:TRAP_ENTRY(0xef, bad_trap_handler) /* Software Trap */
+t_badf0:TRAP_ENTRY(0xf0, bad_trap_handler) /* Software Trap */
+t_badf1:TRAP_ENTRY(0xf1, bad_trap_handler) /* Software Trap */
+t_badf2:TRAP_ENTRY(0xf2, bad_trap_handler) /* Software Trap */
+t_badf3:TRAP_ENTRY(0xf3, bad_trap_handler) /* Software Trap */
+t_badf4:TRAP_ENTRY(0xf4, bad_trap_handler) /* Software Trap */
+t_badf5:TRAP_ENTRY(0xf5, bad_trap_handler) /* Software Trap */
+t_badf6:TRAP_ENTRY(0xf6, bad_trap_handler) /* Software Trap */
+t_badf7:TRAP_ENTRY(0xf7, bad_trap_handler) /* Software Trap */
+t_badf8:TRAP_ENTRY(0xf8, bad_trap_handler) /* Software Trap */
+t_badf9:TRAP_ENTRY(0xf9, bad_trap_handler) /* Software Trap */
+t_badfa:TRAP_ENTRY(0xfa, bad_trap_handler) /* Software Trap */
+t_badfb:TRAP_ENTRY(0xfb, bad_trap_handler) /* Software Trap */
+t_badfc:TRAP_ENTRY(0xfc, bad_trap_handler) /* Software Trap */
+t_badfd:TRAP_ENTRY(0xfd, bad_trap_handler) /* Software Trap */
+dbtrap: TRAP_ENTRY(0xfe, bad_trap_handler) /* Debugger/PROM breakpoint #1 */
+dbtrap2:TRAP_ENTRY(0xff, bad_trap_handler) /* Debugger/PROM breakpoint #2 */
+
+ .globl C_LABEL(end_traptable)
+C_LABEL(end_traptable):
-pstring4:
- .ascii "Total Physical Memory: %d bytes\nVM mapped by Prom: %d bytes\n"
- .asciz "Available Physical Memory: %d bytes\n"
- .align 4
+ .skip 4096
+/* This was the only reasonable way I could think of to properly align
+ * these page-table data structures.
+ *
+ * XXX swapper_pg_dir is going to have to be 'per-CPU' for SMP support
+ */
- .text
+ .globl C_LABEL(auxio_reg_addr)
+C_LABEL(auxio_reg_addr): .skip (PAGE_SIZE)
- .globl C_LABEL(msgbuf)
-msgbufsize = PAGE_SIZE ! 1 page for msg buffer
-C_LABEL(msgbuf) = PAGE_SIZE
+ .globl C_LABEL(clock_reg_addr)
+C_LABEL(clock_reg_addr): .skip (PAGE_SIZE*5)
+ .globl C_LABEL(int_reg_addr)
+C_LABEL(int_reg_addr): .skip (PAGE_SIZE*5)
-IE_reg_addr = C_LABEL(msgbuf) + msgbufsize ! this page not used; points to IEreg
+ .globl C_LABEL(pg0)
+ .globl C_LABEL(empty_bad_page)
+ .globl C_LABEL(empty_bad_page_table)
+ .globl C_LABEL(empty_zero_page)
+ .globl C_LABEL(swapper_pg_dir)
+C_LABEL(swapper_pg_dir): .skip 0x1000
+C_LABEL(pg0): .skip 0x1000
+C_LABEL(empty_bad_page): .skip 0x1000
+C_LABEL(empty_bad_page_table): .skip 0x1000
+C_LABEL(empty_zero_page): .skip 0x1000
-
-/* Ok, things start to get interesting. We get linked such that 'start'
- is the entry symbol. However, it is real low in kernel address space
- and as such a nifty place to place the trap table. We achieve this goal
- by just jumping to 'gokernel' for the first trap's entry as the sparc
- never receives the zero trap as it is real special (hw reset).
- Each trap entry point is the size of 4 sparc instructions (or 4 bytes
- * 4 insns = 16 bytes). There are 128 hardware traps (some undefined
- or unimplemented) and 128 software traps (sys-calls, etc.).
+/* Cool, here we go. Pick up the romvec pointer in %o0 and stash it in
+ * %g7 and at prom_vector_p. And also quickly check whether we are on
+ * a v0, v2, or v3 prom. We also get a debug structure of some sort from
+ * the boot loader (or is it the prom?) in %o1. Finally a call back vector
+ * is passed in %o2. I think this is how you register yourself with a
+ * debugger. I do know that it wants my %o7 (return PC - 8) as it's
+ * first argument. I will poke around and figure out what the debug
+ * vector is, it could contain useful stuff.
+ */
- One of the instructions must be a branch. More often than not this
- will be to a trap handler entry point because it is completely
- impossible to handle any trap in 4 insns. I welcome anyone to
- challenge this theory. :-)
+/* Grrr, in order to be Sparc ABI complient, the kernel has to live in
+ * an address space above 0xe0000000 ;( Must remain position independant
+ * until we 'remap' ourselves from low to high addresses. We only map the
+ * first 3MB of addresses into upper ram as that is how much the PROM
+ * promises to set up for us.
+ */
+gokernel:
+ /* Ok, it's nice to know, as early as possible, if we
+ * are already mapped where we expect to be in virtual
+ * memory. The Solaris /boot elf format bootloader
+ * will peek into our elf header and load us where
+ * we want to be, otherwise we have to re-map.
+ *
+ * Some boot loaders don't place the jmp'rs address
+ * in %o7, so we do a pc-relative call to a local
+ * label, then see what %o7 has.
+ */
+
+ /* XXX Sparc V9 detection goes here XXX */
+
+ or %g0, %o7, %g4 ! Save %o7
+
+ /* Jump to it, and pray... */
+current_pc:
+ call 1f
+ nop
- On entry into this table the hardware has loaded the program counter
- at which the trap occurred into register %l1 and the next program
- counter into %l2, this way we can return from the trap with a simple
+1:
+ or %g0, %o7, %g3
- jmp %l1; rett %l2 ! poof...
+got_pc:
+ or %g0, %g4, %o7 /* Previous %o7. */
+
+ or %g0, %o0, %l0 ! stash away romvec
+ or %g0, %o0, %g7 ! put it here too
+ or %g0, %o1, %l1 ! stash away debug_vec too
+ rd %psr, %l2 ! Save psr
+ rd %wim, %l3 ! wim
+ rd %tbr, %l4 ! tbr
+ or %g0, %o2, %l5 ! and the possible magic func
+
+ /* Ok, let's check out our run time program counter. */
+ set current_pc, %g5
+ cmp %g3, %g5
+ be already_mapped
+
+ /* %l6 will hold the offset we have to subtract
+ * from absolute symbols in order to access areas
+ * in our own image. If already mapped this is
+ * just plain zero, else it is PAGE_OFFSET which is
+ * also KERNBASE.
+ */
+ set PAGE_OFFSET, %l6
+ b copy_prom_lvl14
+ nop
- after properly servicing the trap. It wouldn't be a bad idea to load
- some more information into the local regs since we have technically
- 2 or 3 instructions to play with besides the jmp to the 'real' trap
- handler (one can even go in the delay slot). For now I am going to put
- the %psr (processor status register) and the trap-type value in %l0
- and %l3 respectively. Also, for IRQ's I'll put the level in %l4.
+already_mapped:
+ or %g0, %g0, %l6
+
+ /* Copy over the Prom's level 14 clock handler. */
+copy_prom_lvl14:
+ rd %tbr, %g1
+ andn %g1, 0xfff, %g1 ! proms trap table base
+ or %g0, (0x1e<<4), %g2 ! offset to lvl14 intr
+ or %g1, %g2, %g2
+ set t_irq14, %g3
+ sub %g3, %l6, %g3
+ ldd [%g2], %g4
+ std %g4, [%g3]
+ ldd [%g2 + 0x8], %g4
+ std %g4, [%g3 + 0x8] ! Copy proms handler
+
+ /* Copy over the Prom/debugger's trap entry points. */
+copy_prom_bpoint:
+ or %g0, (0xfe<<4), %g2
+ or %g1, %g2, %g2
+ set dbtrap, %g3
+ sub %g3, %l6, %g3
+ ldd [%g2], %g4
+ std %g4, [%g3]
+ ldd [%g2 + 0x8], %g4
+ std %g4, [%g3 + 0x8]
+ ldd [%g2 + 0x10], %g4
+ std %g4, [%g3 + 0x10]
+ ldd [%g2 + 0x18], %g4
+ std %g4, [%g3 + 0x18]
+
+copy_prom_done:
+ ld [%o0 + 0x4], %g1
+ and %g1, 0x3, %g1
+ subcc %g1, 0x0, %g0
+ be set_sane_psr ! Not on v0 proms
+ nop
-*/
+ subcc %o2, 0x0, %g0 ! check for boot routine pointer
+ bz set_sane_psr
+ nop
+ jmpl %o2, %o7 ! call boot setup func
+ add %o7, 0x8, %o0
+
+set_sane_psr:
+ /* Traps are on, kadb can be tracing through here. But
+ * we have no clue what the PIL is. So we set up a sane
+ * %psr, but preserve CWP or our locals could disappear!
+ */
+ rd %psr, %g2
+ and %g2, 0x1f, %g2 ! %g2 has CWP now
+ set (PSR_S|PSR_PIL), %g1 ! Supervisor + PIL high
+ or %g1, %g2, %g1 ! mix mix mix
+ wr %g1, 0x0, %psr ! let PIL set in
+ wr %g1, PSR_ET, %psr ! now turn on traps
+
+ /* A note about the last two instructions...
+ * If you are going to increase PIL and turn on
+ * traps at the same time you are asking for trouble.
+ * You MUST set a %psr with traps off containing
+ * your new PIL, then turn on the ET bit with the
+ * next write. On certain buggy Sparc chips if you
+ * set both at the same time you can get a Watchdog
+ * Reset under certain conditions. This is no fun.
+ * Basically the PIL bits get there before the
+ * EnableTrap bit does or something like that.
+ */
+
+ /* Insane asylum... */
+ WRITE_PAUSE
- .globl start
- .globl _start /* warning, solaris hack */
- .globl C_LABEL(trapbase)
-_start: /* danger danger */
-start:
-C_LABEL(trapbase):
- b gokernel; nop; nop; nop; ! we never get trap #0 it is special
-
- TRAP_ENTRY(0x1, my_trap_handler) /* Instruction Access Exception */
- TRAP_ENTRY(0x2, my_trap_handler) /* Illegal Instruction */
- TRAP_ENTRY(0x3, my_trap_handler) /* Privileged Instruction */
- TRAP_ENTRY(0x4, my_trap_handler) /* Floating Point Disabled */
- TRAP_ENTRY(0x5, spill_window_entry) /* Window Overflow */
- TRAP_ENTRY(0x6, fill_window_entry) /* Window Underflow */
- TRAP_ENTRY(0x7, my_trap_handler) /* Memory Address Not Aligned */
- TRAP_ENTRY(0x8, my_trap_handler) /* Floating Point Exception */
- TRAP_ENTRY(0x9, my_trap_handler) /* Data Miss Exception */
- TRAP_ENTRY(0xa, my_trap_handler) /* Tagged Instruction Overflow */
- TRAP_ENTRY(0xb, my_trap_handler) /* Watchpoint Detected */
- TRAP_ENTRY(0xc, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0xd, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0xe, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0xf, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x10, my_trap_handler) /* Undefined... */
-
-/* Level'd interrupt entry points, see macro defs above */
-
- TRAP_ENTRY_INTERRUPT_SOFT(1, 0x101) /* IRQ Software/SBUS Level 1 */
- TRAP_ENTRY_INTERRUPT(2) /* IRQ SBUS Level 2 */
- TRAP_ENTRY_INTERRUPT(3) /* IRQ SCSI/DMA/SBUS Level 3 */
- TRAP_ENTRY_INTERRUPT_SOFT(4, 0x104) /* IRQ Software Level 4 */
- TRAP_ENTRY_INTERRUPT(5) /* IRQ SBUS/Ethernet Level 5 */
- TRAP_ENTRY_INTERRUPT_SOFT(6, 0x106) /* IRQ Software Level 6 */
- TRAP_ENTRY_INTERRUPT(7) /* IRQ Video/SBUS Level 5 */
- TRAP_ENTRY_INTERRUPT(8) /* IRQ SBUS Level 6 */
- TRAP_ENTRY_INTERRUPT(9) /* IRQ SBUS Level 7 */
- TRAP_ENTRY_INTERRUPT(10) /* IRQ Timer #1 */
- TRAP_ENTRY_INTERRUPT(11) /* IRQ Floppy Intr. */
- TRAP_ENTRY_INTERRUPT(12) /* IRQ Zilog serial chip */
- TRAP_ENTRY_INTERRUPT(13) /* IRQ Audio Intr. */
- TRAP_ENTRY_TIMER /* IRQ Timer #2 (one we use) */
- TRAP_ENTRY_INTERRUPT_NMI(15, linux_trap_nmi) /* Level 15 (nmi) */
-
- TRAP_ENTRY(0x20, my_trap_handler) /* General Register Access Error */
- TRAP_ENTRY(0x21, my_trap_handler) /* Instruction Access Error */
- TRAP_ENTRY(0x22, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x23, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x24, my_trap_handler) /* Co-Processor Disabled */
- TRAP_ENTRY(0x25, my_trap_handler) /* Unimplemented FLUSH inst. */
- TRAP_ENTRY(0x26, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x27, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x28, my_trap_handler) /* Co-Processor Exception */
- TRAP_ENTRY(0x29, my_trap_handler) /* Data Access Error */
- TRAP_ENTRY(0x2a, my_trap_handler) /* Division by zero, you lose... */
- TRAP_ENTRY(0x2b, my_trap_handler) /* Data Store Error */
- TRAP_ENTRY(0x2c, my_trap_handler) /* Data Access MMU-Miss */
- TRAP_ENTRY(0x2d, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x2e, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x2f, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x30, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x31, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x32, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x33, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x34, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x35, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x36, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x37, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x38, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x39, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x3a, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x3b, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x3c, my_trap_handler) /* Instruction Access MMU-Miss */
- TRAP_ENTRY(0x3d, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x3e, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x3f, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x40, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x41, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x42, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x43, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x44, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x45, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x46, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x47, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x48, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x49, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x4a, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x4b, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x4c, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x4d, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x4e, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x4f, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x50, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x51, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x52, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x53, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x54, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x55, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x56, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x57, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x58, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x59, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x5a, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x5b, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x5c, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x5d, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x5e, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x5f, my_trap_handler) /* Undefined... */
- TRAP_ENTRY(0x60, my_trap_handler) /* Impl-Dep Exception */
- TRAP_ENTRY(0x61, my_trap_handler) /* Impl-Dep Exception */
- TRAP_ENTRY(0x62, my_trap_handler) /* Impl-Dep Exception */
- TRAP_ENTRY(0x63, my_trap_handler) /* Impl-Dep Exception */
- TRAP_ENTRY(0x64, my_trap_handler) /* Impl-Dep Exception */
- TRAP_ENTRY(0x65, my_trap_handler) /* Impl-Dep Exception */
- TRAP_ENTRY(0x66, my_trap_handler) /* Impl-Dep Exception */
- TRAP_ENTRY(0x67, my_trap_handler) /* Impl-Dep Exception */
- TRAP_ENTRY(0x68, my_trap_handler) /* Impl-Dep Exception */
- TRAP_ENTRY(0x69, my_trap_handler) /* Impl-Dep Exception */
- TRAP_ENTRY(0x6a, my_trap_handler) /* Impl-Dep Exception */
- TRAP_ENTRY(0x6b, my_trap_handler) /* Impl-Dep Exception */
- TRAP_ENTRY(0x6c, my_trap_handler) /* Impl-Dep Exception */
- TRAP_ENTRY(0x6d, my_trap_handler) /* Impl-Dep Exception */
- TRAP_ENTRY(0x6e, my_trap_handler) /* Impl-Dep Exception */
- TRAP_ENTRY(0x6f, my_trap_handler) /* Impl-Dep Exception */
- TRAP_ENTRY(0x70, my_trap_handler) /* Impl-Dep Exception */
- TRAP_ENTRY(0x71, my_trap_handler) /* Impl-Dep Exception */
- TRAP_ENTRY(0x72, my_trap_handler) /* Impl-Dep Exception */
- TRAP_ENTRY(0x73, my_trap_handler) /* Impl-Dep Exception */
- TRAP_ENTRY(0x74, my_trap_handler) /* Impl-Dep Exception */
- TRAP_ENTRY(0x75, my_trap_handler) /* Impl-Dep Exception */
- TRAP_ENTRY(0x76, my_trap_handler) /* Impl-Dep Exception */
- TRAP_ENTRY(0x77, my_trap_handler) /* Impl-Dep Exception */
- TRAP_ENTRY(0x78, my_trap_handler) /* Impl-Dep Exception */
- TRAP_ENTRY(0x79, my_trap_handler) /* Impl-Dep Exception */
- TRAP_ENTRY(0x7a, my_trap_handler) /* Impl-Dep Exception */
- TRAP_ENTRY(0x7b, my_trap_handler) /* Impl-Dep Exception */
- TRAP_ENTRY(0x7c, my_trap_handler) /* Impl-Dep Exception */
- TRAP_ENTRY(0x7d, my_trap_handler) /* Impl-Dep Exception */
- TRAP_ENTRY(0x7e, my_trap_handler) /* Impl-Dep Exception */
- TRAP_ENTRY(0x7f, my_trap_handler) /* Impl-Dep Exception */
- TRAP_ENTRY(0x80, my_trap_handler) /* SunOS System Call */
- TRAP_ENTRY(0x81, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0x82, my_trap_handler) /* Divide by zero trap XXX */
- TRAP_ENTRY(0x83, my_trap_handler) /* Flush Windows Trap XXX */
- TRAP_ENTRY(0x84, my_trap_handler) /* Clean Windows Trap XXX */
- TRAP_ENTRY(0x85, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0x86, my_trap_handler) /* Fix Unaligned Access Trap XXX */
- TRAP_ENTRY(0x87, my_trap_handler) /* Integer Overflow Trap XXX */
- TRAP_ENTRY(0x88, my_trap_handler) /* Slowaris System Call */
- TRAP_ENTRY(0x89, my_trap_handler) /* NetBSD System Call */
- TRAP_ENTRY(0x8a, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0x8b, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0x8c, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0x8d, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0x8e, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0x8f, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0x90, my_trap_handler) /* SparcLinux System Call */
- TRAP_ENTRY(0x91, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0x92, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0x93, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0x94, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0x95, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0x96, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0x97, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0x98, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0x99, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0x9a, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0x9b, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0x9c, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0x9d, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0x9e, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0x9f, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xa0, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xa1, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xa2, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xa3, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xa4, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xa5, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xa6, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xa7, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xa8, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xa9, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xaa, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xab, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xac, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xad, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xae, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xaf, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xb0, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xb1, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xb2, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xb3, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xb4, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xb5, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xb6, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xb7, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xb8, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xb9, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xba, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xbb, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xbc, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xbd, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xbe, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xbf, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xc0, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xc1, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xc2, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xc3, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xc4, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xc5, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xc6, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xc7, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xc8, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xc9, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xca, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xcb, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xcc, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xcd, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xce, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xcf, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xd0, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xd1, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xd2, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xd3, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xd4, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xd5, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xd6, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xd7, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xd8, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xd9, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xda, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xdb, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xdc, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xdd, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xde, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xdf, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xe0, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xe1, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xe2, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xe3, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xe4, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xe5, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xe6, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xe7, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xe8, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xe9, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xea, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xeb, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xec, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xed, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xee, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xef, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xf0, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xf1, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xf2, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xf3, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xf4, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xf5, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xf6, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xf7, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xf8, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xf9, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xfa, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xfb, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xfc, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xfd, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xfe, my_trap_handler) /* Software Trap */
- TRAP_ENTRY(0xff, my_trap_handler) /* Software Trap */
+/* Must determine whether we are on a sun4c MMU, SRMMU, or SUN4/400 MUTANT
+ * MMU so we can remap ourselves properly. DONT TOUCH %l0 thru %l5 in these
+ * remapping routines, we need their values afterwards!
+ *
+ * XXX UGH, need to write some sun4u SpitFire remapping V9 code RSN... XXX
+ */
+ /* Now check whether we are already mapped, if we
+ * are we can skip all this garbage coming up.
+ */
+ subcc %l6, 0x0, %g0
+ bz go_to_highmem ! this will be a nop then
+ nop
- .skip 4096
+ sethi %hi(LOAD_ADDR), %g6
+ subcc %g7, %g6, %g0
+ bne remap_not_a_sun4 ! This is not a Sun4
+ nop
-C_LABEL(msgbufmapped):
- .word 1
+ or %g0, 0x1, %g1
+ lduba [%g1] ASI_CONTROL, %g1 ! Only safe to try on Sun4.
+ subcc %g1, 0x24, %g0 ! Is this a mutant Sun4/400???
+ be sun4_mutant_remap ! Ugh, it is...
+ nop
+remap_not_a_sun4:
+ lda [%g0] ASI_M_MMUREGS, %g1 ! same as ASI_PTE on sun4c
+ and %g1, 0x1, %g1 ! Test SRMMU Enable bit ;-)
+ subcc %g1, 0x0, %g0
+ bz sun4c_remap ! A sun4c MMU or normal Sun4
+ nop
+srmmu_remap:
+ /* First, check for a viking (TI) module. */
+ set 0x40000000, %g2
+ rd %psr, %g3
+ and %g2, %g3, %g3
+ subcc %g3, 0x0, %g0
+ bz srmmu_nviking
+ nop
+ /* Figure out what kind of viking we are on.
+ * We need to know if we have to play with the
+ * AC bit and disable traps or not.
+ */
+
+ /* I've only seen MicroSparc's on SparcClassics with this
+ * bit set.
+ */
+ set 0x800, %g2
+ lda [%g0] ASI_M_MMUREGS, %g3 ! peek in the control reg
+ and %g2, %g3, %g3
+ subcc %g3, 0x0, %g0
+ bnz srmmu_nviking ! is in mbus mode
+ nop
+
+ rd %psr, %g3 ! DONT TOUCH %g3
+ andn %g3, PSR_ET, %g2
+ wr %g2, 0x0, %psr
+ WRITE_PAUSE
+
+ /* Get context table pointer, then convert to
+ * a physical address, which is 36 bits.
+ */
+ set AC_M_CTPR, %g4
+ lda [%g4] ASI_M_MMUREGS, %g4
+ sll %g4, 0x4, %g4 ! We use this below
+ ! DONT TOUCH %g4
+
+ /* Set the AC bit in the Viking's MMU control reg. */
+ lda [%g0] ASI_M_MMUREGS, %g5 ! DONT TOUCH %g5
+ set 0x8000, %g6 ! AC bit mask
+ or %g5, %g6, %g6 ! Or it in...
+ sta %g6, [%g0] ASI_M_MMUREGS ! Close your eyes...
+
+ /* Grrr, why does it seem like every other load/store
+ * on the sun4m is in some ASI space...
+ * Fine with me, let's get the pointer to the level 1
+ * page table directory and fetch it's entry.
+ */
+ lda [%g4] ASI_M_BYPASS, %o1 ! This is a level 1 ptr
+ srl %o1, 0x4, %o1 ! Clear low 4 bits
+ sll %o1, 0x8, %o1 ! Make physical
+
+ /* Ok, pull in the PTD. */
+ lda [%o1] ASI_M_BYPASS, %o2 ! This is the 0x0 16MB pgd
+
+ /* Calculate to KERNBASE entry.
+ *
+ * XXX Should not use imperical constant, but Gas gets an XXX
+ * XXX upset stomach with the bitshift I would have to use XXX
+ */
+ add %o1, 0x3c0, %o3
+
+ /* Poke the entry into the calculated address. */
+ sta %o2, [%o3] ASI_M_BYPASS
+
+ /* I don't get it Sun, if you engineered all these
+ * boot loaders and the PROM (thank you for the debugging
+ * features btw) why did you not have them load kernel
+ * images up in high address space, since this is necessary
+ * for ABI compliance anyways? Does this low-mapping provide
+ * enhanced interoperability?
+ *
+ * "The PROM is the computer."
+ */
+
+ /* Ok, restore the MMU control register we saved in %g5 */
+ sta %g5, [%g0] ASI_M_MMUREGS ! POW... ouch
+
+ /* Turn traps back on. We saved it in %g3 earlier. */
+ wr %g3, 0x0, %psr ! tick tock, tick tock
+
+ /* Now we burn precious CPU cycles due to bad engineering. */
+ WRITE_PAUSE
-/* Cool, here we go. Pick up the romvec pointer in %o0 and stash it in
- %g7 and at _prom_vector_p. And also quickly check whether we are on
- a v0 or v2 prom.
-*/
+ /* Wow, all that just to move a 32-bit value from one
+ * place to another... Jump to high memory.
+ */
+ b go_to_highmem
+ nop
-gokernel: or %g0, %o0, %g7
- sethi %hi( C_LABEL(prom_vector_p) ), %g1
- st %o0, [%g1 + %lo( C_LABEL(prom_vector_p) )] ! we will need it later
- rd %psr, %l2
- rd %wim, %l3
- rd %tbr, %l4
- or %g0, %o2, %l5 ! could be prom magic value...
-
-#if 0 /* You think I'm nutz? */
- subcc %l5, 0x0, %g0 ! check for magic SMP pointer
- bne nosmp
+ /* This works on viking's in Mbus mode and all
+ * other MBUS modules. It is virtually the same as
+ * the above madness sans turning traps off and flipping
+ * the AC bit.
+ */
+srmmu_nviking:
+ set AC_M_CTPR, %g1
+ lda [%g1] ASI_M_MMUREGS, %g1 ! get ctx table ptr
+ sll %g1, 0x4, %g1 ! make physical addr
+ lda [%g1] ASI_M_BYPASS, %g1 ! ptr to level 1 pg_table
+ srl %g1, 0x4, %g1
+ sll %g1, 0x8, %g1 ! make phys addr for l1 tbl
+
+ lda [%g1] ASI_M_BYPASS, %g2 ! get level1 entry for 0x0
+ add %g1, 0x3c0, %g3 ! XXX AWAY WITH IMPERICALS
+ sta %g2, [%g3] ASI_M_BYPASS ! place at KERNBASE entry
+ b go_to_highmem
+ nop ! wheee....
+
+ /* This remaps the kernel on Sun4/4xx machines
+ * that have the Sun Mutant Three Level MMU.
+ * It's like a platypus, Sun didn't have the
+ * SRMMU in conception so they kludged the three
+ * level logic in the regular Sun4 MMU probably.
+ *
+ * Basically, you take each entry in the top level
+ * directory that maps the low 3MB starting at
+ * address zero and put the mapping in the KERNBASE
+ * slots. These top level pgd's are called regmaps.
+ */
+sun4_mutant_remap:
+ or %g0, %g0, %g3 ! source base
+ sethi %hi(KERNBASE), %g4 ! destination base
+ or %g4, %lo(KERNBASE), %g4
+ sethi %hi(0x300000), %g5
+ or %g5, %lo(0x300000), %g5 ! upper bound 3MB
+ or %g0, 0x1, %l6
+ sll %l6, 24, %l6 ! Regmap mapping size
+ add %g3, 0x2, %g3 ! Base magic
+ add %g4, 0x2, %g4 ! Base magic
+
+ /* Main remapping loop on Sun4-Mutant-MMU.
+ * "I am not an animal..." -Famous Mutant Person
+ */
+sun4_mutant_loop:
+ lduha [%g3] ASI_REGMAP, %g2 ! Get lower entry
+ stha %g2, [%g4] ASI_REGMAP ! Store in high entry
+ add %g4, %l6, %g4 ! Move up high memory ptr
+ subcc %g3, %g5, %g0 ! Reached our limit?
+ blu sun4_mutant_loop ! Nope, loop again
+ add %g3, %l6, %g3 ! delay, Move up low ptr
+ b go_to_highmem ! Jump to high memory.
nop
- call %o2 ! call smp prom setup
+
+/* The following works for normal (ie. non Sun4/400) Sun4 MMU's */
+sun4c_remap:
+ or %g0, %g0, %g3 ! source base
+ sethi %hi(KERNBASE), %g4 ! destination base
+ or %g4, %lo(KERNBASE), %g4
+ sethi %hi(0x300000), %g5
+ or %g5, %lo(0x300000), %g5 ! upper bound 3MB
+ or %g0, 0x1, %l6
+ sll %l6, 18, %l6 ! sun4c mmu segmap size
+sun4c_remap_loop:
+ lda [%g3] ASI_SEGMAP, %g6 ! load phys_seg
+ sta %g6, [%g4] ASI_SEGMAP ! store new virt mapping
+ add %g3, %l6, %g3 ! Increment source ptr
+ subcc %g3, %g5, %g0 ! Reached limit?
+ bl sun4c_remap_loop ! Nope, loop again
+ add %g4, %l6, %g4 ! delay, Increment dest ptr
+
+/* Now do a non-relative jump so that PC is in high-memory */
+go_to_highmem:
+ set execute_in_high_mem, %g1
+ jmp %g1
nop
-#endif /* I will be soon... */
/* Acquire boot time privileged register values, this will help debugging.
- * I figure out and store nwindows later on.
+ * I figure out and store nwindows and nwindowsm1 later on.
*/
+execute_in_high_mem:
+ or %g0, %l0, %o0 ! put back romvec
+ or %g0, %l1, %o1 ! and debug_vec
-nosmp: sethi %hi( C_LABEL(boot_psr) ), %l1
- st %l2, [%l1 + %lo( C_LABEL(boot_psr) )]
- sethi %hi( C_LABEL(boot_wim) ), %l1
- st %l3, [%l1 + %lo( C_LABEL(boot_wim) )]
- sethi %hi( C_LABEL(boot_tbr) ), %l1
- st %l4, [%l1 + %lo( C_LABEL(boot_tbr) )]
- sethi %hi( C_LABEL(boot_smp_ptr) ), %l1
- st %l5, [%l1 + %lo( C_LABEL(boot_smp_ptr) )]
+ sethi %hi( C_LABEL(prom_vector_p) ), %g1
+ st %o0, [%g1 + %lo( C_LABEL(prom_vector_p) )]
- or %g0, %o0, %g7
- sethi %hi( C_LABEL(prom_vector_p) ), %g5
- st %o0, [%g5 + %lo( C_LABEL(prom_vector_p) )] ! we will need it later
+ sethi %hi( C_LABEL(linux_dbvec) ), %g1
+ st %o1, [%g1 + %lo( C_LABEL(linux_dbvec) )]
+
+ ld [%o0 + 0x4], %o3
+ and %o3, 0x3, %o5 ! get the version
- ld [%g7 + 0x4], %o3
subcc %o3, 0x2, %g0 ! a v2 prom?
- be found_v2
+ be found_version
nop
/* paul@sfe.com.au */
subcc %o3, 0x3, %g0 ! a v3 prom?
- or %g0, 0x3, %o5
- sethi %hi(C_LABEL(prom_iface_vers) ), %g1
- st %o5, [%g1 + %lo( C_LABEL(prom_iface_vers) )]
- be not_v2
+ be found_version
nop
-
/* Old sun4's pass our load address into %o0 instead of the prom
- pointer. On sun4's you have to hard code the romvec pointer into
- your code. Sun probably still does that because they don't even
- trust their own "OpenBoot" specifications.
-*/
+ * pointer. On sun4's you have to hard code the romvec pointer into
+ * your code. Sun probably still does that because they don't even
+ * trust their own "OpenBoot" specifications.
+ */
sethi %hi(LOAD_ADDR), %g6
subcc %o0, %g6, %g0 ! an old sun4?
be no_sun4_here
nop
- sethi %hi( C_LABEL(prom_iface_vers) ), %g1
- st %g0, [%g1 + %lo( C_LABEL(prom_iface_vers) )]
- b not_v2
- nop
-
-found_v2:
- or %g0, 0x2, %o5
- sethi %hi( C_LABEL(prom_iface_vers) ), %g1
- st %o5, [%g1 + %lo( C_LABEL(prom_iface_vers) )]
-
-not_v2:
+found_version:
/* Get the machine type via the mysterious romvec node operations.
- * Here we can find out whether we are on a sun4 sun4c, sun4m, or
- * a sun4m. The "nodes" are set up as a bunch of n-ary trees which
- * you can traverse to get information about devices and such. The
- * information acquisition happens via the node-ops which are defined
- * in the linux_openprom.h header file. Of particular interest is the
- * 'nextnode(int node)' function as it does the smart thing when
- * presented with a value of '0', it gives you the first node in the
+ * Here we can find out whether we are on a sun4, sun4c, sun4m,
+ * sun4d, or a sun4e. The "nodes" are set up as a bunch of n-ary trees
+ * which you can traverse to get information about devices and such.
+ * The information acquisition happens via the node-ops which are
+ * defined in the openprom.h header file. Of particular interest
+ * is the 'nextnode(int node)' function as it does the smart thing when
+ * presented with a value of '0', it gives you the root node in the
* tree. These node integers probably offset into some internal prom
* pointer table the openboot has. It's completely undocumented, so
* I'm not about to go sifting through the prom address space, but may
ld [%l0], %l0
call %l0
or %g0, %g0, %o0 ! next_node(0) = first_node
+ or %o0, %g0, %g6
- sethi %hi( C_LABEL(cputypvar) ), %o1 ! first node has cpu-arch
+ sethi %hi( C_LABEL(cputypvar) ), %o1 ! First node has cpu-arch
or %o1, %lo( C_LABEL(cputypvar) ), %o1
sethi %hi( C_LABEL(cputypval) ), %o2 ! information, the string
or %o2, %lo( C_LABEL(cputypval) ), %o2
! to a buf where above string
! will get stored by the prom.
- sethi %hi( C_LABEL(cputypval) ), %o2 ! better safe than sorry
- or %o2, %lo( C_LABEL(cputypval) ), %o2
- ldub [%o2 + 0x4], %o0
- subcc %o0, 'c', %g0 ! we already know we are not
- be is_sun4c ! on a plain sun4 because of
- nop ! the check for 0x4000 in %o0
- subcc %o0, 'm', %g0 ! at start:
- be is_sun4m
+ subcc %o0, %g0, %g0
+ bpos got_prop ! Got the property
nop
- b no_sun4d_here ! god bless the person who
- nop ! tried to run this on sun4d
-
-is_sun4m:
-is_sun4c: ! OK, this is a sun4c, yippie
- or %g0, %g7, %g6 ! load up the promvec offsets
- sethi %hi(prom_magic), %g5 ! magic mushroom :>
- st %g6, [%g5 + %lo(prom_magic)]
- add %g7, 0x4, %g6
- sethi %hi(prom_rom_vers), %g5
- st %g6, [%g5 + %lo(prom_rom_vers)]
- add %g7, 0x8, %g6
- sethi %hi(prom_pluginvers), %g5
- st %g6, [%g5 + %lo(prom_pluginvers)]
- add %g7, 0xc, %g6
- sethi %hi(prom_revision), %g5
- st %g6, [%g5 + %lo(prom_revision)]
- add %g7, 0x10, %g6
- sethi %hi(prom_v0mem_desc), %g5
- st %g6, [%g5 + %lo(prom_v0mem_desc)]
- add %g7, 0x1c, %g6
- sethi %hi(prom_nodefuncs), %g5
- st %g6, [%g5 + %lo(prom_nodefuncs)]
- add %g7, 0x68, %g6
- sethi %hi(prom_printf), %g5
- st %g6, [%g5 + %lo(prom_printf)]
- add %g7, 0x6c, %g6
- sethi %hi(prom_abort), %g5
- st %g6, [%g5 + %lo(prom_abort)]
- add %g7, 0x74, %g6
- sethi %hi(prom_halt), %g5
- st %g6, [%g5 + %lo(prom_halt)]
- add %g7, 0x78, %g6
- sethi %hi(prom_sync), %g5
- st %g6, [%g5 + %lo(prom_sync)]
- add %g7, 0x7c, %g6
- sethi %hi(prom_eval), %g5
- st %g6, [%g5 + %lo(prom_eval)]
- add %g7, 0x80, %g6
- sethi %hi(prom_v0bootline), %g6
- st %g6, [%g5 + %lo(prom_v0bootline)]
-
-
-/* That was easy, now lets try to print some message on the screen.
- * We don't have to worry about bad address translations when the prom
- * addresses our pointers because our pointers are at 0x0-kern_size
- * as the prom expects.
- */
-/* paul@sfe.com.au */
-/* V3 doesn't have printf.. And I don't really feel like doing the formatting
- * myself.. So we miss out on some messages (for now).
- */
- ld [%g7 + 0x4], %o0
- subcc %o3, 0x3, %g0
- be v3_bootmsg
+ or %g6, %g0, %o0
+ sethi %hi( C_LABEL(cputypvar_sun4m) ), %o1
+ or %o1, %lo( C_LABEL(cputypvar_sun4m) ), %o1
+ sethi %hi( C_LABEL(cputypval) ), %o2
+ or %o2, %lo( C_LABEL(cputypval) ), %o2
+ ld [%l1], %l0
+ ld [%l0 + 0xc], %l0
+ call %l0
nop
- sethi %hi(boot_msg), %o0
- or %o0, %lo(boot_msg), %o0
- sethi %hi(prom_printf), %o1
- ld [%o1 + %lo(prom_printf)], %o1
- ld [%o1], %o1
- call %o1 ! print boot message #1
- nop
+got_prop:
+ sethi %hi( C_LABEL(cputypval) ), %o2
+ or %o2, %lo( C_LABEL(cputypval) ), %o2
- sethi %hi(pstring1), %o0
- or %o0, %lo(pstring1), %o0
- sethi %hi(prom_printf), %o2
- ld [%o2 + %lo(prom_printf)], %o2
- ld [%o2], %o2
- sethi %hi(prom_magic), %o1
- ld [%o1 + %lo(prom_magic)], %o1
- ld [%o1], %o1
- call %o2
+ ldub [%o2 + 0x4], %l1
+ subcc %l1, 'c', %g0 ! We already know we are not
+ be 1f ! on a plain sun4 because of
+ nop ! the check for 0x4000 in %o0
+ subcc %l1, 'm', %g0 ! at start
+ be 1f
nop
- sethi %hi(pstring2), %o0
- or %o0, %lo(pstring2), %o0
- sethi %hi(prom_printf), %o2
- ld [%o2 + %lo(prom_printf)], %o2
- ld [%o2], %o2
- sethi %hi( C_LABEL(prom_iface_vers) ), %o1
- ld [%o1 + %lo( C_LABEL(prom_iface_vers) )], %o1
- ld [%o1], %o1
- call %o2
+ subcc %l1, 'd', %g0
+ be no_sun4d_here ! God bless the person who
+ nop ! tried to run this on sun4d.
+
+ subcc %l1, 'e', %g0
+ be no_sun4e_here ! Could be a sun4e.
nop
- b rest_of_boot
+ b no_sun4u_here ! AIEEE, a V9 sun4u...
nop
-v3_bootmsg:
- ld [%g7 + 0x94], %o0
- ld [%o0], %o0
- sethi %hi(boot_msg), %o1
- or %o1, %lo(boot_msg), %o1
- mov BOOT_MSG_LEN, %o2
- ld [%g7 + 0xb8], %o4
- call %o4
- nop
-
- ld [%g7 + 0x94], %o0
- ld [%o0], %o0
- sethi %hi(boot_msg2), %o1
- or %o1, %lo(boot_msg2), %o1
- mov BOOT_MSG2_LEN, %o2
- ld [%g7 + 0xb8], %o4
- call %o4
- nop
- b rest_of_boot
- nop
+1:
+ or %g0, PAGE_SHIFT, %g5
-no_sun4_here:
- ld [%g7 + 0x68], %o1
- set sun4_notsup, %o0
- call %o1
+ sethi %hi( C_LABEL(cputypval) ), %l1
+ or %l1, %lo( C_LABEL(cputypval) ), %l1
+ ldub [%l1 + 0x4], %l1
+ subcc %l1, 'm', %g0 ! Test for sun4d, sun4e ?
+ be sun4m_init
nop
-rest_of_boot:
- or %g0, PAGE_SHIFT, %g5
-
sethi %hi(AC_CONTEXT), %g1 ! kernel context, safe now
! the only valid context
! until we call paging_init()
stba %g0, [%g1] ASI_CONTROL
+ b sun4c_continue_boot
+ nop
-/* I make the kernel image sit in memory relative to 0x0 with the text
- * starting at 0x4000. Now it looks like the way memory is set in Linux
- * on an ix86.
+sun4m_init:
+/* P3: I just do not know what to do here. But I do know that ASI_CONTROL
+ * will not serve on sun4m. Also I do not want to smash the current MMU
+ * setup until we call paging_init().
*/
-/* Uh, oh, interrupt time. This crap is real confusing. What I want to do is
- * clear all interrupts, map the interrupt enable register which in effect
- * enables non-maskable interrupts (or NMI's). Actually we take no interrupts
- * until we frob with the %tbr (trap base register) which the prom has set
- * to all its routines which allows some sanity during bootup.
+/* Ok, the PROM could have done funny things and apple cider could still
+ * be sitting in the fault status/address registers. Read them all to
+ * clear them so we don't get magic faults later on.
*/
+/* This sucks, aparently this makes Vikings call prom panic, will fix later */
- sethi %hi(IE_reg_addr), %l0
- or %l0, %lo(IE_reg_addr), %l0
+ set (0x40000000), %o1
+ rd %psr, %o0
+ andcc %o0, %o1, %g0
+ bne sun4c_continue_boot ! quick hack
+ nop
- set 0xf4000000, %l3
- sethi %hi(INT_ENABLE_REG_PHYSADR), %l2
- or %l2, %lo(INT_ENABLE_REG_PHYSADR), %l2
- srl %l2, %g5, %l2
- or %l2, %l3, %l1
+clr_srmmu_fregs:
+ set AC_M_SFSR, %o0
+ lda [%o0] ASI_M_MMUREGS, %g0
+ set AC_M_SFAR, %o0
+ lda [%o0] ASI_M_MMUREGS, %g0
+ set AC_M_AFSR, %o0
+ lda [%o0] ASI_M_MMUREGS, %g0
+ set AC_M_AFAR, %o0
+ lda [%o0] ASI_M_MMUREGS, %g0
+ nop
+
+
+sun4c_continue_boot:
-#ifndef CONFIG_SRMMU
- sta %l1, [%l0] ASI_PTE
-#endif
-
- or %g0, 0x1, %l1
- stb %l1, [%l0]
-
/* Aieee, now set PC and nPC, enable traps, give ourselves a stack and it's
* show-time!
*/
- sethi %hi(1f), %g1
- or %g1, %lo(1f), %g1
- jmp %g1
- nop
-
- .align 4
-1: sethi %hi( C_LABEL(cputyp) ), %o0
+ sethi %hi( C_LABEL(cputyp) ), %o0
st %g4, [%o0 + %lo( C_LABEL(cputyp) )]
- sethi %hi( C_LABEL(pgshift) ), %o0
- st %g5, [%o0 + %lo( C_LABEL(pgshift) )]
-
- mov 1, %o0
- sll %o0, %g5, %g5
- sethi %hi( C_LABEL(nbpg) ), %o0
- st %g5, [%o0 + %lo( C_LABEL(nbpg) )]
-
- sub %g5, 1, %g5
- sethi %hi( C_LABEL(pgofset) ), %o0
- st %g5, [%o0 + %lo( C_LABEL(pgofset) )]
-
-
- rd %psr, %g3
- andn %g3, PSR_ET, %g3
- wr %g3, 0x0, %psr ! make sure traps are off
- ! before we play around
- WRITE_PAUSE ! no guarantees until 3 insns
-
-
- wr %g0, 0x0, %wim ! magical invalid window reg
- WRITE_PAUSE ! see above
-
-/* I keep the timer interrupt on so that BogoMIPS works and the prom
- * keeps updating its "jiffies" counter. 100HZ clock on sparcstations.
- */
-
-/* If gas wasn't so dumb, I could use or'd macros in this next
- * write. ;-( like this (PSR_PS | PSR_S | PSR_PIL)...
- */
-
+ /* Turn on PreviousSupervisor, Supervisor, EnableFloating,
+ * and all the PIL bits. Also puts us in register window
+ * zero.
+ */
sethi %hi(PSR_PS | PSR_S | PSR_PIL | PSR_EF), %g2
or %g2, %lo(PSR_PS | PSR_S | PSR_PIL | PSR_EF), %g2
wr %g2, 0x0, %psr
WRITE_PAUSE
- wr %g0, 0x2, %wim ! window 1 invalid
+ wr %g0, 0x2, %wim ! Make window 1 invalid.
WRITE_PAUSE
+ /* Initialize the WIM value for init_task. */
or %g0, 0x1, %g1
sethi %hi( C_LABEL(current) + THREAD_WIM), %g2
st %g1, [%g2 + %lo( C_LABEL(current) + THREAD_WIM)]
/* I want a kernel stack NOW! */
+/* Grrr, gotta be real careful about alignment here */
- set ( C_LABEL(init_user_stack) + 4092 - 96 - 80), %fp
- set ( C_LABEL(init_user_stack) + 4092), %sp
-
-/* now out stack is set up similarly to the way it is on the i386 */
+ set ( C_LABEL(init_user_stack) + PAGE_SIZE - 96 - 96 - 80), %g1
+ andn %g1, 0x7, %g1
+ or %g1, 0x0, %fp
+ add %fp, (96+80), %sp
+ /* Enable traps. */
rd %psr, %l0
wr %l0, PSR_ET, %psr
WRITE_PAUSE
* don't know, do you?
*/
- set C_LABEL(edata) , %o0
- set C_LABEL(end) , %o1
- sub %o1, %o0, %g2
- sethi %hi( C_LABEL(kernel_bss_len) ), %g3
- st %g2, [%g3 + %lo( C_LABEL(kernel_bss_len) )]
- sethi %hi( C_LABEL(trapbase) ), %g3
- or %g3, %lo( C_LABEL(trapbase) ), %g3
- sethi %hi( C_LABEL(etext) ), %g4
- or %g4, %lo( C_LABEL(etext) ), %g4
- sub %g4, %g3, %g2
- sethi %hi( C_LABEL(kernel_text_len) ), %g3
- st %g2, [%g3 + %lo( C_LABEL(kernel_text_len) )]
- sethi %hi( C_LABEL(etext) ), %g4
- or %g4, %lo( C_LABEL(etext) ), %g4
- sethi %hi( C_LABEL(edata) ), %g3
- or %g3, %lo( C_LABEL(edata) ), %g3
- sub %g3, %g4, %g2
- sethi %hi( C_LABEL(kernel_data_len) ), %g3
- st %g2, [%g3 + %lo( C_LABEL(kernel_data_len) )]
- or %g0, %g0, %g1
+ set C_LABEL(edata) , %o0 ! First address of BSS
+ set C_LABEL(end) , %o1 ! Last address of BSS
+
+ /* Argh, ELF gets me again... */
+ andn %o0, 0x3, %o0
+ andn %o1, 0x3, %o1
+
+/* Friggin' bzero() kludge. */
1:
st %g0, [%o0]
nop
/* Compute NWINDOWS and stash it away. Now uses %wim trick explained
- * in the V8 manual. Ok, this method seems to work, sparc is cool...
+ * in the V8 manual. Ok, this method seems to work, Sparc is cool...
+ * No, it doesn't work, have to play the save/readCWP/restore trick.
*/
- sethi %hi(0xffffffff), %g1
- rd %wim, %g2 ! save current value
- or %g1, %lo(0xffffffff), %g1
- wr %g1, 0x0, %wim
- rd %wim, %g1 ! get remaining mask
- wr %g2, 0x0, %wim ! restore old value
+ rd %wim, %g1
+ rd %psr, %g2
+ wr %g0, 0x0, %wim ! so we dont get a trap
+ andn %g2, 0x1f, %g3
+ wr %g3, 0x0, %psr
WRITE_PAUSE
+ save
+ rd %psr, %g3
+ restore
+ and %g3, 0x1f, %g3
+ add %g3, 0x1, %g3
+ wr %g2, 0x0, %psr
+ wr %g1, 0x0, %wim
- or %g0, 0x0, %g3
+ cmp %g3, 0x7
+ bne,a 2f
+ sethi %hi( C_LABEL(nwindows) ), %g4
+
+
+ /* Nop out one save and one restore in the save state code
+ * and system call entry if this is a seven window Sparc.
+ */
+ sethi %hi(nop7), %g5
+ or %g5, %lo(nop7), %g5
+ sethi %hi(NOP_INSN), %g6
+ or %g6, %lo(NOP_INSN), %g6
+ /* patch 1 */
+ st %g6, [%g5]
+ st %g6, [%g5 + 0x4]
+ sethi %hi(rnop7), %g5
+ or %g5, %lo(rnop7), %g5
+ /* patch 2 */
+ st %g6, [%g5]
+ st %g6, [%g5 + 0x4]
-1: srl %g1, 0x1, %g1 ! shift until highest
- subcc %g1, 0x0, %g0 ! bit set
- bne 1b
- add %g3, 0x1, %g3
sethi %hi( C_LABEL(nwindows) ), %g4
+
+2:
st %g3, [%g4 + %lo( C_LABEL(nwindows) )] ! store final value
sub %g3, 0x1, %g3
sethi %hi( C_LABEL(nwindowsm1) ), %g4
st %g3, [%g4 + %lo( C_LABEL(nwindowsm1) )]
-
-/* Here we go */
-
-#ifndef CONFIG_SUN4M
- /* paul@sfe.com.au */
- /* Look into traps later :( */
+ /* Initialize lnx_winmask. */
+ set lnx_winmask, %g4
+ or %g0, 0x2, %g5
+ or %g0, 0x0, %g6
+msk_loop:
+ stb %g5, [%g4 + %g6]
+ add %g6, 0x1, %g6
+ cmp %g6, %g3
+ bl,a msk_loop
+ sll %g5, 0x1, %g5
+ or %g0, 0x1, %g5
+ stb %g5, [%g4 + %g3]
+
+ /* Here we go */
set C_LABEL(trapbase), %g3
wr %g3, 0x0, %tbr
WRITE_PAUSE
-#endif
-/* First we call init_prom() to set up romvec, then off to start_kernel() */
-/* XXX put this in arch_init() */
+/* First we call prom_init() to set up PROMLIB, then off to start_kernel() */
+/* XXX put this in setup_arch() */
sethi %hi( C_LABEL(prom_vector_p) ), %g5
- call C_LABEL(init_prom)
- ld [%g5 + %lo( C_LABEL(prom_vector_p) )], %o0 /* delay slot */
+ call C_LABEL(prom_init)
+ ld [%g5 + %lo( C_LABEL(prom_vector_p) )], %o0
+
+ subcc %o0, 0x1, %g0
+ be halt_me ! promlib init failed
+ nop
call C_LABEL(start_kernel)
nop
+ /* We should not get here. */
call halt_me
nop
-/* There, happy now adrian? */
+/* There, happy now Adrian? */
+
+ /* XXX Fix this... XXX */
+no_sun4_here:
+ sethi %hi(SUN4_PROM_VECTOR+SUN4_PRINTF), %o1
+ ld [%o1 + %lo(SUN4_PROM_VECTOR+SUN4_PRINTF)], %o1
+ set sun4_notsup, %o0
+ call %o1
+ nop
+1:
+ ba 1b ! Cannot exit into KMON
+ nop
no_sun4d_here:
ld [%g7 + 0x68], %o1
b halt_me
nop
+no_sun4e_here:
+ ld [%g7 + 0x68], %o1
+ set sun4e_notsup, %o0
+ call %o1
+ nop
+ b halt_me
+ nop
+
+no_sun4u_here:
+ ld [%g7 + 0x68], %o1
+ set sun4u_notsup, %o0
+ call %o1
+ nop
+ b halt_me
+ nop
+
halt_me:
ld [%g7 + 0x74], %o0
- call %o0 ! get us out of here...
- nop ! apparently solaris is better
+ call %o0 ! Get us out of here...
+ nop ! Apparently Solaris is better.
.data
.align 4
*/
.globl C_LABEL(prom_vector_p)
-
C_LABEL(prom_vector_p): .skip 4
-prom_magic: .skip 4 ! magic mushroom, beware...
-prom_rom_vers: .skip 4 ! interface version (v0 or v2)
-prom_pluginvers: .skip 4 ! XXX help help help ???
-prom_revision: .skip 4 ! PROM revision (ie. 1.4)
-prom_halt: .skip 4 ! void halt(void) solaris friend
-prom_eval: .skip 4 ! void eval(int len, char* string)
-prom_v0bootline: .skip 4 ! boot command line
-prom_v0mem_desc: .skip 4 ! V0 memory descriptor list ptr.
-prom_nodefuncs: .skip 4 ! Magical Node functions
-prom_printf: .skip 4 ! minimal printf()
-
-/* The prom_abort pointer MUST be mapped in all contexts, because if you
- * don't then if a user process is running when you press the abort key
- * sequence, all sorts of bad things can happen
- */
-
-prom_abort: .skip 4 ! L1-A magic cookie
- ! must be mapped in ALL contexts
-
-/* prom_sync is a place where the kernel should place a pointer to a kernel
- * function that when called will sync all pending information to the drives
- * and then promptly return. If the kernel gets aborted with 'L1-A' one can
- * give the 'sync' command to the boot prompt and this magic cookie gets
- * executed. Nice feature eh?
- */
-
-prom_sync: .skip 4 ! hook in prom for sync func
.align 4
C_LABEL(nwindowsm1): .skip 4
.align 4
-/* Boot time privileged register values, plus magic %o2 value */
- .globl C_LABEL(boot_wim)
- .globl C_LABEL(boot_psr)
- .globl C_LABEL(boot_tbr)
- .globl C_LABEL(boot_smp_ptr)
-C_LABEL(boot_wim): .skip 4
-C_LABEL(boot_psr): .skip 4
-C_LABEL(boot_tbr): .skip 4
-C_LABEL(boot_smp_ptr): .skip 4
+/* Boot time debugger vector value. We need this later on. */
+ .globl C_LABEL(linux_dbvec)
+C_LABEL(linux_dbvec): .skip 4
.align 4
-/* Miscellaneous pieces of information saved at kernel startup. */
- .globl C_LABEL(kernel_text_len)
- .globl C_LABEL(kernel_data_len)
- .globl C_LABEL(kernel_bss_len)
-C_LABEL(kernel_text_len): .word 0
-C_LABEL(kernel_data_len): .word 0
-C_LABEL(kernel_bss_len): .word 0
-
-/* These are for page alignment/offset information as they change from
- machine to machine.
-*/
-
- .globl C_LABEL(pgshift)
- .globl C_LABEL(nbpg)
- .globl C_LABEL(pgofset)
-
- .align 4
-C_LABEL(pgshift):
- .word 1
-C_LABEL(nbpg):
- .word 1
-C_LABEL(pgofset):
- .word 1
/* Just to get the kernel through the compiler for now */
- .globl C_LABEL(swapper_pg_dir), C_LABEL(pg0)
- .globl C_LABEL(empty_bad_page)
- .globl C_LABEL(empty_bad_page_table)
- .globl C_LABEL(empty_zero_page)
.globl C_LABEL(floppy_track_buffer)
C_LABEL(floppy_track_buffer):
.fill 512*2*36,1,0
-
- .align 4
-C_LABEL(swapper_pg_dir): .skip 0x1000
-C_LABEL(pg0): .skip 0x1000
-C_LABEL(empty_bad_page): .skip 0x1000
-C_LABEL(empty_bad_page_table): .skip 0x1000
-C_LABEL(empty_zero_page): .skip 0x1000
-
- .align 4
-diagstr: .asciz "DIAG\n"
- .align 4
/* idprom.c: Routines to load the idprom into kernel addresses and
* interpret the data contained within.
*
+ * Because they use the IDPROM's machine type field, some of the
+ * virtual address cache probings on the sun4c are done here.
+ *
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
*/
#include <linux/kernel.h>
#include <asm/types.h>
-#include <asm/openprom.h>
+#include <asm/oplib.h>
#include <asm/idprom.h>
+#include <asm/machines.h> /* Fun with Sun released architectures. */
+#include <asm/system.h> /* For halt() macro */
+
+struct idp_struct *idprom;
+static struct idp_struct idprom_buff;
-struct idp_struct idprom;
-extern int num_segmaps, num_contexts;
+/* Here is the master table of Sun machines which use some implementation
+ * of the Sparc CPU and have a meaningful IDPROM machtype value that we
+ * know about. See asm-sparc/machines.h for imperical constants.
+ */
+struct Sun_Machine_Models Sun_Machines[NUM_SUN_MACHINES] = {
+/* First, Sun4's */
+{ "Sun 4/100 Series", (SM_SUN4 | SM_4_110) },
+{ "Sun 4/200 Series", (SM_SUN4 | SM_4_260) },
+{ "Sun 4/300 Series", (SM_SUN4 | SM_4_330) },
+{ "Sun 4/400 Series", (SM_SUN4 | SM_4_470) },
+/* Now, Sun4c's */
+{ "Sun4c SparcStation 1", (SM_SUN4C | SM_4C_SS1) },
+{ "Sun4c SparcStation IPC", (SM_SUN4C | SM_4C_IPC) },
+{ "Sun4c SparcStation 1+", (SM_SUN4C | SM_4C_SS1PLUS) },
+{ "Sun4c SparcStation SLC", (SM_SUN4C | SM_4C_SLC) },
+{ "Sun4c SparcStation 2", (SM_SUN4C | SM_4C_SS2) },
+{ "Sun4c SparcStation ELC", (SM_SUN4C | SM_4C_ELC) },
+{ "Sun4c SparcStation IPX", (SM_SUN4C | SM_4C_IPX) },
+/* Finally, early Sun4m's */
+{ "Sun4m SparcSystem600", (SM_SUN4M | SM_4M_SS60) },
+{ "Sun4m SparcStation10", (SM_SUN4M | SM_4M_SS50) },
+{ "Sun4m SparcStation5", (SM_SUN4M | SM_4M_SS40) },
+/* One entry for the OBP arch's which are sun4d, sun4e, and newer sun4m's */
+{ "Sun4M OBP based system", (SM_SUN4M_OBP | 0x0) } };
-void get_idprom(void)
+void
+sparc_display_systype(unsigned char machtyp)
+{
+ char system_name[128];
+ int i;
+
+ for(i = 0; i<NUM_SUN_MACHINES; i++) {
+ if(Sun_Machines[i].id_machtype == machtyp) {
+ if(machtyp!=(SM_SUN4M_OBP | 0x0)) {
+ printk("TYPE: %s\n", Sun_Machines[i].name);
+ break;
+ } else {
+ prom_getproperty(prom_root_node, "banner-name",
+ system_name, sizeof(system_name));
+ printk("TYPE: %s\n", system_name);
+ break;
+ }
+ }
+ }
+ if(i == NUM_SUN_MACHINES)
+ printk("Uh oh, IDPROM had bogus id_machtype value <%x>\n", machtyp);
+ return;
+}
+
+void
+get_idprom(void)
{
- char* idp_addr;
- char* knl_idp_addr;
- int i;
+ prom_getidp((char *) &idprom_buff, sizeof(idprom_buff));
- idp_addr = (char *)IDPROM_ADDR;
- knl_idp_addr = (char *) &idprom;
+ idprom = &idprom_buff;
- for(i = 0; i<IDPROM_SIZE; i++)
- *knl_idp_addr++ = *idp_addr++;
+ sparc_display_systype(idprom->id_machtype);
- return;
+ printk("Ethernet address: %x:%x:%x:%x:%x:%x\n",
+ idprom->id_eaddr[0], idprom->id_eaddr[1], idprom->id_eaddr[2],
+ idprom->id_eaddr[3], idprom->id_eaddr[4], idprom->id_eaddr[5]);
+
+ return;
}
/* find_vac_size() returns the number of bytes in the VAC (virtual
int
find_vac_size(void)
{
- int vac_prop_len;
- int vacsize = 0;
- int node_root;
-
- node_root = (*(romvec->pv_nodeops->no_nextnode))(0);
-
- vac_prop_len = (*(romvec->pv_nodeops->no_proplen))(node_root, "vac-size");
-
- if(vac_prop_len != -1)
- {
- (*(romvec->pv_nodeops->no_getprop))(node_root, "vac-size", (char *) &vacsize);
- return vacsize;
- }
- else
- {
-
- /* The prom node functions can't help, do it via idprom struct */
- switch(idprom.id_machtype)
- {
- case 0x51:
- case 0x52:
- case 0x53:
- case 0x54:
- case 0x55:
- case 0x56:
- case 0x57:
- return 65536;
- default:
- return -1;
- }
- };
+ int vac_prop_len;
+ int vacsize = 0;
+
+ vac_prop_len = prom_getproplen(prom_root_node, "vac-size");
+ if(vac_prop_len != -1) {
+ vacsize = prom_getint(prom_root_node, "vac-size");
+ return vacsize;
+ } else {
+ switch(idprom->id_machtype) {
+ case (SM_SUN4C | SM_4C_SS1): /* SparcStation1 */
+ case (SM_SUN4C | SM_4C_IPC): /* SparcStation IPX */
+ case (SM_SUN4C | SM_4C_SS1PLUS): /* SparcStation1+ */
+ case (SM_SUN4C | SM_4C_SLC): /* SparcStation SLC */
+ case (SM_SUN4C | SM_4C_SS2): /* SparcStation2 Cache-Chip BUG! */
+ case (SM_SUN4C | SM_4C_ELC): /* SparcStation ELC */
+ case (SM_SUN4C | SM_4C_IPX): /* SparcStation IPX */
+ return 65536;
+ default:
+ printk("find_vac_size: Can't determine size of VAC, bailing out...\n");
+ halt();
+ break;
+ };
+ };
+ return -1;
}
/* find_vac_linesize() returns the size in bytes of the VAC linesize */
int
find_vac_linesize(void)
{
- int vac_prop_len;
- int vaclinesize = 0;
- int node_root;
-
- node_root = (*(romvec->pv_nodeops->no_nextnode))(0);
-
- vac_prop_len = (*(romvec->pv_nodeops->no_proplen))(node_root, "vac-linesize");
-
- if(vac_prop_len != -1)
- {
- (*(romvec->pv_nodeops->no_getprop))(node_root, "vac-linesize",
- (char *) &vaclinesize);
- return vaclinesize;
- }
- else
- {
-
- /* The prom node functions can't help, do it via idprom struct */
- switch(idprom.id_machtype)
- {
- case 0x51:
- case 0x52:
- case 0x53:
- case 0x54:
- return 16;
- case 0x55:
- case 0x56:
- case 0x57:
- return 32;
- default:
- return -1;
- }
- };
+ int vac_prop_len;
+
+ vac_prop_len = prom_getproplen(prom_root_node, "vac-linesize");
+
+ if(vac_prop_len != -1)
+ return prom_getint(prom_root_node, "vac-linesize");
+ else {
+ switch(idprom->id_machtype) {
+ case (SM_SUN4C | SM_4C_SS1): /* SparcStation1 */
+ case (SM_SUN4C | SM_4C_IPC): /* SparcStation IPC */
+ case (SM_SUN4C | SM_4C_SS1PLUS): /* SparcStation1+ */
+ case (SM_SUN4C | SM_4C_SLC): /* SparcStation SLC */
+ return 16;
+ case (SM_SUN4C | SM_4C_SS2): /* SparcStation2 Cache-Chip BUG! */
+ case (SM_SUN4C | SM_4C_ELC): /* SparcStation ELC */
+ case (SM_SUN4C | SM_4C_IPX): /* SparcStation IPX */
+ return 32;
+ default:
+ printk("find_vac_linesize: Can't determine VAC linesize, bailing out...\n");
+ halt();
+ break;
+ };
+ };
+ return -1;
}
int
find_vac_hwflushes(void)
{
- register int len, node_root;
- int tmp1, tmp2;
-
- node_root = (*(romvec->pv_nodeops->no_nextnode))(0);
-
- len = (*(romvec->pv_nodeops->no_proplen))(node_root, "vac_hwflush");
-
-#ifdef DEBUG_IDPROM
- printf("DEBUG: find_vac_hwflushes: proplen vac_hwflush=0x%x\n", len);
-#endif
-
- /* Sun 4/75 has typo in prom_node, it's a dash instead of an underscore
- * in the property name. :-(
- */
- len |= (*(romvec->pv_nodeops->no_proplen))(node_root, "vac-hwflush");
-
-#ifdef DEBUG_IDPROM
- printf("DEBUG: find_vac_hwflushes: proplen vac-hwflush=0x%x\n", len);
-#endif
-
- len = (*(romvec->pv_nodeops->no_getprop))(node_root,"vac_hwflush",
- (char *) &tmp1);
- if(len != 4) tmp1=0;
-
- len = (*(romvec->pv_nodeops->no_getprop))(node_root, "vac-hwflush",
- (char *) &tmp2);
- if(len != 4) tmp2=0;
-
-
- return (tmp1|tmp2);
-}
-
-void
-find_mmu_num_segmaps(void)
-{
- register int root_node, len;
-
- root_node = (*(romvec->pv_nodeops->no_nextnode))(0);
-
- len = (*(romvec->pv_nodeops->no_getprop))(root_node, "mmu-npmg",
- (char *) &num_segmaps);
-
-#ifdef DEBUG_MMU
- printf("find_mmu_num_segmaps: property length = %d\n", len);
-#endif
-
- if(len != 4) num_segmaps = 128;
-
- return;
-}
-
-void
-find_mmu_num_contexts(void)
-{
- register int root_node, len;
-
- root_node = (*(romvec->pv_nodeops->no_nextnode))(0);
-
- len = (*(romvec->pv_nodeops->no_getprop))(root_node, "mmu-nctx",
- (char *) &num_contexts);
+ register int len;
+ int tmp1, tmp2;
-#ifdef DEBUG_MMU
- printf("find_mmu_num_contexts: property length = %d\n", len);
-#endif
+ /* Sun 4/75 has typo in prom_node, it's a dash instead of an underscore
+ * in the property name. :-(
+ */
+ len = prom_getproperty(prom_root_node, "vac_hwflush",
+ (char *) &tmp1, sizeof(int));
+ if(len != 4) tmp1=0;
- if(len != 4) num_contexts = 8;
+ len = prom_getproperty(prom_root_node, "vac-hwflush",
+ (char *) &tmp2, sizeof(int));
+ if(len != 4) tmp2=0;
- return;
+ return (tmp1|tmp2);
}
-/* ioport.c: I/O access on the Sparc. Work in progress.. Most of the things
- * in this file are for the sole purpose of getting the kernel
- * through the compiler. :-)
+/* ioport.c: Simple io mapping allocator.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ *
+ * The routines in this file should be changed for a memory allocator
+ * that would be setup just like NetBSD does : you create regions that
+ * are administered by a general purpose allocator, and then you call
+ * that allocator with your handle and the block size instead of this
+ * weak stuff.
+ *
+ * XXX No joke, this needs to be rewritten badly. XXX
*/
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/ioport.h>
+
+#include <asm/io.h>
+#include <asm/vaddrs.h>
+#include <asm/oplib.h>
+
+/* This points to the next to use virtual memory for io mappings */
+static long next_free_region = IOBASE_VADDR;
+static long dvma_next_free = DVMA_VADDR;
+
+/*
+ * sparc_alloc_dev:
+ * Map and allocates an obio device.
+ * Implements a simple linear allocator, you can force the function
+ * to use your own mapping, but in practice this should not be used.
+ *
+ * Input:
+ * address: the obio address to map
+ * virtual: if non zero, specifies a fixed virtual address where
+ * the mapping should take place.
+ * len: the length of the mapping
+ * bus_type: The bus on which this io area sits.
+ *
+ * Returns:
+ * The virtual address where the mapping actually took place.
+ */
+
+void *sparc_alloc_io (void *address, void *virtual, int len, char *name,
+ int bus_type, int rdonly)
+{
+ unsigned long vaddr, base_address;
+ unsigned long addr = (unsigned long) address;
+
+
+ if (virtual){
+ vaddr = (unsigned long) virtual;
+ } else {
+ vaddr = next_free_region;
+ }
+
+ if (((unsigned long) virtual + len) > (IOBASE_VADDR + IOBASE_LEN)){
+ printk ("alloc_io: Mapping ouside IOBASE area\n");
+ prom_halt ();
+ }
+ if (check_region (vaddr, len)){
+ printk ("alloc_io: 0x%lx is already in use\n", vaddr);
+ prom_halt ();
+ }
+
+ /* Tell Linux resource manager about the mapping */
+ request_region (vaddr, len, name);
+
+ base_address = vaddr;
+ /* Do the actual mapping */
+ for (; len > 0; len -= PAGE_SIZE){
+ mapioaddr (addr, vaddr, bus_type, rdonly);
+ vaddr += PAGE_SIZE;
+ addr += PAGE_SIZE;
+ if (!virtual)
+ next_free_region += PAGE_SIZE;
+ }
+ return (void *) base_address;
+}
+
+/* Does DVMA allocations with PAGE_SIZE granulatity */
+void *sparc_dvma_malloc (int len, char *name)
+{
+ unsigned long vaddr, base_address;
+
+ vaddr = dvma_next_free;
+ if (check_region (vaddr, len)){
+ printk ("alloc_dma: 0x%lx is already in use\n", vaddr);
+ prom_halt ();
+ }
+ if (vaddr + len > (DVMA_VADDR + DVMA_LEN)){
+ printk ("alloc_dvma: out of dvma memory\n");
+ prom_halt ();
+ }
+
+ /* Tell Linux resource manager about the mapping */
+ request_region (vaddr, len, name);
+
+ base_address = vaddr;
+ /* Assign the memory area and remove the cache bit */
+ /* XXX EWWWWEE!! Sun4c specific, fix now! XXX */
+ for (; len > 0; len -= PAGE_SIZE){
+ printk ("Len=%d\n", len);
+ put_pte (vaddr, get_pte (vaddr) | PTE_NC);
+ vaddr += PAGE_SIZE;
+ dvma_next_free += PAGE_SIZE;
+ }
+ return (void *) base_address;
+}
* and you are supposed to probe the prom's device
* node trees to find out who's got which IRQ.
*
- * Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
- *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ * Copyright (C) 1995 Pete A. Zaitcev (zaitcev@jamica.lab.ipmce.su)
*/
/*
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
+
#include <asm/ptrace.h>
+#include <asm/processor.h>
#include <asm/system.h>
#include <asm/psr.h>
#include <asm/vaddrs.h>
-#include <asm/clock.h>
+#include <asm/timer.h>
#include <asm/openprom.h>
-
-#define DEBUG_IRQ
-
-void disable_irq(unsigned int irq_nr)
+#include <asm/oplib.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+
+/* Pointer to the interrupt enable byte */
+/* XXX Ugh, this is so sun4c specific it's driving me nuts. XXX */
+unsigned char *interrupt_enable = 0;
+struct sun4m_intregs *sun4m_interrupts;
+
+/* XXX Needs to handle Sun4m semantics XXX */
+void
+disable_irq(unsigned int irq_nr)
{
- unsigned long flags;
- unsigned char *int_reg;
-
- save_flags(flags);
- cli();
-
- /* We have mapped the irq enable register in head.S and all we
- * have to do here is frob the bits.
- */
-
- int_reg = (unsigned char *) IRQ_ENA_ADR;
-
- switch(irq_nr)
- {
- case 1:
- *int_reg = ((*int_reg) & (~(0x02)));
- break;
- case 4:
- *int_reg = ((*int_reg) & (~(0x04)));
- break;
- case 6:
- *int_reg = ((*int_reg) & (~(0x08)));
- break;
- case 8:
- *int_reg = ((*int_reg) & (~(0x10)));
- break;
- case 10:
- *int_reg = ((*int_reg) & (~(0x20)));
- break;
- case 14:
- *int_reg = ((*int_reg) & (~(0x80)));
- break;
- default:
- printk("AIEEE, Illegal interrupt disable requested irq=%d\n",
- (int) irq_nr);
- break;
- };
+ unsigned long flags;
+ unsigned char current_mask, new_mask;
+
+ if(sparc_cpu_model != sun4c) return;
+
+ save_flags(flags);
+ cli();
+
+ current_mask = *interrupt_enable;
+
+ switch(irq_nr) {
+ case 1:
+ new_mask = ((current_mask) & (~(SUN4C_INT_E1)));
+ break;
+ case 4:
+ new_mask = ((current_mask) & (~(SUN4C_INT_E4)));
+ break;
+ case 6:
+ new_mask = ((current_mask) & (~(SUN4C_INT_E6)));
+ break;
+ case 8:
+ new_mask = ((current_mask) & (~(SUN4C_INT_E8)));
+ break;
+ case 10:
+ new_mask = ((current_mask) & (~(SUN4C_INT_E10)));
+ break;
+ case 14:
+ new_mask = ((current_mask) & (~(SUN4C_INT_E14)));
+ break;
+ default:
+#if 0 /* Actually this is safe, as the floppy driver needs this */
+ printk("AIEEE, Illegal interrupt disable requested irq=%d\n",
+ (int) irq_nr);
+ prom_halt();
+#endif
+ break;
+ };
- restore_flags(flags);
- return;
+ restore_flags(flags);
+ return;
}
-void enable_irq(unsigned int irq_nr)
+/* XXX Needs to handle sun4m semantics XXX */
+void
+enable_irq(unsigned int irq_nr)
{
- unsigned long flags;
- unsigned char *int_reg;
-
- save_flags(flags);
- cli();
+ unsigned long flags;
+ unsigned char current_mask, new_mask;
+
+ if(sparc_cpu_model != sun4c) return;
+
+ save_flags(flags);
+ cli();
+
+ current_mask = *interrupt_enable;
+
+ switch(irq_nr) {
+ case 1:
+ new_mask = ((current_mask) | SUN4C_INT_E1);
+ break;
+ case 4:
+ new_mask = ((current_mask) | SUN4C_INT_E4);
+ break;
+ case 6:
+ new_mask = ((current_mask) | SUN4C_INT_E6);
+ break;
+ case 8:
+ new_mask = ((current_mask) | SUN4C_INT_E8);
+ break;
+ case 10:
+ new_mask = ((current_mask) | SUN4C_INT_E10);
+ break;
+ case 14:
+ new_mask = ((current_mask) | SUN4C_INT_E14);
+ break;
+ default:
+#if 0 /* Floppy driver does this on sun4c's anyhow */
+ printk ("Interrupt does not need to enable IE\n");
+ return;
+#endif
+ restore_flags(flags);
+ return;
+ };
- /* We have mapped the irq enable register in head.S and all we
- * have to do here is frob the bits.
- */
+ *interrupt_enable = new_mask;
- int_reg = (unsigned char *) IRQ_ENA_ADR;
-
-#ifdef DEBUG_IRQ
- printk(" --- Enabling IRQ level %d ---\n", irq_nr);
-#endif
+ restore_flags(flags);
- switch(irq_nr)
- {
- case 1:
- *int_reg = ((*int_reg) | 0x02);
- break;
- case 4:
- *int_reg = ((*int_reg) | 0x04);
- break;
- case 6:
- *int_reg = ((*int_reg) | 0x08);
- break;
- case 8:
- *int_reg = ((*int_reg) | 0x10);
- break;
- case 10:
- *int_reg = ((*int_reg) | 0x20);
- break;
- case 14:
- *int_reg = ((*int_reg) | 0x80);
- break;
- default:
- printk("AIEEE, Illegal interrupt enable requested irq=%d\n",
- (int) irq_nr);
- break;
- };
-
- restore_flags(flags);
-
- return;
+ return;
}
/*
* Initial irq handlers.
*/
struct irqaction {
- void (*handler)(int, struct pt_regs *);
- unsigned long flags;
- unsigned long mask;
- const char *name;
+ void (*handler)(int, struct pt_regs *);
+ unsigned long flags;
+ unsigned long mask;
+ const char *name;
};
static struct irqaction irq_action[16] = {
};
-int get_irq_list(char *buf)
+int
+get_irq_list(char *buf)
{
- int i, len = 0;
- struct irqaction * action = irq_action;
-
- for (i = 0 ; i < 16 ; i++, action++) {
- if (!action->handler)
- continue;
- len += sprintf(buf+len, "%2d: %8d %c %s\n",
- i, kstat.interrupts[i],
- (action->flags & SA_INTERRUPT) ? '+' : ' ',
- action->name);
- }
- return len;
+ int i, len = 0;
+ struct irqaction * action = irq_action;
+
+ for (i = 0 ; i < 16 ; i++, action++) {
+ if (!action->handler)
+ continue;
+ len += sprintf(buf+len, "%2d: %8d %c %s\n",
+ i, kstat.interrupts[i],
+ (action->flags & SA_INTERRUPT) ? '+' : ' ',
+ action->name);
+ }
+ return len;
}
-void free_irq(unsigned int irq)
+void
+free_irq(unsigned int irq)
{
struct irqaction * action = irq + irq_action;
unsigned long flags;
if (irq > 14) { /* 14 irq levels on the sparc */
- printk("Trying to free IRQ %d\n", irq);
+ printk("Trying to free bogus IRQ %d\n", irq);
return;
}
if (!action->handler) {
restore_flags(flags);
}
-#if 0
-static void handle_nmi(struct pt_regs * regs)
-{
- printk("NMI, probably due to bus-parity error.\n");
- printk("PC=%08lx, SP=%08lx\n", regs->pc, regs->sp);
-}
-#endif
-
-void unexpected_irq(int irq, struct pt_regs * regs)
+void
+unexpected_irq(int irq, struct pt_regs * regs)
{
int i;
printk("IO device interrupt, irq = %d\n", irq);
- printk("PC = %08lx NPC = %08lx SP=%08lx\n", regs->pc,
- regs->npc, regs->sp);
+ printk("PC = %08lx NPC = %08lx FP=%08lx\n", regs->pc,
+ regs->npc, regs->u_regs[14]);
printk("Expecting: ");
for (i = 0; i < 16; i++)
if (irq_action[i].handler)
- printk("[%s:%d] ", irq_action[i].name, i);
+ printk("[%s:%d:0x%x] ", irq_action[i].name, (int) i,
+ (unsigned int) irq_action[i].handler);
printk("AIEEE\n");
+ prom_halt();
}
-static inline void handler_irq(int irq, struct pt_regs * regs)
+void
+handler_irq(int irq, struct pt_regs * regs)
{
- struct irqaction * action = irq + irq_action;
+ struct irqaction * action = irq_action + irq;
- if (!action->handler) {
- unexpected_irq(irq, regs);
- return;
- }
- action->handler(irq, regs);
+ if (!action->handler) {
+ unexpected_irq(irq, regs);
+ return;
+ }
+ action->handler(irq, regs);
}
/*
* IRQ's should use this format: notably the keyboard/timer
* routines.
*/
-asmlinkage void do_IRQ(int irq, struct pt_regs * regs)
-{
- struct irqaction *action = irq + irq_action;
-
- kstat.interrupts[irq]++;
- action->handler(irq, regs);
- return;
-}
-
-/*
- * Since we need to special things to clear up the clock chip around
- * the do_timer() call we have a special version of do_IRQ for the
- * level 14 interrupt which does these things.
- */
-
-asmlinkage void do_sparc_timer(int irq, struct pt_regs * regs)
+asmlinkage void
+do_IRQ(int irq, struct pt_regs * regs)
{
- struct irqaction *action = irq + irq_action;
- register volatile int clear;
-
- kstat.interrupts[irq]++;
+ struct irqaction *action = irq + irq_action;
- /* I do the following already in the entry code, better safe than
- * sorry for now. Reading the limit register clears the interrupt.
- */
- clear = TIMER_STRUCT->timer_limit14;
-
- action->handler(irq, regs);
- return;
+ kstat.interrupts[irq]++;
+ action->handler(irq, regs);
+ return;
}
/*
* stuff - the handler is also running with interrupts disabled unless
* it explicitly enables them later.
*/
-asmlinkage void do_fast_IRQ(int irq)
+asmlinkage void
+do_fast_IRQ(int irq)
{
- kstat.interrupts[irq]++;
- printk("Got FAST_IRQ number %04lx\n", (long unsigned int) irq);
- return;
+ kstat.interrupts[irq]++;
+ printk("Got FAST_IRQ number %04lx\n", (long unsigned int) irq);
+ return;
}
-extern int first_descent;
-extern void probe_clock(int);
+extern void probe_clock(void);
-int request_irq(unsigned int irq, void (*handler)(int, struct pt_regs *),
- unsigned long irqflags, const char * devname)
+int
+request_irq(unsigned int irq, void (*handler)(int, struct pt_regs *),
+ unsigned long irqflags, const char * devname)
{
- struct irqaction *action;
- unsigned long flags;
+ struct irqaction *action;
+ unsigned long flags;
+
+ if(irq > 14) /* Only levels 1-14 are valid on the Sparc. */
+ return -EINVAL;
+
+ /* i386 keyboard interrupt request, just return */
+ if(irq == 1) return 0;
- if(irq > 14) /* Only levels 1-14 are valid on the Sparc. */
- return -EINVAL;
+ /* sched_init() requesting the timer IRQ */
+ if(irq == 0) {
+ irq = 10;
+ }
- if(irq == 0) /* sched_init() requesting the timer IRQ */
- {
- irq = 14;
- probe_clock(first_descent);
- }
+ action = irq + irq_action;
- action = irq + irq_action;
+ if(action->handler)
+ return -EBUSY;
- if(action->handler)
- return -EBUSY;
+ if(!handler)
+ return -EINVAL;
- if(!handler)
- return -EINVAL;
+ save_flags(flags);
- save_flags(flags);
+ cli();
- cli();
+ action->handler = handler;
+ action->flags = irqflags;
+ action->mask = 0;
+ action->name = devname;
- action->handler = handler;
- action->flags = irqflags;
- action->mask = 0;
- action->name = devname;
+ enable_irq(irq);
- enable_irq(irq);
+ /* Init the timer/clocks if necessary. */
+ if(irq == 10) probe_clock();
- restore_flags(flags);
+ restore_flags(flags);
- return 0;
+ return 0;
}
-unsigned int probe_irq_on (void)
+void
+sun4c_init_IRQ(void)
{
- unsigned int irqs = 0;
-
- return irqs;
+ struct linux_prom_registers int_regs[2];
+ int ie_node;
+
+ ie_node = prom_searchsiblings (prom_getchild(prom_root_node),
+ "interrupt-enable");
+ if(ie_node == 0) {
+ printk("Cannot find /interrupt-enable node\n");
+ prom_halt();
+ }
+ /* Depending on the "address" property is bad news... */
+ prom_getproperty(ie_node, "reg", (char *) int_regs, sizeof(int_regs));
+ sparc_alloc_io(int_regs[0].phys_addr, (void *) INTREG_VADDR,
+ int_regs[0].reg_size, "sun4c_interrupts",
+ int_regs[0].which_io, 0x0);
+
+ interrupt_enable = (char *) INTREG_VADDR;
+
+ /* Default value, accept interrupts, but no one is actually active */
+ /* We also turn on level14 interrupts so PROM can run the console. */
+ *interrupt_enable = (SUN4C_INT_ENABLE | SUN4C_INT_E14);
+ sti(); /* As of NOW, L1-A works. Turn irq's on full-blast. */
+ return;
}
-int probe_irq_off (unsigned int irqs)
+void
+sun4m_init_IRQ(void)
{
- unsigned int i = 0;
+ int ie_node, i;
+
+ struct linux_prom_registers int_regs[PROMREG_MAX];
+ int num_regs;
+
+ cli();
+
+ if((ie_node = prom_searchsiblings(prom_getchild(prom_root_node), "obio")) == 0 ||
+ (ie_node = prom_getchild (ie_node)) == 0 ||
+ (ie_node = prom_searchsiblings (ie_node, "interrupt")) == 0)
+ {
+ printk("Cannot find /obio/interrupt node\n");
+ prom_halt();
+ }
+ num_regs = prom_getproperty(ie_node, "reg", (char *) int_regs,
+ sizeof(int_regs));
+ num_regs = (num_regs/sizeof(struct linux_prom_registers));
+
+ /* Apply the obio ranges to these registers. */
+ prom_apply_obio_ranges(int_regs, num_regs);
+
+ /* Map the interrupt registers for all possible cpus. */
+ sparc_alloc_io(int_regs[0].phys_addr, (void *) INTREG_VADDR,
+ PAGE_SIZE*NCPUS, "interrupts_percpu",
+ int_regs[0].which_io, 0x0);
+
+ /* Map the system interrupt control registers. */
+ sparc_alloc_io(int_regs[num_regs-1].phys_addr,
+ (void *) INTREG_VADDR+(NCPUS*PAGE_SIZE),
+ int_regs[num_regs-1].reg_size, "interrupts_system",
+ int_regs[num_regs-1].which_io, 0x0);
+
+ sun4m_interrupts = (struct sun4m_intregs *) INTREG_VADDR;
+
+#if 0
+ printk("Interrupt register dump...\n");
+
+ for(i=0; i<NCPUS; i++)
+ printk("cpu%d: tbt %08x\n", i,
+ sun4m_interrupts->cpu_intregs[i].tbt);
+
+ printk("Master tbt %08x\n", sun4m_interrupts->tbt);
+ printk("Master irqs %08x\n", sun4m_interrupts->irqs);
+ printk("Master set %08x\n", sun4m_interrupts->set);
+ printk("Master clear %08x\n", sun4m_interrupts->clear);
+ printk("Undirected ints taken by: %08x\n",
+ sun4m_interrupts->undirected_target);
+
+ prom_halt();
+#endif
+
+ sti();
- return i;
+ return;
}
-void init_IRQ(void)
+void
+init_IRQ(void)
{
- return;
+ switch(sparc_cpu_model) {
+ case sun4c:
+ sun4c_init_IRQ();
+ break;
+ case sun4m:
+ sun4m_init_IRQ();
+ break;
+ default:
+ printk("Cannot initialize IRQ's on this Sun machine...\n");
+ halt();
+ break;
+ };
+
+ return;
}
--- /dev/null
+/* mp.S: Multiprocessor low-level routines on the Sparc.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/cprefix.h>
+#include <asm/head.h>
+#include <asm/psr.h>
+#include <asm/asi.h>
+#include <asm/vaddrs.h>
+#include <asm/contregs.h>
+
+
+ .text
+ .align 4
+
+/* When we start up a cpu for the first time it enters this routine.
+ * This initializes the chip from whatever state the prom left it
+ * in and sets PIL in %psr to 15, no irqs.
+ */
+
+ .globl C_LABEL(sparc_cpu_startup)
+C_LABEL(sparc_cpu_startup):
+ /* Set up a sane %psr -- PIL<0xf> S<0x1> PS<0x1> CWP<0x0> */
+ set (PSR_PIL | PSR_S | PSR_PS), %g1
+ wr %g1, 0x0, %psr ! traps off though
+ WRITE_PAUSE
+
+ /* Our %wim is one behind CWP */
+ wr %g0, 0x2, %wim
+
+ rd %tbr, %g4
+ or %g0, 0x3, %g5
+ sll %g5, 20, %g5
+ and %g4, %g5, %g4 ! Mask cpu-id bits
+
+ /* Give ourselves a stack. */
+ set PERCPU_VADDR, %g1
+ add %g1, %g4, %g1
+ set PERCPU_KSTACK_OFFSET, %g5
+ add %g1, %g5, %g1
+ set 0x1000, %g5
+ add %g1, %g5, %g1 ! end of stack
+ sub %g1, (96+96+80), %g1 ! set up a frame
+ andn %g1, 0x7, %g1
+ or %g1, 0x0, %fp ! bottom of frame
+ add %fp, (96+80), %sp ! top of frame
+
+ /* Set up per-cpu trap table pointer. In actuality, the virtual
+ * address for the trap table on every cpu points to the same
+ * physical address, this virtual address is only used for cpu
+ * identification purposes.
+ */
+#if 0
+/* set PERCPU_VADDR, %g1 */
+/* add %g1, %g4, %g1 */
+/* add %g1, PERCPU_TBR_OFFSET, %g1 */
+ set C_LABEL(thiscpus_tbr), %g1
+ ld [%g1], %g1
+ wr %g1, 0x0, %tbr
+ WRITE_PAUSE
+#else
+ set C_LABEL(trapbase), %g3
+ wr %g3, 0x0, %tbr
+ WRITE_PAUSE
+#endif
+
+ /* Turn on traps (PSR_ET). */
+ rd %psr, %g1
+ wr %g1, PSR_ET, %psr ! traps on
+
+#if 0
+1: nop
+ b 1b
+ nop
+#endif
+
+ /* Call C-code to do the rest of the real work. */
+ call C_LABEL(sparc_cpu_init)
+ nop
+
+ /* Call cpu-idle routine so we can start it up later on. */
+ call C_LABEL(sparc_cpu_idle)
+ nop
+
+ /* Done... This cpu should me spinning in a test loop.
+ * If execution gets here, something really bad happened.
+ */
+ call C_LABEL(prom_halt) ! Seems reasonable...
+ nop
+
/* probe.c: Preliminary device tree probing routines...
-
- Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
-*/
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
#include <linux/string.h>
+
+#include <asm/oplib.h>
#include <asm/vac-ops.h>
+#include <asm/idprom.h>
#include <asm/io.h>
#include <asm/vaddrs.h>
#include <asm/param.h>
-#include <asm/clock.h>
+#include <asm/timer.h>
+#include <asm/mostek.h>
+#include <asm/auxio.h>
#include <asm/system.h>
+#include <asm/mp.h>
+#include <asm/mbus.h>
/* #define DEBUG_PROBING */
-char promstr_buf[64]; /* overkill */
-unsigned int promint_buf[1];
-
-extern int prom_node_root;
-extern int num_segmaps, num_contexts;
-
-extern int node_get_sibling(int node);
-extern int node_get_child(int node);
-extern char* get_str_from_prom(int node, char* name, char* value);
-extern unsigned int* get_int_from_prom(int node, char* name, unsigned int *value);
+/* XXX Grrr, this stuff should have it's own file, only generic stuff goes
+ * XXX here. Possibly clock.c and timer.c?
+ */
+enum sparc_clock_type sp_clock_typ;
+struct mostek48t02 *mstk48t02_regs;
+struct mostek48t08 *mstk48t08_regs;
+volatile unsigned int *master_l10_limit = 0;
+struct sun4m_timer_regs *sun4m_timers;
-int first_descent;
+static char node_str[128];
/* Cpu-type information and manufacturer strings */
-
struct cpu_iu_info {
int psr_impl;
int psr_vers;
char* fp_name;
};
+/* In order to get the fpu type correct, you need to take the IDPROM's
+ * machine type value into consideration too. I will fix this.
+ */
struct cpu_fp_info linux_sparc_fpu[] = {
{ 0, 0, "Fujitsu MB86910 or Weitek WTL1164/5"},
- { 0, 1, "Fujitsu MB86911 or Weitek WTL1164/5"},
+ { 0, 1, "Fujitsu MB86911 or Weitek WTL1164/5 or LSI L64831"},
{ 0, 2, "LSI Logic L64802 or Texas Instruments ACT8847"},
+ /* SparcStation SLC, SparcStation1 */
{ 0, 3, "Weitek WTL3170/2"},
- { 0, 4, "Lsi Logic/Meiko L64804"},
+ /* SPARCstation-5 */
+ { 0, 4, "Lsi Logic/Meiko L64804 or compatible"},
{ 0, 5, "reserved"},
{ 0, 6, "reserved"},
{ 0, 7, "No FPU"},
- { 1, 0, "Lsi Logic L64812 or Texas Instruments ACT8847"},
+ { 1, 0, "ROSS HyperSparc combined IU/FPU"},
{ 1, 1, "Lsi Logic L64814"},
{ 1, 2, "Texas Instruments TMS390-C602A"},
- { 1, 3, "Weitek WTL3171"},
+ { 1, 3, "Cypress CY7C602 FPU"},
{ 1, 4, "reserved"},
{ 1, 5, "reserved"},
{ 1, 6, "reserved"},
{ 2, 1, "reserved"},
{ 2, 2, "reserved"},
{ 2, 3, "reserved"},
- { 2, 4, "reserved"},
+ { 2, 4, "reserved"},
{ 2, 5, "reserved"},
{ 2, 6, "reserved"},
{ 2, 7, "No FPU"},
+ /* SuperSparc 50 module */
+ { 4, 0, "SuperSparc on-chip FPU"},
+ /* SparcClassic */
+ { 4, 4, "TI MicroSparc on chip FPU"},
{ 5, 0, "Matsushita MN10501"},
{ 5, 1, "reserved"},
{ 5, 2, "reserved"},
{ 5, 7, "No FPU"},
};
+#define NSPARCFPU (sizeof(linux_sparc_fpu)/sizeof(struct cpu_fp_info))
+
struct cpu_iu_info linux_sparc_chips[] = {
- { 0, 0, "Fujitsu Microelectronics, Inc. - MB86900/1A"},
- { 1, 0, "Cypress CY7C601"},
- { 1, 1, "LSI Logic Corporation - L64811"},
- { 1, 3, "Cypress CY7C611"},
+ /* Sun4/100, 4/200, SLC */
+ { 0, 0, "Fujitsu MB86900/1A or LSI L64831 SparcKIT-40"},
+ /* borned STP1012PGA */
+ { 0, 4, "Fujitsu MB86904"},
+ /* SparcStation2, SparcServer 490 & 690 */
+ { 1, 0, "LSI Logic Corporation - L64811"},
+ /* SparcStation2 */
+ { 1, 1, "Cypress/ROSS CY7C601"},
+ /* Embedded controller */
+ { 1, 3, "Cypress/ROSS CY7C611"},
+ /* Ross Technologies HyperSparc */
+ { 1, 0xf, "ROSS HyperSparc RT620"},
+ { 1, 0xe, "ROSS HyperSparc RT625"},
+ /* ECL Implementation, CRAY S-MP Supercomputer... AIEEE! */
+ /* Someone please write the code to support this beast! ;) */
{ 2, 0, "Bipolar Integrated Technology - B5010"},
{ 3, 0, "LSI Logic Corporation - unknown-type"},
- { 4, 0, "Texas Instruments, Inc. - unknown"},
- { 4, 1, "Texas Instruments, Inc. - Sparc Classic"},
- { 4, 2, "Texas Instruments, Inc. - unknown"},
- { 4, 3, "Texas Instruments, Inc. - unknown"},
- { 4, 4, "Texas Instruments, Inc. - unknown"},
+ { 4, 0, "Texas Instruments, Inc. - SuperSparc 50"},
+ /* SparcClassic -- borned STP1010TAB-50*/
+ { 4, 1, "Texas Instruments, Inc. - MicroSparc"},
+ { 4, 2, "Texas Instruments, Inc. - MicroSparc II"},
+ { 4, 3, "Texas Instruments, Inc. - SuperSparc 51"},
+ { 4, 4, "Texas Instruments, Inc. - SuperSparc 61"},
{ 4, 5, "Texas Instruments, Inc. - unknown"},
{ 5, 0, "Matsushita - MN10501"},
{ 6, 0, "Philips Corporation - unknown"},
{ 7, 0, "Harvest VLSI Design Center, Inc. - unknown"},
+ /* Gallium arsenide 200MHz, BOOOOGOOOOMIPS!!! */
{ 8, 0, "Systems and Processes Engineering Corporation (SPEC)"},
{ 9, 0, "UNKNOWN CPU-VENDOR/TYPE"},
{ 0xa, 0, "UNKNOWN CPU-VENDOR/TYPE"},
{ 0xf, 0, "UNKNOWN CPU-VENDOR/TYPE"},
};
-char *sparc_cpu_type = "cpu-oops";
-char *sparc_fpu_type = "fpu-oops";
+#define NSPARCCHIPS (sizeof(linux_sparc_chips)/sizeof(struct cpu_iu_info))
+
+char *sparc_cpu_type[NCPUS] = { "cpu-oops", "cpu-oops1", "cpu-oops2", "cpu-oops3" };
+char *sparc_fpu_type[NCPUS] = { "fpu-oops", "fpu-oops1", "fpu-oops2", "fpu-oops3" };
/* various Virtual Address Cache parameters we find at boot time... */
extern int find_vac_size(void);
extern int find_vac_linesize(void);
extern int find_vac_hwflushes(void);
-extern void find_mmu_num_segmaps(void);
-extern void find_mmu_num_contexts(void);
+
+static inline int find_mmu_num_contexts(int cpu)
+{
+ return prom_getintdefault(cpu, "mmu-nctx", 0x8);
+}
+
+unsigned int fsr_storage;
void
probe_cpu(void)
{
- register int psr_impl=0;
- register int psr_vers = 0;
- register int fpu_vers = 0;
- register int i = 0;
- unsigned int tmp_fsr;
-
- &tmp_fsr; /* GCC grrr... */
-
- __asm__("rd %%psr, %0\n\t"
- "mov %0, %1\n\t"
- "srl %0, 28, %0\n\t"
- "srl %1, 24, %1\n\t"
- "and %0, 0xf, %0\n\t"
- "and %1, 0xf, %1\n\t" :
- "=r" (psr_impl),
- "=r" (psr_vers) :
- "0" (psr_impl),
- "1" (psr_vers));
-
-
- __asm__("st %%fsr, %1\n\t"
- "ld %1, %0\n\t"
- "srl %0, 17, %0\n\t"
- "and %0, 0x7, %0\n\t" :
- "=r" (fpu_vers),
- "=m" (tmp_fsr) :
- "0" (fpu_vers),
- "1" (tmp_fsr));
-
- printk("fpu_vers: %d ", fpu_vers);
- printk("psr_impl: %d ", psr_impl);
- printk("psr_vers: %d \n\n", psr_vers);
-
- for(i = 0; i<23; i++)
+ int psr_impl, psr_vers, fpu_vers;
+ int i, cpuid;
+
+ cpuid = get_cpuid();
+
+ psr_impl = ((get_psr()>>28)&0xf);
+ psr_vers = ((get_psr()>>24)&0xf);
+
+ fpu_vers = ((get_fsr()>>17)&0x7);
+
+ for(i = 0; i<NSPARCCHIPS; i++)
{
if(linux_sparc_chips[i].psr_impl == psr_impl)
if(linux_sparc_chips[i].psr_vers == psr_vers)
{
- sparc_cpu_type = linux_sparc_chips[i].cpu_name;
+ sparc_cpu_type[cpuid] = linux_sparc_chips[i].cpu_name;
break;
}
}
- if(i==23)
+ if(i==NSPARCCHIPS)
{
printk("No CPU type! You lose\n");
printk("DEBUG: psr.impl = 0x%x psr.vers = 0x%x\n", psr_impl,
psr_vers);
- return;
+ printk("Send this information and the type of SUN and CPU/FPU type you have\n");
+ printk("to davem@caip.rutgers.edu so that this can be fixed.\n");
+ printk("halting...\n\r\n");
+ /* If I don't know about this CPU, I don't want people running on it
+ * until I do.....
+ */
+ halt();
}
- for(i = 0; i<32; i++)
+ for(i = 0; i<NSPARCFPU; i++)
{
if(linux_sparc_fpu[i].psr_impl == psr_impl)
if(linux_sparc_fpu[i].fp_vers == fpu_vers)
{
- sparc_fpu_type = linux_sparc_fpu[i].fp_name;
+ sparc_fpu_type[cpuid] = linux_sparc_fpu[i].fp_name;
break;
}
}
- if(i == 32)
+ if(i == NSPARCFPU)
{
printk("No FPU type! You don't completely lose though...\n");
printk("DEBUG: psr.impl = 0x%x fsr.vers = 0x%x\n", psr_impl, fpu_vers);
- sparc_fpu_type = linux_sparc_fpu[31].fp_name;
+ printk("Send this information and the type of SUN and CPU/FPU type you have\n");
+ printk("to davem@caip.rutgers.edu so that this can be fixed.\n");
+ sparc_fpu_type[cpuid] = linux_sparc_fpu[31].fp_name;
}
- printk("CPU: %s \n", sparc_cpu_type);
- printk("FPU: %s \n", sparc_fpu_type);
+ printk("cpu%d CPU: %s \n", cpuid, sparc_cpu_type[cpuid]);
+ printk("cpu%d FPU: %s \n", cpuid, sparc_fpu_type[cpuid]);
return;
}
void
probe_vac(void)
{
- register unsigned int x,y;
-
-#ifndef CONFIG_SRMMU
- vac_size = find_vac_size();
- vac_linesize = find_vac_linesize();
- vac_do_hw_vac_flushes = find_vac_hwflushes();
-
- /* Calculate various constants that make the cache-flushing code
- * mode speedy.
- */
-
- vac_entries_per_segment = vac_entries_per_context = vac_size >> 12;
-
- for(x=0,y=vac_linesize; ((1<<x)<y); x++);
- if((1<<x) != vac_linesize) printk("Warning BOGUS VAC linesize 0x%x",
- vac_size);
-
- vac_entries_per_page = x;
-
- printk("Sparc VAC cache: Size=%d bytes Line-Size=%d bytes ... ", vac_size,
- vac_linesize);
-
- /* Here we want to 'invalidate' all the software VAC "tags"
- * just in case there is garbage in there. Then we enable it.
- */
-
- for(x=0x80000000, y=(x+vac_size); x<y; x+=vac_linesize)
- __asm__("sta %0, [%1] %2" : : "r" (0), "r" (x), "n" (0x2));
-
- x=enable_vac();
- printk("ENABLED\n");
-#endif
-
- return;
+ unsigned int x,y;
+
+ vac_size = find_vac_size();
+ vac_linesize = find_vac_linesize();
+ vac_do_hw_vac_flushes = find_vac_hwflushes();
+
+ /* Calculate various constants that make the cache-flushing code
+ * mode speedy.
+ */
+
+ vac_entries_per_segment = vac_entries_per_context = vac_size >> 12;
+ for(x=0,y=vac_linesize; ((1<<x)<y); x++);
+ if((1<<x) != vac_linesize) printk("Warning BOGUS VAC linesize 0x%x",
+ vac_size);
+ vac_entries_per_page = x;
+
+ /* Here we want to 'invalidate' all the software VAC "tags"
+ * just in case there is garbage in there. Then we enable it.
+ */
+
+ /* flush flush flush */
+ for(x=AC_CACHETAGS; x<(AC_CACHETAGS+vac_size); x+=vac_linesize)
+ __asm__("sta %%g0, [%0] %1" : : "r" (x), "i" (0x2));
+ x=enable_vac();
+ return;
}
+extern int num_segmaps, num_contexts;
+
void
-probe_mmu(void)
+probe_mmu(int prom_node_cpu)
{
- find_mmu_num_segmaps();
- find_mmu_num_contexts();
-
- printk("MMU segmaps: %d MMU contexts: %d\n", num_segmaps,
- num_contexts);
-
- return;
+ int cpuid;
+
+ /* who are we? */
+ cpuid = get_cpuid();
+ switch(sparc_cpu_model) {
+ case sun4:
+ break;
+ case sun4c:
+ /* A sun4, sun4c. */
+ num_segmaps = prom_getintdefault(prom_node_cpu, "mmu-npmg", 128);
+ num_contexts = find_mmu_num_contexts(prom_node_cpu);
+
+ printk("cpu%d MMU segmaps: %d MMU contexts: %d\n", cpuid,
+ num_segmaps, num_contexts);
+ break;
+ case sun4m:
+ case sun4d:
+ case sun4e:
+ /* Any Reference MMU derivate should be handled here, hopefuly. */
+ num_segmaps = 0;
+ num_contexts = find_mmu_num_contexts(prom_node_cpu);
+ vac_linesize = prom_getint(prom_node_cpu, "cache-line-size");
+ vac_size = (prom_getint(prom_node_cpu, "cache-nlines")*vac_linesize);
+ printk("cpu%d MMU contexts: %d\n", cpuid, num_contexts);
+ printk("cpu%d CACHE linesize %d total size %d\n", cpuid, vac_linesize,
+ vac_size);
+ break;
+ default:
+ printk("cpu%d probe_mmu: sparc_cpu_model botch\n", cpuid);
+ break;
+ };
+
+ return;
}
+/* Clock probing, we probe the timers here also. */
+/* #define DEBUG_PROBE_CLOCK */
+volatile unsigned int foo_limit;
+
void
probe_clock(int fchild)
{
register int node, type;
- register char *node_str;
+ struct linux_prom_registers clk_reg[2];
/* This will basically traverse the node-tree of the prom to see
* which timer chip is on this machine.
*/
- printk("Probing timer chip... ");
-
- type = 0;
- for(node = fchild ; ; )
- {
- node_str = get_str_from_prom(node, "model", promstr_buf);
- if(strcmp(node_str, "mk48t02") == 0)
- {
- type = 2;
- break;
- }
-
- if(strcmp(node_str, "mk48t08") == 0)
- {
- type = 8;
- break;
- }
-
- node = node_get_sibling(node);
- if(node == fchild)
- {
- printk("Aieee, could not find timer chip type\n");
+ node = 0;
+ if(sparc_cpu_model == sun4) {
+ printk("probe_clock: No SUN4 Clock/Timer support yet...\n");
return;
- }
- }
-
- printk("Mostek %s\n", node_str);
- printk("At OBIO address: 0x%x Virtual address: 0x%x\n",
- (unsigned int) TIMER_PHYSADDR, (unsigned int) TIMER_STRUCT);
-
- mapioaddr((unsigned long) TIMER_PHYSADDR,
- (unsigned long) TIMER_STRUCT);
-
- TIMER_STRUCT->timer_limit14=(((1000000/HZ) << 10) | 0x80000000);
+ }
+ if(sparc_cpu_model == sun4c) node=prom_getchild(prom_root_node);
+ else
+ if(sparc_cpu_model == sun4m)
+ node=prom_getchild(prom_searchsiblings(prom_getchild(prom_root_node), "obio"));
+ type = 0;
+ sp_clock_typ = MSTK_INVALID;
+ for(;;) {
+ prom_getstring(node, "model", node_str, sizeof(node_str));
+ if(strcmp(node_str, "mk48t02") == 0) {
+ sp_clock_typ = MSTK48T02;
+ if(prom_getproperty(node, "reg", (char *) clk_reg, sizeof(clk_reg)) == -1) {
+ printk("probe_clock: FAILED!\n");
+ halt();
+ }
+ prom_apply_obio_ranges(clk_reg, 1);
+ /* Map the clock register io area read-only */
+ mstk48t02_regs = (struct mostek48t02 *)
+ sparc_alloc_io((void *) clk_reg[0].phys_addr,
+ (void *) 0, sizeof(*mstk48t02_regs),
+ "clock", 0x0, 0x1);
+ mstk48t08_regs = 0; /* To catch weirdness */
+ break;
+ }
- return;
-}
+ if(strcmp(node_str, "mk48t08") == 0) {
+ sp_clock_typ = MSTK48T08;
+ if(prom_getproperty(node, "reg", (char *) clk_reg,
+ sizeof(clk_reg)) == -1) {
+ printk("probe_clock: FAILED!\n");
+ halt();
+ }
+ prom_apply_obio_ranges(clk_reg, 1);
+ /* Map the clock register io area read-only */
+ mstk48t08_regs = (struct mostek48t08 *)
+ sparc_alloc_io((void *) clk_reg[0].phys_addr,
+ (void *) 0, sizeof(*mstk48t08_regs),
+ "clock", 0x0, 0x1);
+
+ mstk48t02_regs = &mstk48t08_regs->regs;
+ break;
+ }
+ node = prom_getsibling(node);
+ if(node == 0) {
+ printk("Aieee, could not find timer chip type\n");
+ return;
+ }
+ }
-void
-probe_esp(register int esp_node)
-{
- register int nd;
- register char* lbuf;
+ if(sparc_cpu_model == sun4c) {
- nd = node_get_child(esp_node);
+ /* Map the Timer chip, this is implemented in hardware inside
+ * the cache chip on the sun4c.
+ */
+ sparc_alloc_io ((void *) SUN4C_TIMER_PHYSADDR, (void *) TIMER_VADDR,
+ sizeof (*SUN4C_TIMER_STRUCT), "timer", 0x0, 0x0);
- printk("\nProbing ESP:\n");
- lbuf = get_str_from_prom(nd, "name", promstr_buf);
+ /* Have the level 10 timer tick at 100HZ. We don't touch the
+ * level 14 timer limit since we are letting the prom handle
+ * them until we have a real console driver so L1-A works.
+ */
+ SUN4C_TIMER_STRUCT->timer_limit10 = (((1000000/HZ) + 1) << 10);
+ master_l10_limit = &(SUN4C_TIMER_STRUCT->timer_limit10);
- if(*get_int_from_prom(nd, "name", promint_buf) != 0)
- printk("Node: 0x%x Name: %s\n", nd, lbuf);
+ } else {
+ int reg_count;
+ struct linux_prom_registers cnt_regs[PROMREG_MAX];
+ int obio_node, cnt_node;
+
+ cnt_node = 0;
+ if((obio_node =
+ prom_searchsiblings (prom_getchild(prom_root_node), "obio")) == 0 ||
+ (obio_node = prom_getchild (obio_node)) == 0 ||
+ (cnt_node = prom_searchsiblings (obio_node, "counter")) == 0) {
+ printk ("Cannot find /obio/counter node\n");
+ prom_halt ();
+ }
+ reg_count = prom_getproperty(cnt_node, "reg",
+ (void *) cnt_regs, sizeof(cnt_regs));
- while((nd = node_get_sibling(nd)) != 0) {
- lbuf = get_str_from_prom(nd, "name", promstr_buf);
- printk("Node: 0x%x Name: %s\n", nd, lbuf);
- }
+ reg_count = (reg_count/sizeof(struct linux_prom_registers));
- printk("\n");
+ /* Apply the obio ranges to the timer registers. */
+ prom_apply_obio_ranges(cnt_regs, reg_count);
- return;
-}
+ /* Map the per-cpu Counter registers. */
+ sparc_alloc_io(cnt_regs[0].phys_addr, (void *) TIMER_VADDR,
+ PAGE_SIZE*NCPUS, "counters_percpu", cnt_regs[0].which_io, 0x0);
-void
-probe_sbus(register int cpu_child_node)
-{
- register int nd, savend;
- register char* lbuf;
+ /* Map the system Counter register. */
+ sparc_alloc_io(cnt_regs[reg_count-1].phys_addr,
+ (void *) TIMER_VADDR+(NCPUS*PAGE_SIZE),
+ cnt_regs[reg_count-1].reg_size,
+ "counters_system", cnt_regs[reg_count-1].which_io, 0x0);
- nd = cpu_child_node;
- lbuf = (char *) 0;
+ sun4m_timers = (struct sun4m_timer_regs *) TIMER_VADDR;
- while((nd = node_get_sibling(nd)) != 0) {
- lbuf = get_str_from_prom(nd, "name", promstr_buf);
- if(strcmp(lbuf, "sbus") == 0)
- break;
- };
+ foo_limit = (volatile) sun4m_timers->l10_timer_limit;
- nd = node_get_child(nd);
+/* printk("Timer register dump:\n"); */
+#if 0
+ printk("cpu0: l14limit %08x l14curcount %08x\n",
+ sun4m_timers->cpu_timers[0].l14_timer_limit,
+ sun4m_timers->cpu_timers[0].l14_cur_count);
+#endif
+#if 0
+ printk("sys: l10limit %08x l10curcount %08x\n",
+ sun4m_timers->l10_timer_limit,
+ sun4m_timers->l10_cur_count);
+#endif
- printk("Node: 0x%x Name: %s\n", nd,
- get_str_from_prom(nd, "name", promstr_buf));
+ /* Must set the master pointer first or we will lose badly. */
+ master_l10_limit =
+ &(((struct sun4m_timer_regs *)TIMER_VADDR)->l10_timer_limit);
- if(strcmp(lbuf, "esp") == 0) {
- probe_esp(nd);
- };
+ ((struct sun4m_timer_regs *)TIMER_VADDR)->l10_timer_limit =
+ (((1000000/HZ) + 1) << 10); /* 0x9c4000 */
- while((nd = node_get_sibling(nd)) != 0) {
- printk("Node: 0x%x Name: %s\n", nd,
- lbuf = get_str_from_prom(nd, "name", promstr_buf));
-
- if(strcmp(lbuf, "esp") == 0) {
- savend = nd;
- probe_esp(nd);
- nd = savend;
- };
- };
+ /* We should be getting level 10 interrupts now. */
+ }
- printk("\n");
return;
}
-extern unsigned long probe_memory(void);
-extern struct sparc_phys_banks sp_banks[14];
-unsigned int phys_bytes_of_ram, end_of_phys_memory;
-
+/* Probe and map in the Auxiliaary I/O register */
void
-probe_devices(void)
+probe_auxio(void)
{
- register int nd, i;
- register char* str;
+ int node, auxio_nd;
+ struct linux_prom_registers auxregs[1];
+
+ node = prom_getchild(prom_root_node);
+ auxio_nd = prom_searchsiblings(node, "auxiliary-io");
+ if(!auxio_nd) {
+ node = prom_searchsiblings(node, "obio");
+ node = prom_getchild(node);
+ auxio_nd = prom_searchsiblings(node, "auxio");
+ if(!auxio_nd) {
+ printk("Cannot find auxio node, cannot continue...\n");
+ prom_halt();
+ }
+ }
+ prom_getproperty(auxio_nd, "reg", (char *) auxregs, sizeof(auxregs));
+ prom_apply_obio_ranges(auxregs, 0x1);
+ /* Map the register both read and write */
+ sparc_alloc_io(auxregs[0].phys_addr, (void *) AUXIO_VADDR,
+ auxregs[0].reg_size, "auxilliaryIO", auxregs[0].which_io, 0x0);
+ printk("Mapped AUXIO at paddr %08lx vaddr %08lx\n",
+ (unsigned long) auxregs[0].phys_addr,
+ (unsigned long) AUXIO_VADDR);
+ return;
+}
- nd = prom_node_root;
+extern unsigned long probe_memory(void);
+extern struct sparc_phys_banks sp_banks[SPARC_PHYS_BANKS];
+unsigned int phys_bytes_of_ram, end_of_phys_memory;
+extern unsigned long probe_sbus(int, unsigned long);
+extern void probe_mbus(void);
- printk("PROBING DEVICES:\n");
+/* #define DEBUG_PROBE_DEVICES */
+struct prom_cpuinfo linux_cpus[NCPUS];
+int linux_num_cpus;
- str = get_str_from_prom(nd, "device_type", promstr_buf);
- if(strcmp(str, "cpu") == 0) {
- printk("Found CPU root prom device tree node.\n");
+unsigned long
+probe_devices(unsigned long mem_start)
+{
+ int nd, i, prom_node_cpu, thismid;
+ int cpu_nds[NCPUS]; /* One node for each cpu */
+ int cpu_ctr = 0;
+
+ prom_getstring(prom_root_node, "device_type", node_str, sizeof(node_str));
+ if(strcmp(node_str, "cpu") == 0) {
+ cpu_nds[0] = prom_root_node;
+ cpu_ctr++;
} else {
- printk("Root node in device tree was not 'cpu' cannot continue.\n");
- halt();
+ int scan;
+ scan = prom_getchild(prom_root_node);
+ nd = 0;
+ while((scan = prom_getsibling(scan)) != 0) {
+ prom_getstring(scan, "device_type", node_str, sizeof(node_str));
+ if(strcmp(node_str, "cpu") == 0) {
+ cpu_nds[cpu_ctr] = scan;
+ linux_cpus[cpu_ctr].prom_node = scan;
+ prom_getproperty(scan, "mid", (char *) &thismid, sizeof(thismid));
+ linux_cpus[cpu_ctr].mid = thismid;
+ cpu_ctr++;
+ }
+ };
+ if(cpu_ctr == 0) {
+ printk("No CPU nodes found, cannot continue.\n");
+ /* Probably a sun4d or sun4e, Sun is trying to trick us ;-) */
+ halt();
+ }
+ printk("Found %d CPU prom device tree node(s).\n", cpu_ctr);
};
+ prom_node_cpu = cpu_nds[0];
-#ifdef DEBUG_PROBING
- printk("String address for d_type: 0x%x\n", (unsigned int) str);
- printk("str[0] = %c str[1] = %c str[2] = %c \n", str[0], str[1], str[2]);
-#endif
-
- str = get_str_from_prom(nd, "name", promstr_buf);
-
-#ifdef DEBUG_PROBING
- printk("String address for name: 0x%x\n", (unsigned int) str);
- printk("str[0] = %c str[1] = %c str[2] = %c \n", str[0], str[1], str[2]);
-#endif
-
- printk("Name: %s \n", str);
-
- first_descent = nd = node_get_child(nd);
-
+ linux_num_cpus = cpu_ctr;
+ for(i=0; i<cpu_ctr; i++) {
+ prom_getstring(cpu_nds[i], "name", node_str, sizeof(node_str));
+ printk("cpu%d: %s \n", i, node_str);
+ }
/* Ok, here will go a call to each specific device probe. We can
* call these now that we have the 'root' node and the child of
*/
probe_cpu();
- probe_vac();
- probe_mmu();
- phys_bytes_of_ram = probe_memory();
-
- printk("Physical Memory: %d bytes\n", (int) phys_bytes_of_ram);
- for(i=0; sp_banks[i].num_bytes != 0; i++) {
- printk("Bank %d: base 0x%x bytes %d\n", i,
- (unsigned int) sp_banks[i].base_addr,
- (int) sp_banks[i].num_bytes);
- end_of_phys_memory = sp_banks[i].base_addr + sp_banks[i].num_bytes;
- }
-
- printk("PROM Root Child Node: 0x%x Name: %s \n", nd,
- get_str_from_prom(nd, "name", promstr_buf));
+ probe_auxio();
+ if(sparc_cpu_model == sun4c) probe_vac();
+ if(sparc_cpu_model != sun4c) probe_mbus(); /* Are MBUS's relevant on sun4c's? */
- while((nd = node_get_sibling(nd)) != 0) {
- printk("Node: 0x%x Name: %s", nd,
- get_str_from_prom(nd, "name", promstr_buf));
- printk("\n");
- };
-
- printk("\nProbing SBUS:\n");
- probe_sbus(first_descent);
+ mem_start = probe_sbus(prom_getchild(prom_root_node), mem_start);
- return;
+ return mem_start;
}
/*
- * linux/arch/i386/kernel/process.c
+ * linux/arch/sparc/kernel/process.c
*
- * Copyright (C) 1995 Linus Torvalds
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
*/
/*
#include <linux/user.h>
#include <linux/a.out.h>
+#include <asm/oplib.h>
#include <asm/segment.h>
#include <asm/system.h>
-
-void ret_from_sys_call(void) { __asm__("nop"); }
+#include <asm/processor.h>
/*
- * The idle loop on a i386..
+ * The idle loop on a sparc... ;)
*/
asmlinkage int sys_idle(void)
{
+
if (current->pid != 0)
return -EPERM;
+ printk("in sys_idle...\n");
/* Map out the low memory: it's no longer needed */
/* Sparc version RSN */
/* endless idle loop with no priority at all */
current->counter = -100;
for (;;) {
- schedule();
+ printk("calling schedule() aieee!\n");
+ schedule();
+ printk("schedule() returned, halting...\n");
+ halt();
}
}
void hard_reset_now(void)
{
- halt();
+ prom_reboot("boot vmlinux");
}
void show_regs(struct pt_regs * regs)
{
- printk("\nSP: %08lx PC: %08lx NPC: %08lx\n", regs->sp, regs->pc,
- regs->npc);
-}
-
-/*
- * Do necessary setup to start up a newly executed thread.
- */
-void start_thread(struct pt_regs * regs, unsigned long sp, unsigned long fp)
-{
- regs->sp = sp;
- regs->fp = fp;
+ printk("\nFP: %08lx PC: %08lx NPC: %08lx\n", regs->u_regs[14],
+ regs->pc, regs->npc);
}
/*
halt();
}
-void copy_thread(int nr, unsigned long clone_flags, unsigned long sp, struct task_struct * p, struct pt_regs * regs)
+extern void ret_sys_call(void);
+
+/*
+ * Copy a Sparc thread. The context of a process on the Sparc is
+ * composed of the following:
+ * 1) status registers %psr (for condition codes + CWP) and %wim
+ * 2) current register window (in's and global registers)
+ * 3) the current live stack frame, it contains the register
+ * windows the child may 'restore' into, this is important
+ * 4) kernel stack pointer, user stack pointer (which is %i6)
+ * 5) The pc and npc the child returns to during a switch
+ */
+
+void copy_thread(int nr, unsigned long clone_flags, unsigned long sp,
+ struct task_struct * p, struct pt_regs * regs)
{
- struct pt_regs * childregs;
+ struct pt_regs *childregs;
+ unsigned char *old_stack;
+ unsigned char *new_stack;
+ int i;
+
+ /* This process has no context yet. */
+ p->tss.context = -1;
+
+ /* Grrr, Sparc stack alignment restrictions make things difficult. */
+ childregs = ((struct pt_regs *)
+ ((p->kernel_stack_page + PAGE_SIZE - 80)&(~7)));
- childregs = ((struct pt_regs *) (p->kernel_stack_page + PAGE_SIZE)) - 1;
- p->tss.usp = (unsigned long) childregs;
*childregs = *regs;
- childregs->sp = sp;
- p->tss.psr = regs->psr; /* for condition codes */
+
+ p->tss.usp = sp; /* both processes have the same user stack */
+ /* See entry.S */
+
+ /* Allocate new processes kernel stack right under pt_regs.
+ * Hopefully this should align things the right way.
+ */
+ p->tss.ksp = (unsigned long) ((p->kernel_stack_page + PAGE_SIZE - 80 - 96)&(~7));
+ new_stack = (unsigned char *) (p->tss.ksp);
+ old_stack = (unsigned char *) (((unsigned long) regs) - 96);
+
+ /* Copy c-stack. */
+ for(i=0; i<96; i++) *new_stack++ = *old_stack++;
+
+ /* These pc values are only used when we switch to the child for
+ * the first time, it jumps the child to ret_sys_call in entry.S
+ * so that the child returns from the sys_call just like parent.
+ */
+ p->tss.pc = (((unsigned long) ret_sys_call) - 8);
+ p->tss.npc = p->tss.pc+4;
+
+ /* Set the return values for both the parent and the child */
+ regs->u_regs[8] = p->pid;
+ childregs->u_regs[8] = 0;
+
return;
}
return; /* solaris does this enough */
}
-asmlinkage int sys_fork(struct pt_regs regs)
+asmlinkage int sys_fork(struct pt_regs *regs)
{
- return do_fork(COPYVM | SIGCHLD, regs.sp, ®s);
+ return do_fork(COPYVM | SIGCHLD, regs->u_regs[14], regs);
}
/*
*/
asmlinkage int sys_execve(struct pt_regs regs)
{
+ printk("sys_execve()... halting\n");
halt();
return 0;
}
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/io.h>
-#include <asm/openprom.h> /* for console registration + cheese */
+#include <asm/processor.h>
+#include <asm/oplib.h> /* The PROM is your friend... */
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/traps.h>
+#include <asm/vaddrs.h>
+#include <asm/kdebug.h>
-extern void get_idprom(void);
-extern void probe_devices(void);
+extern unsigned long probe_devices(unsigned long);
/*
* Gcc is hard to keep happy ;-)
25 /* orig-video-lines */
};
-/* At least I hide the sneaky floppy_track_buffer in my dirty assembly
- * code. ;-)
- */
+char wp_works_ok = 0;
unsigned long bios32_init(unsigned long memory_start, unsigned long memory_end)
{
}
/* Lame prom console routines, gets registered below. Thanks for the
- * tip Linus. First comes the V0 prom routine, then the V3 version
- * written by Paul Hatchman (paul@sfe.com.au).
+ * tip Linus. We now use a generic putchar prom routine through the
+ * linux prom library.
*/
void sparc_console_print(const char * p)
while ((c = *(p++)) != 0)
{
- if (c == '\n') romvec->pv_putchar('\r');
- (*(romvec->pv_putchar))(c);
+ if (c == '\n')
+ prom_putchar('\r');
+ prom_putchar(c);
}
return;
}
-/* paul@sfe.com.au */
-/* V3 prom console printing routines */
-void sparc_console_print_v3 (const char *p)
+/* Typing sync at the prom promptcalls the function pointed to by
+ * romvec->pv_synchook which I set to the following function.
+ * This should sync all filesystems and return, for now it just
+ * prints out pretty messages and returns.
+ */
+void prom_sync_me(void)
{
- unsigned char c;
+ printk("PROM SYNC COMMAND...\n");
+ show_free_areas();
+ printk("Returning to prom\n");
+ return;
+}
- while ((c = *(p++)) != 0)
- {
- if (c == '\n') romvec->pv_v2devops.v2_dev_write
- ((*romvec->pv_v2bootargs.fd_stdout), "\r", 1);
- romvec->pv_v2devops.v2_dev_write
- ((*romvec->pv_v2bootargs.fd_stdout), &c, 1);
- }
+unsigned int boot_flags;
+#define BOOTME_DEBUG 0x1
+#define BOOTME_SINGLE 0x2
+#define BOOTME_KGDB 0x3
- return;
-}
+/* This routine does no error checking, make sure your string is sane
+ * before calling this!
+ * XXX This is cheese, make generic and better.
+ */
+void
+boot_flags_init(char *commands)
+{
+ int i;
+ for(i=0; i<strlen(commands); i++)
+ if(commands[i]=='-')
+ switch(commands[i+1]) {
+ case 'd':
+ boot_flags |= BOOTME_DEBUG;
+ break;
+ case 's':
+ boot_flags |= BOOTME_SINGLE;
+ break;
+ case 'h':
+ printk("boot_flags_init: Found halt flag, doing so now...\n");
+ halt();
+ break;
+ case 'k':
+ printk("Found KGDB boot flag...\n");
+ boot_flags |= BOOTME_KGDB;
+ break;
+ default:
+ printk("boot_flags_init: Unknown boot arg (-%c)\n",
+ commands[i+1]);
+ break;
+ };
+ return;
+}
/* This routine will in the future do all the nasty prom stuff
* to probe for the mmu type and its parameters, etc. This will
*/
extern void register_console(void (*proc)(const char *));
-extern unsigned int prom_iface_vers, end_of_phys_memory;
+extern void load_mmu(void);
+extern int prom_probe_memory(void);
+extern void probe_mmu(int node);
+extern void get_idprom(void);
+extern void srmmu_patch_fhandlers(void);
+extern unsigned int prom_iface_vers, end_of_phys_memory, phys_bytes_of_ram;
+extern char cputypval;
+extern unsigned long start;
+char sparc_command_line[256]; /* Should be enough */
+enum sparc_cpu sparc_cpu_model;
+
+struct tt_entry *sparc_ttable;
+
+/* #define DEBUG_CMDLINE */
void setup_arch(char **cmdline_p,
unsigned long * memory_start_p, unsigned long * memory_end_p)
{
- if(romvec->pv_romvers == 0) {
- register_console(sparc_console_print);
- } else {
- register_console(sparc_console_print_v3);
- };
+ int counter, total, i, node;
+ char devtype[64];
+
+ sparc_ttable = (struct tt_entry *) &start;
+
+ register_console(sparc_console_print);
+
+ /* Initialize PROM console and command line. */
+ *cmdline_p = prom_getbootargs();
+ boot_flags_init(*cmdline_p);
- printk("Sparc PROM-Console registered...\n");
- get_idprom(); /* probe_devices expects this to be done */
- probe_devices(); /* cpu/fpu, mmu probes */
+ /* Synchronize with debugger if necessary. Grrr, have to check
+ * the boot flags too. ;(
+ */
+ if((boot_flags&BOOTME_DEBUG) && (linux_dbvec!=0) &&
+ ((*(short *)linux_dbvec) != -1)) {
+ printk("Booted under debugger. Syncing up trap table.\n");
+ /* Sync us up... */
+ (*(linux_dbvec->teach_debugger))();
+ SP_ENTER_DEBUGGER;
+ }
+
+ /* Set sparc_cpu_model */
+ sparc_cpu_model = sun_unknown;
+ if(!strcmp(&cputypval,"sun4c")) { sparc_cpu_model=sun4c; }
+ if(!strcmp(&cputypval,"sun4m")) { sparc_cpu_model=sun4m; }
+ if(!strcmp(&cputypval,"sun4d")) { sparc_cpu_model=sun4d; }
+ if(!strcmp(&cputypval,"sun4e")) { sparc_cpu_model=sun4e; }
+ if(!strcmp(&cputypval,"sun4u")) { sparc_cpu_model=sun4u; }
+ printk("ARCH: ");
+ switch(sparc_cpu_model)
+ {
+ case sun4c:
+ memset(phys_seg_map, 0x0, sizeof(phys_seg_map[PSEG_ENTRIES]));
+ put_segmap(IOBASE_VADDR, IOBASE_SUN4C_SEGMAP);
+ phys_seg_map[IOBASE_SUN4C_SEGMAP] = PSEG_RSV;
+ node = prom_root_node;
+
+ printk("SUN4C\n");
+ break;
+ case sun4m:
+ node = prom_getchild(prom_root_node);
+ prom_getproperty(node, "device_type", devtype, sizeof(devtype));
+ while(strcmp(devtype, "cpu") != 0) {
+ node = prom_getsibling(node);
+ prom_getproperty(node, "device_type", devtype,
+ sizeof(devtype));
+ }
+ /* Patch trap table. */
+ srmmu_patch_fhandlers();
+ printk("SUN4M\n");
+ break;
+ case sun4d:
+ printk("SUN4D\n");
+ break;
+ case sun4e:
+ printk("SUN4E\n");
+ break;
+ case sun4u:
+ printk("SUN4U\n");
+ break;
+ default:
+ printk("UNKNOWN!\n");
+ break;
+ };
+
+ /* probe_devices() expects this to be done. */
+ get_idprom();
+
+ /* Probe the mmu constants. */
+ probe_mmu(node);
+
+ /* Set pointers to memory management routines. */
+ load_mmu();
+
+ /* Probe for memory. */
+ total = prom_probe_memory();
*memory_start_p = (((unsigned long) &end));
- *memory_end_p = (((unsigned long) end_of_phys_memory));
+
+ printk("Physical Memory: %d bytes (in hex %08lx)\n", (int) total,
+ (unsigned long) total);
+
+ for(i=0; sp_banks[i].num_bytes != 0; i++) {
+#if 0
+ printk("Bank %d: base 0x%x bytes %d\n", i,
+ (unsigned int) sp_banks[i].base_addr,
+ (int) sp_banks[i].num_bytes);
+#endif
+ end_of_phys_memory = sp_banks[i].base_addr + sp_banks[i].num_bytes;
+ }
+
+ /* Set prom sync hook pointer */
+ prom_setsync(prom_sync_me);
+
+ init_task.mm->start_code = PAGE_OFFSET;
+ init_task.mm->end_code = PAGE_OFFSET + (unsigned long) &etext;
+ init_task.mm->end_data = PAGE_OFFSET + (unsigned long) &edata;
+ init_task.mm->brk = PAGE_OFFSET + (unsigned long) &end;
+ init_task.mm->mmap->vm_page_prot = PAGE_SHARED;
+
+ /* Grrr, wish I knew why I have to do this ;-( */
+ for(counter=1; counter<NR_TASKS; counter++) {
+ task[counter] = NULL;
+ }
+
+ *memory_end_p = (((unsigned long) (total) + PAGE_OFFSET));
+
+ return;
}
asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on)
/*
* arch/sparc/kernel/traps.c
*
- * Copyright 1994 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright 1995 David S. Miller (davem@caip.rutgers.edu)
*/
/*
#include <linux/sched.h> /* for jiffies */
#include <linux/kernel.h>
-void do_hw_interrupt(unsigned long type, unsigned long vector)
-{
- if (vector == 14) {
- jiffies++;
- return;
- }
+#include <asm/delay.h>
+#include <asm/system.h>
+#include <asm/ptrace.h>
+#include <asm/oplib.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/mp.h>
+#include <asm/kdebug.h>
- /* Just print garbage for everything else for now. */
+void
+do_hw_interrupt(unsigned long type, unsigned long psr, unsigned long pc)
+{
- printk("Unimplemented Sparc TRAP, vector = %lx type = %lx\n", vector, type);
+ printk("Unimplemented Sparc TRAP, type = %02lx psr = %08lx pc = %08lx\n",
+ type, psr, pc);
+ halt();
return;
}
-extern unsigned long *trapbase;
+void
+do_illegal_instruction(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+ unsigned long psr)
+{
+ printk("Illegal instruction at PC %08lx NPC %08lx PSR %08lx\n",
+ pc, npc, psr);
+ halt();
+ return;
+}
+
+void
+do_priv_instruction(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+ unsigned long psr)
+{
+ printk("Privileged instruction at PC %08lx NPC %08lx PSR %08lx\n",
+ pc, npc, psr);
+ halt();
+ return;
+}
+
+void
+do_memaccess_unaligned(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+ unsigned long psr)
+{
+ printk("Unaligned memory access at PC %08lx NPC %08lx PSR %08lx\n",
+ pc, npc, psr);
+ halt();
+ return;
+}
+
+void
+do_fpd_trap(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+ unsigned long psr)
+{
+ printk("Floating Point Disabled trap at PC %08lx NPC %08lx PSR %08lx\n",
+ pc, npc, psr);
+ halt();
+ return;
+}
+
+void
+do_fpe_trap(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+ unsigned long psr)
+{
+ printk("Floating Point Exception at PC %08lx NPC %08lx PSR %08lx\n",
+ pc, npc, psr);
+ halt();
+ return;
+}
+
+void
+handle_tag_overflow(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+ unsigned long psr)
+{
+ printk("Tag overflow trap at PC %08lx NPC %08lx PSR %08lx\n",
+ pc, npc, psr);
+ halt();
+ return;
+}
+
+void
+handle_watchpoint(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+ unsigned long psr)
+{
+ printk("Watchpoint detected at PC %08lx NPC %08lx PSR %08lx\n",
+ pc, npc, psr);
+ halt();
+ return;
+}
+
+void
+handle_reg_access(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+ unsigned long psr)
+{
+ printk("Register Access Exception at PC %08lx NPC %08lx PSR %08lx\n",
+ pc, npc, psr);
+ halt();
+ return;
+}
+
+void
+handle_iacc_error(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+ unsigned long psr)
+{
+ printk("Instruction Access Error at PC %08lx NPC %08lx PSR %08lx\n",
+ pc, npc, psr);
+ halt();
+ return;
+}
+
+void
+handle_cp_disabled(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+ unsigned long psr)
+{
+ printk("Co-Processor disabled trap at PC %08lx NPC %08lx PSR %08lx\n",
+ pc, npc, psr);
+ halt();
+ return;
+}
+
+void
+handle_bad_flush(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+ unsigned long psr)
+{
+ printk("Unimplemented FLUSH Exception at PC %08lx NPC %08lx PSR %08lx\n",
+ pc, npc, psr);
+ halt();
+ return;
+}
+
+void
+handle_cp_exception(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+ unsigned long psr)
+{
+ printk("Co-Processor Exception at PC %08lx NPC %08lx PSR %08lx\n",
+ pc, npc, psr);
+ halt();
+ return;
+}
+
+void
+handle_dacc_error(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+ unsigned long psr)
+{
+ printk("Data Access Error at PC %08lx NPC %08lx PSR %08lx\n",
+ pc, npc, psr);
+ halt();
+ return;
+}
+
+void
+handle_hw_divzero(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+ unsigned long psr)
+{
+ printk("Divide By Zero Exception at PC %08lx NPC %08lx PSR %08lx\n",
+ pc, npc, psr);
+ halt();
+ return;
+}
+
+void
+handle_dstore_error(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+ unsigned long psr)
+{
+ printk("Data Store Error at PC %08lx NPC %08lx PSR %08lx\n",
+ pc, npc, psr);
+ halt();
+ return;
+}
+
+void
+handle_dacc_mmu_miss(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+ unsigned long psr)
+{
+ printk("Data Access MMU-Miss Exception at PC %08lx NPC %08lx PSR %08lx\n",
+ pc, npc, psr);
+ halt();
+ return;
+}
-void trap_init(void)
+void
+handle_iacc_mmu_miss(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+ unsigned long psr)
{
+ printk("Instruction Access MMU-Miss Exception at PC %08lx NPC %08lx PSR %08lx\n",
+ pc, npc, psr);
+ halt();
+ return;
+}
+
+/* Since we have our mappings set up, on multiprocessors we can spin them
+ * up here so that timer interrupts work during initialization.
+ */
+
+extern void sparc_cpu_startup(void);
- /* load up the trap table */
+extern int linux_num_cpus;
+extern pgd_t *lnx_root;
-#if 0 /* not yet */
- __asm__("wr %0, 0x0, %%tbr\n\t"
- "nop; nop; nop\n\t" : :
- "r" (trapbase));
+int linux_smp_still_initting;
+unsigned int thiscpus_tbr;
+int thiscpus_mid;
+
+/* #define SMP_TESTING */
+
+void
+trap_init(void)
+{
+ struct linux_prom_registers ctx_reg;
+ int i;
+
+ if(linux_num_cpus == 1) {
+ printk("trap_init: Uniprocessor detected.\n");
+ return;
+ }
+ if(sparc_cpu_model != sun4m) {
+ printk("trap_init: Multiprocessor on a non-sun4m! Aieee...\n");
+ printk("trap_init: Cannot continue, bailing out.\n");
+ prom_halt();
+ }
+ /* Ok, we are on a sun4m with multiple cpu's */
+ printk("trap_init: Multiprocessor detected, initiating CPU-startup. cpus=%d\n",
+ linux_num_cpus);
+ linux_smp_still_initting = 1;
+ ctx_reg.which_io = 0x0; /* real ram */
+ ctx_reg.phys_addr = (char *) (((unsigned long) lnx_root) - PAGE_OFFSET);
+ ctx_reg.reg_size = 0x0;
+ /* This basically takes every cpu, loads up our Linux context table
+ * into it's context table pointer register, inits it at the low level
+ * and then makes it spin in an endless loop...
+ */
+ for(i=0; i<linux_num_cpus; i++) {
+ if((linux_cpus[i].mid & (~8)) != 0x0) {
+ static int cpuid = 0;
+ cpuid = (linux_cpus[i].mid & (~8));
+ percpu_table[cpuid].cpu_is_alive = 0;
+ thiscpus_mid = linux_cpus[i].mid;
+ thiscpus_tbr = (unsigned int)
+ percpu_table[cpuid].trap_table;
+#ifdef SMP_TESTING
+ printk("thiscpus_tbr = %08x\n", thiscpus_tbr);
+ printk("About to fire up cpu %d mid %d cpuid %d\n", i,
+ linux_cpus[i].mid, cpuid);
#endif
+ prom_startcpu(linux_cpus[i].prom_node, &ctx_reg, 0x0,
+ (char *) sparc_cpu_startup);
+ printk("Waiting for cpu %d to start up...\n", i);
+ while(percpu_table[cpuid].cpu_is_alive == 0) {
+ static int counter = 0;
+ counter++;
+ if(counter>200) {
+#ifdef SMP_TESTING
+ printk("UGH, CPU would not start up ;-( \n");
+#endif
+ break;
+ }
+ __delay(200000);
+ }
+ }
+ }
- return;
+ linux_smp_still_initting = 1;
+
+ return;
}
-void die_if_kernel(char * str, struct pt_regs * regs, long err)
+void
+die_if_kernel(char * str, struct pt_regs * regs, long err)
{
return;
}
.c.s:
$(CC) $(CFLAGS) -S $<
-OBJS = fault.o vac-flush.o init.o
+OBJS = fault.o vac-flush.o init.o sun4c.o srmmu.o loadmmu.o
mm.o: $(OBJS)
$(LD) -r -o mm.o $(OBJS)
+/* fault.c: Page fault handlers for the Sparc.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
#include <linux/string.h>
#include <linux/types.h>
#include <linux/ptrace.h>
#include <asm/system.h>
#include <asm/segment.h>
#include <asm/openprom.h>
+#include <asm/idprom.h>
#include <asm/page.h>
#include <asm/pgtable.h>
+#include <asm/memreg.h>
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/kdebug.h>
-extern unsigned long pg0[1024]; /* page table for 0-4MB for everybody */
-extern struct sparc_phys_banks sp_banks[14];
+#define ELEMENTS(arr) (sizeof (arr)/sizeof (arr[0]))
+
+extern struct sparc_phys_banks sp_banks[SPARC_PHYS_BANKS];
+extern int prom_node_root;
extern void die_if_kernel(char *,struct pt_regs *,long);
*/
#undef CONFIG_TEST_VERIFY_AREA
-/* Traverse the memory lists in the prom to see how much physical we
- * have.
- */
-
-unsigned long
-probe_memory(void)
+/* Nice, simple, prom library does all the sweating for us. ;) */
+int prom_probe_memory (void)
{
- register struct linux_romvec *lprom;
register struct linux_mlist_v0 *mlist;
register unsigned long bytes, base_paddr, tally;
register int i;
- bytes = tally = 0;
- base_paddr = 0;
- i=0;
- lprom = romvec;
- switch(lprom->pv_romvers)
- {
- case 0:
- mlist=(*(lprom->pv_v0mem.v0_totphys));
- bytes = tally = mlist->num_bytes;
- base_paddr = (unsigned long) mlist->start_adr;
-
- sp_banks[0].base_addr = base_paddr;
- sp_banks[0].num_bytes = bytes;
-
- if(mlist->theres_more != (void *)0) {
- i++;
- mlist=mlist->theres_more;
- bytes=mlist->num_bytes;
- tally += bytes;
- sp_banks[i].base_addr = (unsigned long) mlist->start_adr;
- sp_banks[i].num_bytes = mlist->num_bytes;
- }
- break;
- case 2:
- printk("no v2 memory probe support yet.\n");
- (*(lprom->pv_halt))();
+ i = 0;
+ mlist= *prom_meminfo()->v0_available;
+ bytes = tally = mlist->num_bytes;
+ base_paddr = (unsigned long) mlist->start_adr;
+
+ sp_banks[0].base_addr = base_paddr;
+ sp_banks[0].num_bytes = bytes;
+
+ while (mlist->theres_more != (void *) 0){
+ i++;
+ mlist = mlist->theres_more;
+ bytes = mlist->num_bytes;
+ tally += bytes;
+ if (i >= SPARC_PHYS_BANKS-1) {
+ printk ("The machine has more banks that this kernel can support\n"
+ "Increase the SPARC_PHYS_BANKS setting (currently %d)\n",
+ SPARC_PHYS_BANKS);
+ i = SPARC_PHYS_BANKS-1;
break;
}
+
+ sp_banks[i].base_addr = (unsigned long) mlist->start_adr;
+ sp_banks[i].num_bytes = mlist->num_bytes;
+ }
i++;
sp_banks[i].base_addr = 0xdeadbeef;
return tally;
}
-/* Sparc routine to reserve the mapping of the open boot prom */
+/* Traverse the memory lists in the prom to see how much physical we
+ * have.
+ */
+unsigned long
+probe_memory(void)
+{
+ int total;
+
+ total = prom_probe_memory();
-/* uncomment this for FAME and FORTUNE! */
-/* #define DEBUG_MAP_PROM */
+ /* Oh man, much nicer, keep the dirt in promlib. */
+ return total;
+}
-int
-map_the_prom(int curr_num_segs)
+asmlinkage void sparc_txtmem_error(int type, unsigned long sync_err_reg,
+ unsigned long sync_vaddr,
+ unsigned long async_err_reg,
+ unsigned long async_vaddr)
{
- register unsigned long prom_va_begin;
- register unsigned long prom_va_end;
- register int segmap_entry, i;
+ printk("Aieee, sparc text page access error, halting\n");
+ printk("type = %d sync_err_reg = 0x%x sync_vaddr = 0x%x\n",
+ type, (unsigned int) sync_err_reg, (unsigned int) sync_vaddr);
+ printk("async_err_reg = 0x%x async_vaddr = 0x%x\n",
+ (unsigned int) async_err_reg, (unsigned int) async_vaddr);
+ halt();
+}
- prom_va_begin = LINUX_OPPROM_BEGVM;
- prom_va_end = LINUX_OPPROM_ENDVM;
+/* #define DEBUG_SPARC_TEXT_ACCESS_FAULT */
-#ifdef DEBUG_MAP_PROM
- printk("\ncurr_num_segs = 0x%x\n", curr_num_segs);
+asmlinkage void sparc_text_access_fault(int type, unsigned long sync_err_reg,
+ unsigned long sync_vaddr,
+ unsigned long pc, unsigned long psr,
+ struct pt_regs *regs)
+{
+ struct vm_area_struct *vma;
+ unsigned long address;
+
+ address = sync_vaddr;
+#ifdef DEBUG_SPARC_TEXT_ACCESS_FAULT
+ printk("Text FAULT: address = %08lx code = %08lx\n",
+ (unsigned long) address, (unsigned long) sync_err_reg);
+ printk("PC = %08lx\n", (unsigned long) regs->pc);
+ SP_ENTER_DEBUGGER;
+ halt();
#endif
+ vma = find_vma(current, address);
+ if(!vma) {
+ goto bad_area;
+ }
+ if(vma->vm_start <= address)
+ goto good_area;
+ goto bad_area;
- while( prom_va_begin < prom_va_end)
- {
- segmap_entry=get_segmap(prom_va_begin);
+/*
+ * Ok, we have a good vm_area for this memory access, so
+ * we can handle it..
+ */
+good_area:
+ handle_mm_fault(vma, address, 0);
+ return;
- curr_num_segs = ((segmap_entry<curr_num_segs)
- ? segmap_entry : curr_num_segs);
+/*
+ * Something tried to access memory that isn't in our memory map..
+ * Fix it, but check if it's kernel or user first..
+ */
+bad_area:
+ if((unsigned long) address < PAGE_SIZE) {
+ printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
+ } else
+ printk(KERN_ALERT "Unable to handle kernel paging request");
+ printk(" at virtual address %08lx\n",address);
+ printk(KERN_ALERT "current->tss.pgd_ptr = %08lx\n",
+ (unsigned long) current->tss.pgd_ptr);
+ halt();
+}
- for(i = num_contexts; --i > 0;)
- (*romvec->pv_setctxt)(i, (char *) prom_va_begin,
- segmap_entry);
+asmlinkage void sparc_datamem_error(int type, unsigned long sync_err_reg,
+ unsigned long sync_vaddr,
+ unsigned long async_err_reg,
+ unsigned long async_vaddr)
+{
+ printk("Aieee, sparc data page access error, halting\n");
+ printk("type = %d sync_err_reg = 0x%x sync_vaddr = 0x%x\n",
+ type, (unsigned int) sync_err_reg, (unsigned int) sync_vaddr);
+ printk("async_err_reg = 0x%x async_vaddr = 0x%x\n",
+ (unsigned int) async_err_reg, (unsigned int) async_vaddr);
+ printk("SYNC PAGE has MMU entry %08lx\n",
+ (unsigned long) get_pte(sync_vaddr));
+ halt();
+}
- if(segmap_entry == invalid_segment)
- {
+/* #define DEBUG_SPARC_DATA_ACCESS_FAULT */
-#ifdef DEBUG_MAP_PROM
- printk("invalid_segments, virt_addr 0x%x\n", prom_va_begin);
+asmlinkage void sparc_data_access_fault(int type, unsigned long sync_err_reg,
+ unsigned long sync_vaddr,
+ unsigned long pc, unsigned long psr,
+ struct pt_regs *regs)
+{
+ struct vm_area_struct *vma;
+ unsigned long address;
+ int error_code;
+
+ address = sync_vaddr;
+#ifdef DEBUG_SPARC_DATA_ACCESS_FAULT
+ printk("Data FAULT: address = %08lx code = %08lx\n",
+ (unsigned long) address, (unsigned long) sync_err_reg);
+ printk("PC = %08lx\n", (unsigned long) regs->pc);
+ printk("PTE = %08lx\n", (unsigned long) get_pte(address));
+#endif
+ vma = find_vma(current, address);
+ if(!vma) {
+#ifdef DEBUG_SPARC_DATA_ACCESS_FAULT
+ printk("NULL VMA\n");
#endif
+ goto bad_area;
+ }
+ if(vma->vm_start <= address)
+ goto good_area;
- prom_va_begin += 0x40000; /* num bytes per segment entry */
- continue;
- }
+ if(!(vma->vm_flags & VM_GROWSDOWN)) {
+ goto bad_area;
+ }
+ if(vma->vm_end - address > current->rlim[RLIMIT_STACK].rlim_cur) {
+ goto bad_area;
+ }
- /* DUH, prom maps itself so that users can access it. This is
- * broken.
- */
+ vma->vm_offset -= vma->vm_start - (address & PAGE_MASK);
+ vma->vm_start = (address & PAGE_MASK);
-#ifdef DEBUG_MAP_PROM
- printk("making segmap for prom privileged, va = 0x%x\n",
- prom_va_begin);
+/*
+ * Ok, we have a good vm_area for this memory access, so
+ * we can handle it..
+ */
+good_area:
+#ifdef DEBUG_SPARC_DATA_ACCESS_FAULT
+ printk("Found good_area\n");
#endif
-
- for(i = 0x40; --i >= 0; prom_va_begin+=4096)
- {
- put_pte(prom_va_begin, get_pte(prom_va_begin) | 0x20000000);
- }
-
+ if((sync_err_reg & SUN4C_SYNC_BADWRITE) &&
+ (sync_err_reg & SUN4C_SYNC_NPRESENT)) {
+ if(!(vma->vm_flags & VM_WRITE)) {
+#ifdef DEBUG_SPARC_DATA_ACCESS_FAULT
+ printk("oops, vma not writable\n");
+#endif
+ goto bad_area;
}
+ } else {
+ if(sync_err_reg & SUN4C_SYNC_PROT) {
+#ifdef DEBUG_SPARC_DATA_ACCESS_FAULT
+ printk("PROT violation\n");
+#endif
+ goto bad_area;
+ }
+ if(!(vma->vm_flags & (VM_READ | VM_EXEC))) {
+#ifdef DEBUG_SPARC_DATA_ACCESS_FAULT
+ printk("vma not readable nor executable\n");
+#endif
+ goto bad_area;
+ }
+ }
- printk("Mapped the PROM in all contexts...\n");
+ if(sync_err_reg & SUN4C_SYNC_BADWRITE)
+ error_code = 0x2;
+ else
+ error_code = 0x0;
-#ifdef DEBUG_MAP_PROM
- printk("curr_num_segs = 0x%x\n", curr_num_segs);
+#ifdef DEBUG_SPARC_DATA_ACCESS_FAULT
+ printk("calling handle_mm_fault vma=%08lx addr=%08lx code=%d\n",
+ (unsigned long) vma, (unsigned long) address,
+ (int) error_code);
#endif
+ handle_mm_fault(vma, address, error_code);
+ return;
+
+/*
+ * Something tried to access memory that isn't in our memory map..
+ * Fix it, but check if it's kernel or user first..
+ */
+bad_area:
+ if (wp_works_ok < 0 && address == 0x0) {
+ wp_works_ok = 1;
+ pg0[0] = pte_val(mk_pte(PAGE_OFFSET, PAGE_SHARED));
+ put_pte((unsigned long) 0x0, pg0[0]);
+ printk("This Sparc honours the WP bit even when in supervisor mode. Good.\n");
+ return;
+ }
+
+ if((unsigned long) address < PAGE_SIZE) {
+ printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
+ } else
+ printk(KERN_ALERT "Unable to handle kernel paging request");
+ printk(" at virtual address %08lx\n",address);
+ printk(KERN_ALERT "current->tss.pgd_ptr = %08lx\n",
+ (unsigned long) current->tss.pgd_ptr);
+ halt();
+}
+
+/* Dump the contents of the SRMMU fsr in a human readable format. */
+void
+dump_srmmu_fsr(unsigned long fsr)
+{
+ unsigned long ebe, l, at, ft, fav, ow;
+
+ ebe = (fsr&SRMMU_FSR_EBE_MASK)>>10;
+ l = (fsr&SRMMU_FSR_L_MASK)>>8;
+ at = (fsr&SRMMU_FSR_AT_MASK)>>5;
+ ft = (fsr&SRMMU_FSR_FT_MASK)>>2;
+ fav = (fsr&SRMMU_FSR_FAV_MASK)>>1;
+ ow = (fsr&SRMMU_FSR_OW_MASK);
+
+ printk("FSR %08lx: ", fsr);
+
+ /* Ugh, the ebe is arch-dep, have to find out the meanings. */
+ printk("EBE<%s> ", (ebe == 0 ? "none" : (ebe == 1 ? "bus err" :
+ (ebe == 2 ? "bus timeout" :
+ (ebe == 4 ? "uncorrectable err" :
+ (ebe == 8 ? "undefined err" :
+ (ebe == 16 ? "parity err" :
+ (ebe == 24 ? "tsunami parity err" :
+ (ebe == 32 ? "store buf err" :
+ (ebe == 64 ? "control space err" :
+ "VIKING emergency response team"))))))))));
+ printk("L<%s> ", (l == 0 ? "context table" : (l == 1 ? "level1" :
+ (l == 2 ? "level2" :
+ "level3"))));
+ printk("AT<%s> ", (at == 0 ? "user load" :
+ (at == 1 ? "priv load" :
+ (at == 2 ? "user rd/exec" :
+ (at == 3 ? "priv rd/exec" :
+ (at == 4 ? "user store data" :
+ (at == 5 ? "priv store data" :
+ (at == 6 ? "user store inst" :
+ "priv store inst"))))))));
+
+ printk("FT<%s> ", (ft == 0 ? "none" :
+ (ft == 1 ? "invalid address" :
+ (ft == 2 ? "prot violation" :
+ (ft == 3 ? "priv violation" :
+ (ft == 4 ? "translation error" :
+ (ft == 5 ? "bus acc error" :
+ (ft == 6 ? "internal error" :
+ "reserved"))))))));
+
+ printk("FAV<%s> ", (fav == 0 ? "faddr invalid" : "faddr valid"));
+ printk("OW<%s>\n", (ow == 0 ? "fsr not overwritten" : "fsr overwritten"));
+
+ return;
+}
- return curr_num_segs;
+/* #define DEBUG_SRMMU_TEXT_ACCESS_FAULT */
+void
+really_bad_srmmu_fault(int type, unsigned long fstatus, unsigned long faddr)
+{
+ /* Learn how to handle these later... */
+ printk("REALLY BAD SRMMU FAULT DETECTED\n");
+ printk("Bailing out...\n");
+ dump_srmmu_fsr(fstatus);
+ prom_halt();
+ return;
}
+asmlinkage void srmmu_text_access_fault(int type, unsigned long fstatus,
+ unsigned long faddr,
+ unsigned long pc, unsigned long psr,
+ struct pt_regs *regs)
+{
+ struct vm_area_struct *vma;
+ unsigned long address;
+
+ /* Check for external bus errors and uncorrectable errors */
+ if(fstatus&SRMMU_FSR_EBE_MASK)
+ printk("External Bus Error detected during a text fault.\n");
+
+ /* Check for multiple faults... */
+ if(fstatus&SRMMU_FSR_OW_MASK && (fstatus&SRMMU_FSR_FT_TRANS)) {
+ printk("Multiple faults detected in text fault handler\n");
+ printk("Fault number one: Text fault at addr %08lx", faddr);
+ printk("Fault number two: Translation Error\n");
+ printk("Giving up...\n");
+ prom_halt();
+ }
+
+ if(fstatus&(SRMMU_FSR_EBE_BERR | SRMMU_FSR_EBE_BTIMEO | SRMMU_FSR_EBE_UNCOR))
+ really_bad_srmmu_fault(type, fstatus, faddr);
+
+ /* Ok, we should be able to handle it at this point. */
+
+ address = faddr;
+#ifdef DEBUG_SRMMU_TEXT_ACCESS_FAULT
+ printk("Text FAULT: address = %08lx code = %08lx\n",
+ (unsigned long) address, (unsigned long) fstatus);
+ printk("PC = %08lx\n", (unsigned long) regs->pc);
+ dump_srmmu_fsr(fstatus);
+ halt();
+#endif
+
+ /* Ugh, how often does this happen? ;-( */
+ if(!(fstatus&SRMMU_FSR_FAV_MASK)) {
+ printk("Fault address register is INVALID! Since this is a text\n");
+ printk("fault I'll use the value of the trapped PC instead...\n");
+ address = regs->pc;
+ }
+
+ /* Ugh, I don't wanna know... */
+
+ vma = find_vma(current, address);
+ if(!vma) {
+ goto bad_area;
+ }
+ if(vma->vm_start <= address)
+ goto good_area;
+ goto bad_area;
+
/*
- * This routine handles page faults. It determines the address,
- * and the problem, and then passes it off to one of the appropriate
- * routines.
+ * Ok, we have a good vm_area for this memory access, so
+ * we can handle it..
*/
-asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code)
-{
- die_if_kernel("Oops", regs, error_code);
- do_exit(SIGKILL);
+good_area:
+ do_no_page(vma, address, 0);
+ return;
+
+/*
+ * Something tried to access memory that isn't in our memory map..
+ * Fix it, but check if it's kernel or user first..
+ */
+bad_area:
+ if((unsigned long) address < PAGE_SIZE) {
+ printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
+ } else
+ printk(KERN_ALERT "Unable to handle kernel paging request");
+ printk(" at virtual address %08lx\n",address);
+ printk(KERN_ALERT "current->tss.pgd_ptr = %08lx\n",
+ (unsigned long) current->tss.pgd_ptr);
+ halt();
}
+/* #define DEBUG_SRMMU_DATA_ACCESS_FAULT */
+
+asmlinkage void srmmu_data_access_fault(int type, unsigned long fstatus,
+ unsigned long faddr,
+ unsigned long afstatus,
+ unsigned long afaddr,
+ struct pt_regs *regs)
+{
+ struct vm_area_struct *vma;
+ unsigned long address, pc, psr;
+ int error_code;
+
+ pc = regs->pc;
+ psr= regs->psr;
+ address = faddr;
+#ifdef DEBUG_SRMMU_DATA_ACCESS_FAULT
+ printk("Data FAULT: address = %08lx code = %08lx\n",
+ (unsigned long) address, (unsigned long) fstatus);
+ printk("PC = %08lx\n", (unsigned long) pc);
+ printk("afsr = %08lx afaddr = %08lx\n", afstatus, afaddr);
+ dump_srmmu_fsr(fstatus);
+#endif
+
+ /* I figure if I make the panic's look like they came from SunOS or Solaris
+ * my life will be a lot easier ;-)
+ */
+ if(!(fstatus&SRMMU_FSR_FAV_MASK)) {
+ dump_srmmu_fsr(fstatus);
+ panic("hat_pteload: Fault address is invalid on a data fault, I'm confused...\n");
+ }
+
+#if 0 /* I see this all the time on supersparcs ;-( */
+ if(fstatus&SRMMU_FSR_OW_MASK) {
+ dump_srmmu_fsr(fstatus);
+ panic("hat_pteload: Multiple faults at once, giving up...\n");
+ }
+#endif
+
+ vma = find_vma(current, address);
+ if(!vma) {
+#ifdef DEBUG_SRMMU_DATA_ACCESS_FAULT
+ printk("NULL VMA\n");
+#endif
+ goto bad_area;
+ }
+ if(vma->vm_start <= address)
+ goto good_area;
+
+ if(!(vma->vm_flags & VM_GROWSDOWN)) {
+ goto bad_area;
+ }
+ if(vma->vm_end - address > current->rlim[RLIMIT_STACK].rlim_cur) {
+ goto bad_area;
+ }
+
+ vma->vm_offset -= vma->vm_start - (address & PAGE_MASK);
+ vma->vm_start = (address & PAGE_MASK);
+
+/*
+ * Ok, we have a good vm_area for this memory access, so
+ * we can handle it..
+ */
+good_area:
+#ifdef DEBUG_SRMMU_DATA_ACCESS_FAULT
+ printk("Found good_area\n");
+#endif
+ if((fstatus & SUN4C_SYNC_BADWRITE) &&
+ (fstatus & SUN4C_SYNC_NPRESENT)) {
+ if(!(vma->vm_flags & VM_WRITE)) {
+#ifdef DEBUG_SRMMU_DATA_ACCESS_FAULT
+ printk("oops, vma not writable\n");
+#endif
+ goto bad_area;
+ }
+ } else {
+ if(fstatus & SUN4C_SYNC_PROT) {
+#ifdef DEBUG_SRMMU_DATA_ACCESS_FAULT
+ printk("PROT violation\n");
+#endif
+ goto bad_area;
+ }
+ if(!(vma->vm_flags & (VM_READ | VM_EXEC))) {
+#ifdef DEBUG_SRMMU_DATA_ACCESS_FAULT
+ printk("vma not readable nor executable\n");
+#endif
+ goto bad_area;
+ }
+ }
+ if(fstatus & SUN4C_SYNC_BADWRITE)
+ error_code = 0x2;
+ else
+ error_code = 0x0;
+#ifdef DEBUG_SRMMU_DATA_ACCESS_FAULT
+ printk("calling do_no_page vma=%08lx addr=%08lx code=%d\n",
+ (unsigned long) vma, (unsigned long) address,
+ (int) error_code);
+#endif
+ do_no_page(vma, address, error_code);
+ return;
+/*
+ * Something tried to access memory that isn't in our memory map..
+ * Fix it, but check if it's kernel or user first..
+ */
+bad_area:
+ if (wp_works_ok < 0 && address == 0x0) {
+ wp_works_ok = 1;
+ /* Advance the PC and NPC over the test store. */
+ regs->pc = regs->npc;
+ regs->npc += 4;
+ printk("This Sparc honours the WP bit even when in supervisor mode. Good.\n");
+ return;
+ }
+
+ if((unsigned long) address < PAGE_SIZE) {
+ printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
+ } else
+ printk(KERN_ALERT "Unable to handle kernel paging request");
+ printk(" at virtual address %08lx\n",address);
+ printk(KERN_ALERT "current->tss.pgd_ptr = %08lx\n",
+ (unsigned long) current->tss.pgd_ptr);
+ halt();
+}
#include <asm/vac-ops.h>
#include <asm/page.h>
#include <asm/pgtable.h>
+#include <asm/vaddrs.h>
extern void scsi_mem_init(unsigned long);
extern void sound_mem_init(void);
extern void die_if_kernel(char *,struct pt_regs *,long);
extern void show_net_buffers(void);
-extern int map_the_prom(int);
+struct sparc_phys_banks sp_banks[SPARC_PHYS_BANKS];
+
+/* The following number keeps track of which page table is to
+ * next be allocated in a page. This is necessary since there
+ * are 16 page tables per page on the space.
+ */
+unsigned long ptr_in_current_pgd;
+
+/* This keeps track of which physical segments are in use right now. */
+unsigned int phys_seg_map[PSEG_ENTRIES];
+unsigned int phys_seg_life[PSEG_ENTRIES];
+
+/* Context allocation. */
+struct task_struct *ctx_tasks[MAX_CTXS];
+int ctx_tasks_last_frd;
-struct sparc_phys_banks sp_banks[14];
-unsigned long *sun4c_mmu_table;
extern int invalid_segment, num_segmaps, num_contexts;
/*
unsigned long __zero_page(void)
{
memset((void *) ZERO_PGE, 0, PAGE_SIZE);
- return ZERO_PGE;
+ return (unsigned long) ZERO_PGE;
}
void show_mem(void)
}
extern unsigned long free_area_init(unsigned long, unsigned long);
+extern pgprot_t protection_map[16];
/*
- * paging_init() sets up the page tables: in the alpha version this actually
- * unmaps the bootup page table (as we're now in KSEG, so we don't need it).
+ * paging_init() sets up the page tables: We call the MMU specific
+ * init routine based upon the Sun model type on the Sparc.
*
- * The bootup sequence put the virtual page table into high memory: that
- * means that we can change the L1 page table by just using VL1p below.
*/
+extern unsigned long sun4c_paging_init(unsigned long, unsigned long);
+extern unsigned long srmmu_paging_init(unsigned long, unsigned long);
+extern unsigned long probe_devices(unsigned long);
unsigned long paging_init(unsigned long start_mem, unsigned long end_mem)
{
- unsigned long i, a, b, mask=0;
- unsigned long curseg, curpte, num_inval;
- unsigned long address;
- pte_t *pg_table;
-
- register int num_segs, num_ctx;
- register char * c;
+ int i;
- num_segs = num_segmaps;
- num_ctx = num_contexts;
+ switch(sparc_cpu_model) {
+ case sun4c:
+ start_mem = sun4c_paging_init(start_mem, end_mem);
+ break;
+ case sun4m:
+ case sun4d:
+ case sun4e:
+ start_mem = srmmu_paging_init(start_mem, end_mem);
+ break;
+ default:
+ printk("paging_init: Cannot init paging on this Sparc\n");
+ printk("paging_init: sparc_cpu_model = %d\n", sparc_cpu_model);
+ printk("paging_init: Halting...\n");
+ halt();
+ };
+
+ /* Initialize context map. */
+ for(i=0; i<MAX_CTXS; i++) ctx_tasks[i] = NULL;
+
+ /* Initialize the protection map */
+ protection_map[0] = __P000;
+ protection_map[1] = __P001;
+ protection_map[2] = __P010;
+ protection_map[3] = __P011;
+ protection_map[4] = __P100;
+ protection_map[5] = __P101;
+ protection_map[6] = __P110;
+ protection_map[7] = __P111;
+ protection_map[8] = __S000;
+ protection_map[9] = __S001;
+ protection_map[10] = __S010;
+ protection_map[11] = __S011;
+ protection_map[12] = __S100;
+ protection_map[13] = __S101;
+ protection_map[14] = __S110;
+ protection_map[15] = __S111;
+
+ start_mem = probe_devices(start_mem);
- num_segs -= 1;
- invalid_segment = num_segs;
+ return start_mem;
+}
- start_mem = free_area_init(start_mem, end_mem);
+extern unsigned long sun4c_test_wp(unsigned long);
+extern void srmmu_test_wp(void);
-/* On the sparc we first need to allocate the segmaps for the
- * PROM's virtual space, and make those segmaps unusable. We
- * map the PROM in ALL contexts therefore the break key and the
- * sync command work no matter what state you took the machine
- * out of
- */
+void mem_init(unsigned long start_mem, unsigned long end_mem)
+{
+ int codepages = 0;
+ int reservedpages = 0;
+ int datapages = 0;
+ unsigned long tmp2, addr;
+ extern char etext;
- printk("mapping the prom...\n");
- num_segs = map_the_prom(num_segs);
+ end_mem &= PAGE_MASK;
+ high_memory = end_mem;
start_mem = PAGE_ALIGN(start_mem);
- /* Set up static page tables in kernel space, this will be used
- * so that the low-level page fault handler can fill in missing
- * TLB entries since all mmu entries cannot be loaded at once
- * on the sun4c.
- */
-
-#if 0
- /* ugly debugging code */
- for(i=0; i<40960; i+=PAGE_SIZE)
- printk("address=0x%x vseg=%d pte=0x%x\n", (unsigned int) i,
- (int) get_segmap(i), (unsigned int) get_pte(i));
-#endif
-
- printk("Setting up kernel static mmu table... bounce bounce\n");
-
- address = 0; /* ((unsigned long) &end) + 524288; */
- sun4c_mmu_table = (unsigned long *) start_mem;
- pg_table = (pte_t *) start_mem;
- curseg = curpte = num_inval = 0;
- while(address < end_mem) {
- if(curpte == 0)
- put_segmap((address&PGDIR_MASK), curseg);
- for(i=0; sp_banks[i].num_bytes != 0; i++)
- if((address >= sp_banks[i].base_addr) &&
- (address <= (sp_banks[i].base_addr + sp_banks[i].num_bytes)))
- goto good_address;
- /* No physical memory here, so set the virtual segment to
- * the invalid one, and put an invalid pte in the static
- * kernel table.
- */
- *pg_table = mk_pte((address >> PAGE_SHIFT), PAGE_INVALID);
- pg_table++; curpte++; num_inval++;
- if(curpte > 63) {
- if(curpte == num_inval) {
- put_segmap((address&PGDIR_MASK), invalid_segment);
- } else {
- put_segmap((address&PGDIR_MASK), curseg);
- curseg++;
- }
- curpte = num_inval = 0;
- }
- address += PAGE_SIZE;
- continue;
-
- good_address:
- /* create pte entry */
- if(address < (((unsigned long) &end) + 524288)) {
- pte_val(*pg_table) = get_pte(address);
- } else {
- *pg_table = mk_pte((address >> PAGE_SHIFT), PAGE_KERNEL);
- put_pte(address, pte_val(*pg_table));
- }
-
- pg_table++; curpte++;
- if(curpte > 63) {
- put_segmap((address&PGDIR_MASK), curseg);
- curpte = num_inval = 0;
- curseg++;
- }
- address += PAGE_SIZE;
- }
-
- start_mem = (unsigned long) pg_table;
- /* ok, allocate the kernel pages, map them in all contexts
- * (with help from the prom), and lock them. Isn't the sparc
- * fun kiddies? TODO
- */
-
-#if 0
- /* ugly debugging code */
- for(i=0x1a3000; i<(0x1a3000+40960); i+=PAGE_SIZE)
- printk("address=0x%x vseg=%d pte=0x%x\n", (unsigned int) i,
- (int) get_segmap(i), (unsigned int) get_pte(i));
- halt();
-#endif
-
- b=PGDIR_ALIGN(start_mem)>>18;
- c= (char *)0x0;
-
- printk("mapping kernel in all contexts...\n");
-
- for(a=0; a<b; a++)
- {
- for(i=0; i<num_contexts; i++)
- {
- /* map the kernel virt_addrs */
- (*(romvec->pv_setctxt))(i, (char *) c, a);
- }
- c += 0x40000;
- }
-
- /* Ok, since now mapped in all contexts, we can free up
- * context zero to be used amongst user processes.
- */
-
- /* free context 0 here TODO */
-
- /* invalidate all user pages and initialize the pte struct
- * for userland. TODO
- */
-
- /* Make the kernel text unwritable and cacheable, the prom
- * loaded our text as writable, only sneaky sunos kernels need
- * self-modifying code.
- */
-
- a= (unsigned long) &etext;
- mask=~(PTE_NC|PTE_W); /* make cacheable + not writable */
-
- /* must do for every segment since kernel uses all contexts
- * and unlike some sun kernels I know of, we can't hard wire
- * context 0 just for the kernel, that is unnecessary.
- */
-
- for(i=0; i<8; i++)
- {
- b=PAGE_ALIGN((unsigned long) &trapbase);
-
- switch_to_context(i);
-
- for(;b<a; b+=4096)
- {
- put_pte(b, (get_pte(b) & mask));
- }
- }
+ addr = PAGE_OFFSET;
+ while(addr < start_mem) {
+ mem_map[MAP_NR(addr)] = MAP_PAGE_RESERVED;
+ addr += PAGE_SIZE;
+ }
- invalidate(); /* flush the virtual address cache */
+ for(addr = start_mem; addr < end_mem; addr += PAGE_SIZE)
+ mem_map[MAP_NR(addr)] = 0;
- printk("\nCurrently in context - ");
- for(i=0; i<num_contexts; i++)
- {
- switch_to_context(i);
- printk("%d ", (int) i);
- }
- printk("\n");
+#ifdef CONFIG_SCSI
+ scsi_mem_init(high_memory);
+#endif
- switch_to_context(0);
+ for (addr = PAGE_OFFSET; addr < end_mem; addr += PAGE_SIZE) {
+ if(mem_map[MAP_NR(addr)]) {
+ if (addr < (unsigned long) &etext)
+ codepages++;
+ else if(addr < start_mem)
+ datapages++;
+ else
+ reservedpages++;
+ continue;
+ }
+ mem_map[MAP_NR(addr)] = 1;
+ free_page(addr);
+ }
- invalidate();
- return start_mem;
-}
+ tmp2 = nr_free_pages << PAGE_SHIFT;
-void mem_init(unsigned long start_mem, unsigned long end_mem)
-{
- unsigned long start_low_mem = PAGE_SIZE;
- int codepages = 0;
- int reservedpages = 0;
- int datapages = 0;
- int i = 0;
- unsigned long tmp, limit, tmp2, addr;
- extern char etext;
-
- end_mem &= PAGE_MASK;
- high_memory = end_mem;
-
- start_low_mem = PAGE_ALIGN(start_low_mem);
- start_mem = PAGE_ALIGN(start_mem);
-
- for(i = 0; sp_banks[i].num_bytes != 0; i++) {
- tmp = sp_banks[i].base_addr;
- limit = (sp_banks[i].base_addr + sp_banks[i].num_bytes);
- if(tmp<start_mem) {
- if(limit>start_mem)
- tmp = start_mem;
- else continue;
- }
-
- while(tmp<limit) {
- mem_map[MAP_NR(tmp)] = 0;
- tmp += PAGE_SIZE;
- }
- if(sp_banks[i+1].num_bytes != 0)
- while(tmp < sp_banks[i+1].base_addr) {
- mem_map[MAP_NR(tmp)] = MAP_PAGE_RESERVED;
- tmp += PAGE_SIZE;
- }
- }
+ printk("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data)\n",
+ tmp2 >> 10,
+ (high_memory - PAGE_OFFSET) >> 10,
+ codepages << (PAGE_SHIFT-10),
+ reservedpages << (PAGE_SHIFT-10),
+ datapages << (PAGE_SHIFT-10));
-#ifdef CONFIG_SCSI
- scsi_mem_init(high_memory);
+/* Heh, test write protection just like the i386, this is bogus but it is
+ * fun to do ;)
+ */
+ switch(sparc_cpu_model) {
+ case sun4c:
+ start_mem = sun4c_test_wp(start_mem);
+ break;
+ case sun4m:
+ case sun4d:
+ case sun4e:
+ srmmu_test_wp();
+ break;
+ default:
+ printk("mem_init: Could not test WP bit on this machine.\n");
+ printk("mem_init: sparc_cpu_model = %d\n", sparc_cpu_model);
+ printk("mem_init: Halting...\n");
+ halt();
+ };
+
+#ifdef DEBUG_MEMINIT
+ printk("Breaker breaker...Roger roger.... Over and out...\n");
#endif
+ invalidate();
- for (addr = 0; addr < high_memory; addr += PAGE_SIZE) {
- if(mem_map[MAP_NR(addr)]) {
- if (addr < (unsigned long) &etext)
- codepages++;
- else if(addr < start_mem)
- datapages++;
- else
- reservedpages++;
- continue;
- }
- mem_map[MAP_NR(addr)] = 1;
- free_page(addr);
- }
-
- tmp2 = nr_free_pages << PAGE_SHIFT;
-
- printk("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data)\n",
- tmp2 >> 10,
- high_memory >> 10,
- codepages << (PAGE_SHIFT-10),
- reservedpages << (PAGE_SHIFT-10),
- datapages << (PAGE_SHIFT-10));
-
- invalidate();
- return;
+ return;
}
void si_meminfo(struct sysinfo *val)
--- /dev/null
+/* loadmmu.c: This code loads up all the mm function pointers once the
+ * machine type has been determined. It also sets the static
+ * mmu values such as PAGE_NONE, etc.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/system.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+
+void (*invalidate)(void);
+
+unsigned int pmd_shift, pmd_size, pmd_mask;
+unsigned int (*pmd_align)(unsigned int);
+unsigned int pgdir_shift, pgdir_size, pgdir_mask;
+unsigned int (*pgdir_align)(unsigned int);
+unsigned int ptrs_per_pte, ptrs_per_pmd, ptrs_per_pgd;
+
+pgprot_t page_none, page_shared, page_copy, page_readonly, page_kernel;
+pgprot_t page_invalid;
+
+/* Grrr... function pointers galore... */
+unsigned long (*pte_page)(pte_t);
+unsigned long (*pmd_page)(pmd_t);
+unsigned long (*pgd_page)(pgd_t);
+
+void (*sparc_update_rootmmu_dir)(struct task_struct *, pgd_t *pgdir);
+unsigned long (*(vmalloc_start))(void);
+void (*switch_to_context)(int);
+
+int (*pte_none)(pte_t);
+int (*pte_present)(pte_t);
+int (*pte_inuse)(pte_t *);
+void (*pte_clear)(pte_t *);
+void (*pte_reuse)(pte_t *);
+
+int (*pmd_none)(pmd_t);
+int (*pmd_bad)(pmd_t);
+int (*pmd_present)(pmd_t);
+int (*pmd_inuse)(pmd_t *);
+void (*pmd_clear)(pmd_t *);
+void (*pmd_reuse)(pmd_t *);
+
+int (*pgd_none)(pgd_t);
+int (*pgd_bad)(pgd_t);
+int (*pgd_present)(pgd_t);
+int (*pgd_inuse)(pgd_t *);
+void (*pgd_clear)(pgd_t *);
+void (*pgd_reuse)(pgd_t *);
+
+pte_t (*mk_pte)(unsigned long, pgprot_t);
+void (*pgd_set)(pgd_t *, pte_t *);
+pte_t (*pte_modify)(pte_t, pgprot_t);
+pgd_t * (*pgd_offset)(struct task_struct *, unsigned long);
+pmd_t * (*pmd_offset)(pgd_t *, unsigned long);
+pte_t * (*pte_offset)(pmd_t *, unsigned long);
+void (*pte_free_kernel)(pte_t *);
+pte_t * (*pte_alloc_kernel)(pmd_t *, unsigned long);
+
+void (*pmd_free_kernel)(pmd_t *);
+pmd_t * (*pmd_alloc_kernel)(pgd_t *, unsigned long);
+void (*pte_free)(pte_t *);
+pte_t * (*pte_alloc)(pmd_t *, unsigned long);
+
+void (*pmd_free)(pmd_t *);
+pmd_t * (*pmd_alloc)(pgd_t *, unsigned long);
+void (*pgd_free)(pgd_t *);
+
+pgd_t * (*pgd_alloc)(void);
+
+/*
+ * The following only work if pte_present() is true.
+ * Undefined behaviour if not..
+ */
+int (*pte_read)(pte_t);
+int (*pte_write)(pte_t);
+int (*pte_exec)(pte_t);
+int (*pte_dirty)(pte_t);
+int (*pte_young)(pte_t);
+int (*pte_cow)(pte_t);
+
+pte_t (*pte_wrprotect)(pte_t);
+pte_t (*pte_rdprotect)(pte_t);
+pte_t (*pte_exprotect)(pte_t);
+pte_t (*pte_mkclean)(pte_t);
+pte_t (*pte_mkold)(pte_t);
+pte_t (*pte_uncow)(pte_t);
+pte_t (*pte_mkwrite)(pte_t);
+pte_t (*pte_mkread)(pte_t);
+pte_t (*pte_mkexec)(pte_t);
+pte_t (*pte_mkdirty)(pte_t);
+pte_t (*pte_mkyoung)(pte_t);
+pte_t (*pte_mkcow)(pte_t);
+
+extern void ld_mmu_sun4c(void);
+extern void ld_mmu_srmmu(void);
+
+void
+load_mmu(void)
+{
+ switch(sparc_cpu_model) {
+ case sun4c:
+ ld_mmu_sun4c();
+ break;
+ case sun4m:
+ case sun4d:
+ case sun4e:
+ ld_mmu_srmmu();
+ break;
+ default:
+ printk("load_mmu: MMU support not available for this architecture\n");
+ printk("load_mmu: sparc_cpu_model = %d\n", (int) sparc_cpu_model);
+ printk("load_mmu: Halting...\n");
+ halt();
+ };
+ return;
+}
--- /dev/null
+/* srmmu.c: SRMMU specific routines for memory management.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1995 Peter A. Zaitcev (zaitcev@lab.ipmce.su)
+ */
+
+#include <linux/kernel.h> /* for printk */
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/kdebug.h>
+#include <asm/vaddrs.h>
+#include <asm/traps.h>
+#include <asm/mp.h>
+#include <asm/cache.h>
+#include <asm/oplib.h>
+
+extern unsigned long free_area_init(unsigned long, unsigned long);
+
+unsigned int srmmu_pmd_align(unsigned int addr) { return SRMMU_PMD_ALIGN(addr); }
+unsigned int srmmu_pgdir_align(unsigned int addr) { return SRMMU_PGDIR_ALIGN(addr); }
+
+/* Idea taken from Hamish McDonald's MC680x0 Linux code, nice job.
+ * Many of the page table/directory functions on the SRMMU use this
+ * routine.
+ *
+ * Having a complete physical ram structure walk happen for each
+ * invocation is quite costly. However, this does do some nice
+ * sanity checking and we'll see when our maps don't match. Eventually
+ * when I trust my code I will just do a direct mmu probe in mk_pte().
+ */
+static inline unsigned int
+srmmu_virt_to_phys(unsigned int vaddr)
+{
+ unsigned int paddr = 0;
+ unsigned int voff = (vaddr - PAGE_OFFSET);
+ int i;
+
+ for(i=0; sp_banks[i].num_bytes != 0; i++) {
+ if(voff < paddr + sp_banks[i].num_bytes) {
+ /* This matches. */
+ return sp_banks[i].base_addr + voff - paddr;
+ } else
+ paddr += sp_banks[i].num_bytes;
+ }
+ /* Shit, gotta consult the MMU, this shouldn't happen... */
+ printk("srmmu_virt_to_phys: SRMMU virt to phys translation failed, halting\n");
+ halt();
+}
+
+static inline unsigned long
+srmmu_phys_to_virt(unsigned long paddr)
+{
+ int i;
+ unsigned long offset = PAGE_OFFSET;
+
+ for (i=0; sp_banks[i].num_bytes != 0; i++)
+ {
+ if (paddr >= sp_banks[i].base_addr &&
+ paddr < (sp_banks[i].base_addr
+ + sp_banks[i].num_bytes)) {
+ return (paddr - sp_banks[i].base_addr) + offset;
+ } else
+ offset += sp_banks[i].num_bytes;
+ }
+ printk("srmmu_phys_to_virt: Could not make translation, halting...\n");
+ halt();
+}
+
+unsigned long
+srmmu_vmalloc_start(void)
+{
+ return ((high_memory + SRMMU_VMALLOC_OFFSET) & ~(SRMMU_VMALLOC_OFFSET-1));
+}
+
+unsigned long
+srmmu_pmd_page(pmd_t pmd)
+{
+ unsigned long page;
+
+ page = (pmd_val(pmd) & (SRMMU_PTD_PTP_MASK)) << SRMMU_PTD_PTP_PADDR_SHIFT;
+ return srmmu_phys_to_virt(page);
+}
+
+unsigned long
+srmmu_pgd_page(pgd_t pgd)
+{
+ unsigned long page;
+
+ page = (pgd_val(pgd) & (SRMMU_PTD_PTP_MASK)) << SRMMU_PTD_PTP_PADDR_SHIFT;
+ return srmmu_phys_to_virt(page);
+}
+
+unsigned long
+srmmu_pte_page(pte_t pte)
+{
+ unsigned long page;
+
+ page = (pte_val(pte) & (SRMMU_PTE_PPN_MASK)) << SRMMU_PTE_PPN_PADDR_SHIFT;
+ printk("srmmu_pte_page: page = %08lx\n", page);
+ return srmmu_phys_to_virt(page);
+}
+
+int srmmu_pte_none(pte_t pte) { return !pte_val(pte); }
+int srmmu_pte_present(pte_t pte) { return pte_val(pte) & SRMMU_ET_PTE; }
+int srmmu_pte_inuse(pte_t *ptep) { return mem_map[MAP_NR(ptep)] != 1; }
+void srmmu_pte_clear(pte_t *ptep) { pte_val(*ptep) = 0; }
+void srmmu_pte_reuse(pte_t *ptep)
+{
+ if(!(mem_map[MAP_NR(ptep)] & MAP_PAGE_RESERVED))
+ mem_map[MAP_NR(ptep)]++;
+}
+
+int srmmu_pmd_none(pmd_t pmd) { return !pmd_val(pmd); }
+int srmmu_pmd_bad(pmd_t pmd)
+{
+ return ((pmd_val(pmd)&SRMMU_ET_PTDBAD)==SRMMU_ET_PTDBAD) ||
+ (srmmu_pmd_page(pmd) > high_memory);
+}
+
+int srmmu_pmd_present(pmd_t pmd) { return pmd_val(pmd) & SRMMU_ET_PTD; }
+int srmmu_pmd_inuse(pmd_t *pmdp) { return mem_map[MAP_NR(pmdp)] != 1; }
+void srmmu_pmd_clear(pmd_t *pmdp) { pmd_val(*pmdp) = 0; }
+void srmmu_pmd_reuse(pmd_t * pmdp)
+{
+ if (!(mem_map[MAP_NR(pmdp)] & MAP_PAGE_RESERVED))
+ mem_map[MAP_NR(pmdp)]++;
+}
+
+int srmmu_pgd_none(pgd_t pgd) { return !pgd_val(pgd); }
+int srmmu_pgd_bad(pgd_t pgd)
+{
+ return ((pgd_val(pgd)&SRMMU_ET_PTDBAD)==SRMMU_ET_PTDBAD) ||
+ (srmmu_pgd_page(pgd) > high_memory);
+}
+int srmmu_pgd_present(pgd_t pgd) { return pgd_val(pgd) & SRMMU_ET_PTD; }
+int srmmu_pgd_inuse(pgd_t *pgdp) { return mem_map[MAP_NR(pgdp)] != 1; }
+void srmmu_pgd_clear(pgd_t * pgdp) { pgd_val(*pgdp) = 0; }
+void srmmu_pgd_reuse(pgd_t *pgdp)
+{
+ if (!(mem_map[MAP_NR(pgdp)] & MAP_PAGE_RESERVED))
+ mem_map[MAP_NR(pgdp)]++;
+}
+
+/*
+ * The following only work if pte_present() is true.
+ * Undefined behaviour if not..
+ */
+int srmmu_pte_read(pte_t pte) { return (pte_val(pte) & _SRMMU_PAGE_RDONLY) || (pte_val(pte) & _SRMMU_PAGE_WRITE_USR); }
+int srmmu_pte_write(pte_t pte) { return pte_val(pte) & _SRMMU_PAGE_WRITE_USR; }
+int srmmu_pte_exec(pte_t pte) { return pte_val(pte) & _SRMMU_PAGE_EXEC; }
+int srmmu_pte_dirty(pte_t pte) { return pte_val(pte) & _SRMMU_PAGE_DIRTY; }
+int srmmu_pte_young(pte_t pte) { return pte_val(pte) & _SRMMU_PAGE_REF; }
+int srmmu_pte_cow(pte_t pte) { return pte_val(pte) & _SRMMU_PAGE_COW; }
+
+/* When we change permissions, we first clear all bits in the ACCESS field
+ * then apply the wanted bits.
+ */
+pte_t srmmu_pte_wrprotect(pte_t pte) { pte_val(pte) &= ~SRMMU_PTE_ACC_MASK; pte_val(pte) |= _SRMMU_PAGE_EXEC; return pte; }
+pte_t srmmu_pte_rdprotect(pte_t pte) { pte_val(pte) &= ~SRMMU_PTE_ACC_MASK; pte_val(pte) |= _SRMMU_PAGE_NOREAD; return pte; }
+pte_t srmmu_pte_exprotect(pte_t pte) { pte_val(pte) &= ~SRMMU_PTE_ACC_MASK; pte_val(pte) |= _SRMMU_PAGE_WRITE_USR; return pte; }
+pte_t srmmu_pte_mkclean(pte_t pte) { pte_val(pte) &= ~_SRMMU_PAGE_DIRTY; return pte; }
+pte_t srmmu_pte_mkold(pte_t pte) { pte_val(pte) &= ~_SRMMU_PAGE_REF; return pte; }
+pte_t srmmu_pte_uncow(pte_t pte) { pte_val(pte) &= ~SRMMU_PTE_ACC_MASK; pte_val(pte) |= _SRMMU_PAGE_UNCOW; return pte; }
+pte_t srmmu_pte_mkwrite(pte_t pte) { pte_val(pte) &= ~SRMMU_PTE_ACC_MASK; pte_val(pte) |= _SRMMU_PAGE_WRITE_USR; return pte; }
+pte_t srmmu_pte_mkread(pte_t pte) { pte_val(pte) &= ~SRMMU_PTE_ACC_MASK; pte_val(pte) |= _SRMMU_PAGE_RDONLY; return pte; }
+pte_t srmmu_pte_mkexec(pte_t pte) { pte_val(pte) &= ~SRMMU_PTE_ACC_MASK; pte_val(pte) |= _SRMMU_PAGE_EXEC; return pte; }
+pte_t srmmu_pte_mkdirty(pte_t pte) { pte_val(pte) |= _SRMMU_PAGE_DIRTY; return pte; }
+pte_t srmmu_pte_mkyoung(pte_t pte) { pte_val(pte) |= _SRMMU_PAGE_REF; return pte; }
+pte_t srmmu_pte_mkcow(pte_t pte) { pte_val(pte) &= ~SRMMU_PTE_ACC_MASK; pte_val(pte) |= _SRMMU_PAGE_COW; return pte; }
+
+/*
+ * Conversion functions: convert a page and protection to a page entry,
+ * and a page entry and page directory to the page they refer to.
+ */
+pte_t
+srmmu_mk_pte(unsigned long page, pgprot_t pgprot)
+{
+ pte_t pte;
+
+ if(page & (~PAGE_MASK)) panic("srmmu_mk_pte() called with unaligned page");
+ page = (srmmu_virt_to_phys(page) >> SRMMU_PTE_PPN_PADDR_SHIFT);
+ pte_val(pte) = (page & SRMMU_PTE_PPN_MASK);
+ pte_val(pte) |= pgprot_val(pgprot);
+ return pte;
+}
+
+void
+srmmu_pgd_set(pgd_t * pgdp, pmd_t * pmdp)
+{
+ unsigned long page = (unsigned long) pmdp;
+
+ page = (srmmu_virt_to_phys(page) >> SRMMU_PTD_PTP_PADDR_SHIFT);
+
+ pgd_val(*pgdp) = ((page & SRMMU_PTD_PTP_MASK) | SRMMU_ET_PTD);
+}
+
+void
+srmmu_pmd_set(pmd_t * pmdp, pte_t * ptep)
+{
+ unsigned long page = (unsigned long) ptep;
+
+ page = (srmmu_virt_to_phys(page) >> SRMMU_PTD_PTP_PADDR_SHIFT);
+
+ pmd_val(*pmdp) = ((page & SRMMU_PTD_PTP_MASK) | SRMMU_ET_PTD);
+}
+
+pte_t
+srmmu_pte_modify(pte_t pte, pgprot_t newprot)
+{
+ pte_val(pte) = (pte_val(pte) & (~SRMMU_PTE_ACC_MASK)) | pgprot_val(newprot);
+ return pte;
+}
+
+/* to find an entry in a top-level page table... */
+pgd_t *
+srmmu_pgd_offset(struct task_struct * tsk, unsigned long address)
+{
+ return ((pgd_t *) tsk->tss.pgd_ptr) +
+ ((address >> SRMMU_PGDIR_SHIFT) & (SRMMU_PTRS_PER_PGD - 1));
+}
+
+/* Find an entry in the second-level page table.. */
+pmd_t *
+srmmu_pmd_offset(pgd_t * dir, unsigned long address)
+{
+ return ((pmd_t *) pgd_page(*dir)) +
+ ((address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1));
+}
+
+/* Find an entry in the third-level page table.. */
+pte_t *
+srmmu_pte_offset(pmd_t * dir, unsigned long address)
+{
+ return ((pte_t *) pmd_page(*dir)) +
+ ((address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1));
+}
+
+/* This must update the context register for this process. */
+void
+srmmu_update_rootmmu_dir(struct task_struct *tsk, pgd_t *pgdir)
+{
+ /* See if this process has a context entry already, like after execve() */
+ if(tsk->tss.context != -1) {
+ pgd_t *ctable_ptr = 0;
+ ctable_ptr = (pgd_t *) srmmu_phys_to_virt(srmmu_get_ctable_ptr());
+ ctable_ptr += tsk->tss.context;
+ srmmu_pgd_set(ctable_ptr, (pmd_t *) pgdir);
+ /* Should flush caches here too... */
+ srmmu_flush_whole_tlb();
+ }
+
+ tsk->tss.pgd_ptr = (unsigned long) pgdir;
+
+ return;
+}
+
+/*
+ * Allocate and free page tables. The xxx_kernel() versions are
+ * used to allocate a kernel page table - this turns on ASN bits
+ * if any, and marks the page tables reserved.
+ */
+void
+srmmu_pte_free_kernel(pte_t *pte)
+{
+ mem_map[MAP_NR(pte)] = 1;
+ free_page((unsigned long) pte);
+}
+
+pte_t *
+srmmu_pte_alloc_kernel(pmd_t *pmd, unsigned long address)
+{
+ pte_t *page;
+
+ address = (address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1);
+ if (srmmu_pmd_none(*pmd)) {
+ page = (pte_t *) get_free_page(GFP_KERNEL);
+ if (srmmu_pmd_none(*pmd)) {
+ if (page) {
+ srmmu_pmd_set(pmd, page);
+ mem_map[MAP_NR(page)] = MAP_PAGE_RESERVED;
+ return page + address;
+ }
+ srmmu_pmd_set(pmd, (pte_t *) SRMMU_ET_PTDBAD);
+ return NULL;
+ }
+ free_page((unsigned long) page);
+ }
+ if (srmmu_pmd_bad(*pmd)) {
+ printk("Bad pmd in pte_alloc_kernel: %08lx\n", pmd_val(*pmd));
+ srmmu_pmd_set(pmd, (pte_t *) SRMMU_ET_PTDBAD);
+ return NULL;
+ }
+ return (pte_t *) srmmu_pmd_page(*pmd) + address;
+}
+
+/* Full three level on SRMMU */
+void
+srmmu_pmd_free_kernel(pmd_t *pmd)
+{
+ mem_map[MAP_NR(pmd)] = 1;
+ free_page((unsigned long) pmd);
+}
+
+pmd_t *
+srmmu_pmd_alloc_kernel(pgd_t *pgd, unsigned long address)
+{
+ pmd_t *page;
+
+ address = (address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1);
+ if (srmmu_pgd_none(*pgd)) {
+ page = (pmd_t *) get_free_page(GFP_KERNEL);
+ if (srmmu_pgd_none(*pgd)) {
+ if (page) {
+ srmmu_pgd_set(pgd, page);
+ mem_map[MAP_NR(page)] = MAP_PAGE_RESERVED;
+ return page + address;
+ }
+ srmmu_pgd_set(pgd, (pmd_t *) SRMMU_ET_PTDBAD);
+ return NULL;
+ }
+ free_page((unsigned long) page);
+ }
+ if (srmmu_pgd_bad(*pgd)) {
+ printk("Bad pgd in pmd_alloc_kernel: %08lx\n", pgd_val(*pgd));
+ srmmu_pgd_set(pgd, (pmd_t *) SRMMU_ET_PTDBAD);
+ return NULL;
+ }
+ return (pmd_t *) srmmu_pgd_page(*pgd) + address;
+}
+
+void
+srmmu_pte_free(pte_t *pte)
+{
+ free_page((unsigned long) pte);
+}
+
+pte_t *
+srmmu_pte_alloc(pmd_t * pmd, unsigned long address)
+{
+ pte_t *page;
+
+ address = (address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1);
+ if (srmmu_pmd_none(*pmd)) {
+ page = (pte_t *) get_free_page(GFP_KERNEL);
+ if (srmmu_pmd_none(*pmd)) {
+ if (page) {
+ srmmu_pmd_set(pmd, page);
+ mem_map[MAP_NR(page)] = MAP_PAGE_RESERVED;
+ return page + address;
+ }
+ srmmu_pmd_set(pmd, (pte_t *) SRMMU_ET_PTDBAD);
+ return NULL;
+ }
+ free_page((unsigned long) page);
+ }
+ if (srmmu_pmd_bad(*pmd)) {
+ printk("Bad pmd in pte_alloc_kernel: %08lx\n", pmd_val(*pmd));
+ srmmu_pmd_set(pmd, (pte_t *) SRMMU_ET_PTDBAD);
+ return NULL;
+ }
+ return (pte_t *) srmmu_pmd_page(*pmd) + address;
+}
+
+/*
+ * allocating and freeing a pmd is trivial: the 1-entry pmd is
+ * inside the pgd, so has no extra memory associated with it.
+ */
+void
+srmmu_pmd_free(pmd_t * pmd)
+{
+ free_page((unsigned long) pmd);
+}
+
+pmd_t *
+srmmu_pmd_alloc(pgd_t * pgd, unsigned long address)
+{
+ pmd_t *page;
+
+ address = (address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1);
+ if (srmmu_pgd_none(*pgd)) {
+ page = (pmd_t *) get_free_page(GFP_KERNEL);
+ if (srmmu_pgd_none(*pgd)) {
+ if (page) {
+ srmmu_pgd_set(pgd, page);
+ mem_map[MAP_NR(page)] = MAP_PAGE_RESERVED;
+ return page + address;
+ }
+ srmmu_pgd_set(pgd, (pmd_t *) SRMMU_ET_PTDBAD);
+ return NULL;
+ }
+ free_page((unsigned long) page);
+ }
+ if (srmmu_pgd_bad(*pgd)) {
+ printk("Bad pgd in pmd_alloc_kernel: %08lx\n", pgd_val(*pgd));
+ srmmu_pgd_set(pgd, (pmd_t *) SRMMU_ET_PTDBAD);
+ return NULL;
+ }
+ return (pmd_t *) srmmu_pgd_page(*pgd) + address;
+}
+
+void
+srmmu_pgd_free(pgd_t *pgd)
+{
+ free_page((unsigned long) pgd);
+}
+
+/* A page directory on the srmmu needs 1k, but for now to simplify the
+ * alignment constraints and allocation we just grab a whole page.
+ */
+
+pgd_t *
+srmmu_pgd_alloc(void)
+{
+ return (pgd_t *) get_free_page(GFP_KERNEL);
+}
+
+/* Just flush the whole thing for now. We will need module
+ * specific invalidate routines in certain circumstances,
+ * because of different flushing facilities and hardware
+ * bugs.
+ */
+void
+srmmu_invalidate(void)
+{
+ srmmu_flush_whole_tlb();
+ return;
+}
+
+/* XXX Needs to be written */
+void
+srmmu_switch_to_context(int context)
+{
+ printk("switching to context %d\n", context);
+
+ return;
+}
+
+/* Low level IO area allocation on the SRMMU.
+ *
+ * I think we can get away with just using a regular page translation,
+ * just making sure the cacheable bit is off. I would like to avoid
+ * having to mess with the IOMMU if at all possible at first.
+ *
+ * Aparently IOMMU is only necessary for SBus devices, maybe VME too.
+ * We'll see...
+ */
+void
+srmmu_mapioaddr(unsigned long physaddr, unsigned long virt_addr,
+ int bus_type, int rdonly)
+{
+ pgd_t *pgdp;
+ pmd_t *pmdp;
+ pte_t *ptep;
+
+ pgdp = srmmu_pgd_offset(&init_task, virt_addr);
+ pmdp = srmmu_pmd_offset(pgdp, virt_addr);
+ ptep = srmmu_pte_offset(pmdp, virt_addr);
+ pte_val(*ptep) = (physaddr >> SRMMU_PTE_PPN_PADDR_SHIFT) & SRMMU_PTE_PPN_MASK;
+
+ if(!rdonly)
+ pte_val(*ptep) |= (SRMMU_ACC_S_RDWREXEC | SRMMU_ET_PTE);
+ else
+ pte_val(*ptep) |= (SRMMU_ACC_S_RDEXEC | SRMMU_ET_PTE);
+
+ pte_val(*ptep) |= (bus_type << 28);
+ pte_val(*ptep) &= ~(SRMMU_PTE_C_MASK); /* Make sure cacheable bit is off. */
+ srmmu_flush_whole_tlb();
+ flush_ei_ctx(0x0);
+
+ return;
+}
+
+/* Perfom a some soft of MMU tablewalk.
+ * Long contiguous mappings are not supported (yet ?).
+ *
+ * Origionally written by Peter Zaitcev, modified by David S.
+ * Miller. This is only used to copy over the PROM/KADB mappings
+ * in srmmu_paging_init().
+ *
+ * The return value encodes at what level the entry was found,
+ * basically this is found in the lower 2 bits of the return
+ * value. If the return value is zero, there was no valid mapping
+ * found at all, the low bits for a non-zero return value
+ * are:
+ * 0 -- Level 1 PTE
+ * 1 -- Level 2 PTE
+ * 2 -- Normal level 3 PTE
+ * 3 -- Context Table PTE (unlikely, but still)
+ *
+ * Also note that this is called before the context table pointer
+ * register is changed, so the PROMs entry is still in there. Also,
+ * it is safe to assume that the context 0 contains the mappings.
+ */
+/* TODO chop out 'trace' when stable */
+unsigned int
+srmmu_init_twalk(unsigned virt, int trace)
+{
+ unsigned int wh, root;
+
+ root = (unsigned int) srmmu_get_ctable_ptr();
+ if(trace) printk(":0x%x >> ", virt);
+
+ if(trace) printk(" 0x%x :", root);
+ wh = ldw_sun4m_bypass(root);
+ if ((wh & SRMMU_PTE_ET_MASK) == SRMMU_ET_INVALID) {
+ if(trace) printk("\n");
+ return 0;
+ }
+ if((wh & SRMMU_PTE_ET_MASK) == SRMMU_ET_PTE) {
+ wh &= ~SRMMU_PTE_ET_MASK;
+ wh |= 0x3;
+ if(trace) printk("\n");
+ printk("AIEEE context table level pte prom mapping!\n");
+ prom_halt();
+ return 0;
+ }
+
+ if(trace) printk(" 0x%x .", wh);
+ wh = ldw_sun4m_bypass(
+ ((wh & SRMMU_PTD_PTP_MASK) << 4)
+ + ((virt & SRMMU_IDX1_MASK) >> SRMMU_IDX1_SHIFT)*sizeof(pte_t));
+
+ if ((wh & SRMMU_PTE_ET_MASK) == SRMMU_ET_INVALID) {
+ if(trace) printk("\n");
+ return 0;
+ }
+ if((wh & SRMMU_PTE_ET_MASK) == SRMMU_ET_PTE) {
+ wh &= ~SRMMU_PTE_ET_MASK;
+ if(trace) printk("\n");
+ return wh;
+ }
+
+ if(trace) printk(" 0x%x .", wh);
+ wh = ldw_sun4m_bypass(
+ ((wh & SRMMU_PTD_PTP_MASK) << 4)
+ + ((virt & SRMMU_IDX2_MASK) >> SRMMU_IDX2_SHIFT)*sizeof(pte_t));
+ if ((wh & SRMMU_PTE_ET_MASK) == SRMMU_ET_INVALID) {
+ if(trace) printk("\n");
+ return 0;
+ }
+ if((wh & SRMMU_PTE_ET_MASK) == SRMMU_ET_PTE) {
+ wh &= ~SRMMU_PTE_ET_MASK;
+ wh |= 0x1;
+ if(trace) printk("\n");
+ return wh;
+ }
+
+ if(trace) printk(" 0x%x .", wh);
+ wh = ldw_sun4m_bypass(
+ ((wh & SRMMU_PTD_PTP_MASK) << 4)
+ + ((virt & SRMMU_IDX3_MASK) >> SRMMU_IDX3_SHIFT)*sizeof(pte_t));
+ if ((wh & SRMMU_PTE_ET_MASK) == SRMMU_ET_INVALID) {
+ if(trace) printk("\n");
+ return 0;
+ }
+ if(trace) printk(" 0x%x\n", wh);
+ return wh;
+}
+
+
+/* Allocate a block of RAM which is aligned to its size.
+ * This procedure can be used until the call to mem_init().
+ *
+ * To get around the elf bootloader nastyness we have a
+ * early-on page table pool allocation area starting at
+ * C_LABEL(pg0) which is 256k, this should be enough for now.
+ */
+static void *
+srmmu_init_alloc(unsigned long *kbrk, unsigned size)
+{
+ register unsigned mask = size - 1;
+ register unsigned long ret;
+
+ if(size==0) return 0x0;
+ if(size & mask) {
+ printk("panic: srmmu_init_alloc botch\n");
+ prom_halt();
+ }
+ ret = (*kbrk + mask) & ~mask;
+ *kbrk = ret + size;
+ memset((void*) ret, 0, size);
+ return (void*) ret;
+}
+
+extern unsigned long srmmu_data_fault, srmmu_text_fault;
+
+/* Patch in the SRMMU fault handlers for the trap table. */
+void
+srmmu_patch_fhandlers(void)
+{
+ /* Say the following ten times fast... */
+ sparc_ttable[SP_TRAP_TFLT].inst_one = SPARC_MOV_CONST_L3(0x1);
+ sparc_ttable[SP_TRAP_TFLT].inst_two =
+ SPARC_BRANCH((unsigned long) &srmmu_text_fault,
+ (unsigned long) &sparc_ttable[SP_TRAP_TFLT].inst_two);
+ sparc_ttable[SP_TRAP_TFLT].inst_three = SPARC_RD_PSR_L0;
+ sparc_ttable[SP_TRAP_TFLT].inst_four = SPARC_NOP;
+
+ sparc_ttable[SP_TRAP_DFLT].inst_one = SPARC_MOV_CONST_L3(0x9);
+ sparc_ttable[SP_TRAP_DFLT].inst_two =
+ SPARC_BRANCH((unsigned long) &srmmu_data_fault,
+ (unsigned long) &sparc_ttable[SP_TRAP_DFLT].inst_two);
+ sparc_ttable[SP_TRAP_DFLT].inst_three = SPARC_RD_PSR_L0;
+ sparc_ttable[SP_TRAP_DFLT].inst_four = SPARC_NOP;
+
+ return;
+}
+
+/* Paging initialization on the Sparc Reference MMU. */
+
+/* This is all poorly designed, we cannot assume any pages are valid
+ * past _end until *after* this routine runs, thus we can't use the
+ * start_mem mechanism during initialization...
+ */
+static unsigned long mempool;
+
+/* The following is global because trap_init needs it to fire up
+ * the other cpu's on multiprocessors.
+ */
+pgd_t *lnx_root; /* Pointer to the new root table */
+
+extern char start[];
+
+unsigned long
+srmmu_paging_init(unsigned long start_mem, unsigned long end_mem)
+{
+ unsigned long vaddr; /* Virtual counter */
+ int i;
+
+ pte_t *ptep = 0;
+ pmd_t *pmdp = 0;
+ pgd_t *pgdp = 0;
+
+ mempool = start_mem;
+ lnx_root = srmmu_init_alloc(&mempool, num_contexts*sizeof(pgd_t));
+
+ memset(swapper_pg_dir, 0, PAGE_SIZE);
+
+ /* For every entry in the new Linux context table, put in
+ * an entry which points to swapper_pg_dir .
+ */
+ pmdp = (pmd_t *) swapper_pg_dir;
+ for(i = 0; i < num_contexts; i++)
+ srmmu_pgd_set(&lnx_root[i], pmdp);
+
+ /* Make Linux physical page tables. */
+ for(vaddr = KERNBASE; vaddr < end_mem; vaddr+=PAGE_SIZE) {
+ pgdp = srmmu_pgd_offset(&init_task, vaddr);
+ if(srmmu_pgd_none(*pgdp)) {
+ pmdp = srmmu_init_alloc(&mempool,
+ SRMMU_PTRS_PER_PMD*sizeof(pmd_t));
+ srmmu_pgd_set(pgdp, pmdp);
+ }
+
+ pmdp = srmmu_pmd_offset(pgdp, vaddr);
+ if(srmmu_pmd_none(*pmdp)) {
+ ptep = srmmu_init_alloc(&mempool,
+ SRMMU_PTRS_PER_PTE*sizeof(pte_t));
+ srmmu_pmd_set(pmdp, ptep);
+ }
+
+ ptep = srmmu_pte_offset(pmdp, vaddr);
+ *ptep = srmmu_mk_pte(vaddr, SRMMU_PAGE_KERNEL);
+ }
+
+ /* Map IO areas. */
+ for(vaddr = IOBASE_VADDR; vaddr < (IOBASE_VADDR+IOBASE_LEN);
+ vaddr += SRMMU_PMD_SIZE) {
+ pgdp = srmmu_pgd_offset(&init_task, vaddr);
+ if(srmmu_pgd_none(*pgdp)) {
+ pmdp = srmmu_init_alloc(&mempool,
+ SRMMU_PTRS_PER_PMD*sizeof(pmd_t));
+ srmmu_pgd_set(pgdp, pmdp);
+ }
+ pmdp = srmmu_pmd_offset(pgdp, vaddr);
+ if(srmmu_pmd_none(*pmdp)) {
+ ptep = srmmu_init_alloc(&mempool,
+ SRMMU_PTRS_PER_PTE*sizeof(pte_t));
+ srmmu_pmd_set(pmdp, ptep);
+ }
+ }
+
+ /* Map in the PERCPU areas in virtual address space. */
+ printk("PERCPU_VADDR + PERCPU_LEN = %08lx\n",
+ (PERCPU_VADDR + PERCPU_LEN));
+ for(vaddr = PERCPU_VADDR; vaddr < (PERCPU_VADDR + PERCPU_LEN);
+ vaddr += PERCPU_ENTSIZE) {
+ pgdp = srmmu_pgd_offset(&init_task, vaddr);
+ if(srmmu_pgd_none(*pgdp)) {
+ pmdp = srmmu_init_alloc(&mempool,
+ SRMMU_PTRS_PER_PMD*sizeof(pmd_t));
+ srmmu_pgd_set(pgdp, pmdp);
+ }
+ pmdp = srmmu_pmd_offset(pgdp, vaddr);
+ if(srmmu_pmd_none(*pmdp)) {
+ ptep = srmmu_init_alloc(&mempool,
+ SRMMU_PTRS_PER_PTE*sizeof(pte_t));
+ srmmu_pmd_set(pmdp, ptep);
+ }
+ ptep = srmmu_pte_offset(pmdp, vaddr);
+ /* Per-cpu trap table page. */
+ *ptep++ = srmmu_mk_pte((unsigned int) start, SRMMU_PAGE_KERNEL);
+ /* Per-cpu kernel stack page. */
+ *ptep++ = srmmu_mk_pte((unsigned int) srmmu_init_alloc(&mempool, PAGE_SIZE),
+ SRMMU_PAGE_KERNEL);
+ /* Per-cpu Prom MBox. */
+ *ptep++ = srmmu_mk_pte((unsigned int) srmmu_init_alloc(&mempool, PAGE_SIZE),
+ SRMMU_PAGE_KERNEL);
+ /* Per-cpu state variables. */
+ *ptep = srmmu_mk_pte((unsigned int) srmmu_init_alloc(&mempool, PAGE_SIZE),
+ SRMMU_PAGE_KERNEL);
+ }
+ percpu_table = (struct sparc_percpu *) PERCPU_VADDR;
+
+ /* Ugh, have to map DVMA that the prom has mapped too or else
+ * you will lose with video cards when we take over the ctx table.
+ * Also, must take into consideration that prom might be using level
+ * two or one PTE's. TODO
+ */
+ for(vaddr = KADB_DEBUGGER_BEGVM; vaddr != 0x0;) {
+ unsigned int prom_pte;
+
+ prom_pte = srmmu_init_twalk(vaddr, 0);
+
+ if(prom_pte) {
+ pgdp = srmmu_pgd_offset(&init_task, vaddr);
+ if((prom_pte&0x3) == 0x0) {
+ prom_pte &= ~0x3;
+ prom_pte |= SRMMU_ET_PTE;
+ pgd_val(*pgdp) = prom_pte;
+ vaddr = SRMMU_PGDIR_ALIGN(vaddr+1);
+ continue;
+ }
+ if(srmmu_pgd_none(*pgdp)) {
+ pmdp = srmmu_init_alloc(&mempool,
+ SRMMU_PTRS_PER_PMD*sizeof(pmd_t));
+ srmmu_pgd_set(pgdp, pmdp);
+ }
+
+ pmdp = srmmu_pmd_offset(pgdp, vaddr);
+ if((prom_pte&0x3) == 0x1) {
+ prom_pte &= ~0x3;
+ prom_pte |= SRMMU_ET_PTE;
+ pgd_val(*pgdp) = prom_pte;
+ vaddr = SRMMU_PMD_ALIGN(vaddr+1);
+ continue;
+ }
+ if(srmmu_pmd_none(*pmdp)) {
+ ptep = srmmu_init_alloc(&mempool,
+ SRMMU_PTRS_PER_PTE*sizeof(pte_t));
+ srmmu_pmd_set(pmdp, ptep);
+ }
+ /* A normal 3rd level PTE, no need to change ET bits. */
+ ptep = srmmu_pte_offset(pmdp, vaddr);
+ pte_val(*ptep) = prom_pte;
+
+ }
+ vaddr += PAGE_SIZE;
+ }
+
+ /* I believe I do not need to flush VAC here since my stores */
+ /* probably already reached the physical RAM. --P3 */
+
+ /* We probably do, and should do it just to be safe... -Davem */
+
+ /* Take the MMU over from the PROM */
+ printk("Taking over MMU from PROM.\n");
+
+ srmmu_set_ctable_ptr(srmmu_virt_to_phys((unsigned)lnx_root));
+
+ srmmu_flush_whole_tlb();
+
+ /* Now it is ok to use memory at start_mem. */
+ start_mem = PAGE_ALIGN(mempool);
+ start_mem = free_area_init(start_mem, end_mem);
+ start_mem = PAGE_ALIGN(start_mem);
+
+#if 0
+ printk("Testing context switches...\n");
+ for(i=0; i<num_contexts; i++)
+ srmmu_set_context(i);
+ printk("done...\n");
+ srmmu_set_context(0);
+#endif
+
+ printk("survived...\n");
+ return start_mem;
+}
+
+/* Test the WP bit on the Sparc Reference MMU. */
+void
+srmmu_test_wp(void)
+{
+ pgd_t *pgdp;
+ pmd_t *pmdp;
+ pte_t *ptep;
+
+ wp_works_ok = -1;
+ /* We mapped page zero as a read-only page in paging_init()
+ * So fire up the test, then invalidate the pgd for page zero.
+ * It is no longer needed.
+ */
+
+ /* Let it rip... */
+ __asm__ __volatile__("st %%g0, [0x0]\n\t": : :"memory");
+ if (wp_works_ok < 0)
+ wp_works_ok = 0;
+
+ pgdp = srmmu_pgd_offset(&init_task, 0x0);
+ pgd_val(*pgdp) = 0x0;
+
+ return;
+}
+
+/* Load up routines and constants for sun4m mmu */
+void
+ld_mmu_srmmu(void)
+{
+ printk("Loading srmmu MMU routines\n");
+
+ /* First the constants */
+ pmd_shift = SRMMU_PMD_SHIFT;
+ pmd_size = SRMMU_PMD_SIZE;
+ pmd_mask = SRMMU_PMD_MASK;
+ pgdir_shift = SRMMU_PGDIR_SHIFT;
+ pgdir_size = SRMMU_PGDIR_SIZE;
+ pgdir_mask = SRMMU_PGDIR_MASK;
+
+ ptrs_per_pte = SRMMU_PTRS_PER_PTE;
+ ptrs_per_pmd = SRMMU_PTRS_PER_PMD;
+ ptrs_per_pgd = SRMMU_PTRS_PER_PGD;
+
+ page_none = SRMMU_PAGE_NONE;
+ page_shared = SRMMU_PAGE_SHARED;
+ page_copy = SRMMU_PAGE_COPY;
+ page_readonly = SRMMU_PAGE_READONLY;
+ page_kernel = SRMMU_PAGE_KERNEL;
+ page_invalid = SRMMU_PAGE_INVALID;
+
+ /* Functions */
+ invalidate = srmmu_invalidate;
+ switch_to_context = srmmu_switch_to_context;
+ pmd_align = srmmu_pmd_align;
+ pgdir_align = srmmu_pgdir_align;
+ vmalloc_start = srmmu_vmalloc_start;
+
+ pte_page = srmmu_pte_page;
+ pmd_page = srmmu_pmd_page;
+ pgd_page = srmmu_pgd_page;
+
+ sparc_update_rootmmu_dir = srmmu_update_rootmmu_dir;
+
+ pte_none = srmmu_pte_none;
+ pte_present = srmmu_pte_present;
+ pte_inuse = srmmu_pte_inuse;
+ pte_clear = srmmu_pte_clear;
+ pte_reuse = srmmu_pte_reuse;
+
+ pmd_none = srmmu_pmd_none;
+ pmd_bad = srmmu_pmd_bad;
+ pmd_present = srmmu_pmd_present;
+ pmd_inuse = srmmu_pmd_inuse;
+ pmd_clear = srmmu_pmd_clear;
+ pmd_reuse = srmmu_pmd_reuse;
+
+ pgd_none = srmmu_pgd_none;
+ pgd_bad = srmmu_pgd_bad;
+ pgd_present = srmmu_pgd_present;
+ pgd_inuse = srmmu_pgd_inuse;
+ pgd_clear = srmmu_pgd_clear;
+ pgd_reuse = srmmu_pgd_reuse;
+
+ mk_pte = srmmu_mk_pte;
+ pgd_set = srmmu_pgd_set; /* XXX needs a cast */
+ pte_modify = srmmu_pte_modify;
+ pgd_offset = srmmu_pgd_offset;
+ pmd_offset = srmmu_pmd_offset;
+ pte_offset = srmmu_pte_offset;
+ pte_free_kernel = srmmu_pte_free_kernel;
+ pmd_free_kernel = srmmu_pmd_free_kernel;
+ pte_alloc_kernel = srmmu_pte_alloc_kernel;
+ pmd_alloc_kernel = srmmu_pmd_alloc_kernel;
+ pte_free = srmmu_pte_free;
+ pte_alloc = srmmu_pte_alloc;
+ pmd_free = srmmu_pmd_free;
+ pmd_alloc = srmmu_pmd_alloc;
+ pgd_free = srmmu_pgd_free;
+ pgd_alloc = srmmu_pgd_alloc;
+
+ pte_read = srmmu_pte_read;
+ pte_write = srmmu_pte_write;
+ pte_exec = srmmu_pte_exec;
+ pte_dirty = srmmu_pte_dirty;
+ pte_young = srmmu_pte_young;
+ pte_cow = srmmu_pte_cow;
+ pte_wrprotect = srmmu_pte_wrprotect;
+ pte_rdprotect = srmmu_pte_rdprotect;
+ pte_exprotect = srmmu_pte_exprotect;
+ pte_mkclean = srmmu_pte_mkclean;
+ pte_mkold = srmmu_pte_mkold;
+ pte_uncow = srmmu_pte_uncow;
+ pte_mkwrite = srmmu_pte_mkwrite;
+ pte_mkread = srmmu_pte_mkread;
+ pte_mkexec = srmmu_pte_mkexec;
+ pte_mkdirty = srmmu_pte_mkdirty;
+ pte_mkyoung = srmmu_pte_mkyoung;
+ pte_mkcow = srmmu_pte_mkcow;
+
+ return;
+}
+
--- /dev/null
+/* srmmuinv.c: Invalidate routines for the various different
+ * SRMMU implementations.
+ *
+ * Copyright (C) 1995 David S. Miller
+ */
+
+/* HyperSparc */
+hyper_invalidate(void)
+{
+ volatile unsigned int sfsr_clear;
+
+ /* Flush TLB */
+ srmmu_flush_whole_tlb();
+
+ /* Flush Virtual Address Cache */
+
+ /* Flush ICACHE */
+ flush_whole_icache();
+ sfsr_clear = srmmu_get_fstatus();
+ return;
+}
+
+hyper_invalidate_mp(void)
+{
+ volatile unsigned int sfsr_clear;
+
+ /* Flush TLB */
+ srmmu_flush_whole_tlb();
+
+ /* Flush Virtual Address Cache */
+
+ /* Flush ICACHE */
+ flush_whole_icache();
+
+ sfsr_clear = srmmu_get_fstatus();
+
+ /* Tell other CPUS to each call the Uniprocessor
+ * invalidate routine.
+ */
+
+ return;
+}
+
+/* Cypress */
+void
+cypress_invalidate(void)
+{
+ /* Flush TLB */
+ srmmu_flush_whole_tlb();
+
+ /* Flush Virtual Address Cache */
+
+ return;
+}
+
+void
+cypress_invalidate_mp(void)
+{
+ /* Flush TLB */
+ srmmu_flush_whole_tlb();
+
+ /* Flush Virtual Address Cache */
+
+ /* Tell other CPUS to call the UP version */
+
+ return;
+}
+
+void
+cypress_invalidate_asibad(void)
+{
+ /* Flush TLB */
+ srmmu_flush_whole_tlb();
+
+ /* Flush Virtual Address Cache w/o using ASIs */
+
+ return;
+}
+
+void
+cypress_invalidate_asibad_mp(void)
+{
+ /* Flush TLB */
+ srmmu_flush_whole_tlb();
+
+ /* Flush Virtual Address Cache w/o using ASIs */
+
+ /* Tell other CPUS to call the UP version */
+
+ return;
+}
+
+/* Swift */
+void
+swift_invalidate(void)
+{
+ /* Flush TLB */
+ srmmu_flush_whole_tlb();
+
+ /* Flush Virtual Address Cache */
+
+ return;
+}
+
+void
+swift_invalidate_poke_kernel_pageperms(void)
+{
+ /* Flush TLB */
+ srmmu_flush_whole_tlb();
+
+ /* Flush Virtual Address Cache */
+
+ return;
+}
+
+void
+swift_invalidate_poke_kernel_pte_cbits(void)
+{
+ /* Flush TLB */
+ srmmu_flush_whole_tlb();
+
+ /* Flush Virtual Address Cache */
+
+ return;
+}
+
+void
+swift_invalidate_poke_everything(void)
+{
+ /* Flush TLB */
+ srmmu_flush_whole_tlb();
+
+ /* Flush Virtual Address Cache */
+
+ return;
+}
+
+/* Tsunami */
+tsunami_invalidate()
+{
+ /* Flush TLB */
+ srmmu_flush_whole_tlb();
+
+ /* Flush Virtual Address Cache */
+
+ return;
+}
+
+/* Viking */
+viking_invalidate()
+{
+ /* Flush TLB */
+ srmmu_flush_whole_tlb();
+
+ return;
+}
+
+viking_invalidate_mp()
+{
+ /* Flush TLB */
+ srmmu_flush_whole_tlb();
+
+ /* Make other CPUS call UP routine. */
+
+ return;
+}
+
+/* That should be it */
--- /dev/null
+/* sun4c.c: Sun4C specific mm routines.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+/* The SUN4C has an MMU based upon a Translation Lookaside Buffer scheme
+ * where only so many translations can be loaded at once. As Linus said
+ * in Boston, this is a broken way of doing things.
+ *
+ * NOTE: Free page pool and tables now live in high memory, see
+ * asm-sparc/pgtsun4c.c and asm-sparc/page.h for details.
+ */
+
+#include <linux/kernel.h> /* for printk */
+#include <linux/sched.h>
+
+#include <asm/processor.h> /* for wp_works_ok */
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/vac-ops.h>
+#include <asm/vaddrs.h>
+#include <asm/asi.h>
+#include <asm/contregs.h>
+#include <asm/kdebug.h>
+
+unsigned int sun4c_pmd_align(unsigned int addr) { return SUN4C_PMD_ALIGN(addr); }
+unsigned int sun4c_pgdir_align(unsigned int addr) { return SUN4C_PGDIR_ALIGN(addr); }
+
+extern int num_segmaps, num_contexts;
+
+/* Idea taken from Hamish McDonald's MC680x0 Linux code, nice job.
+ * The only function that actually uses this is sun4c_mk_pte() and
+ * to have a complete physical ram structure walk happen for each
+ * invocation is quite costly. However, this does do some nice
+ * sanity checking and we'll see when our maps don't match. Eventually
+ * when I trust my code I will just do a direct mmu probe in mk_pte().
+ */
+static inline unsigned int sun4c_virt_to_phys(unsigned int vaddr)
+{
+ unsigned int paddr = 0;
+ unsigned int voff = (vaddr - PAGE_OFFSET);
+ int i;
+
+ for(i=0; sp_banks[i].num_bytes != 0; i++) {
+ if(voff < paddr + sp_banks[i].num_bytes) {
+ /* This matches. */
+ return sp_banks[i].base_addr + voff - paddr;
+ } else
+ paddr += sp_banks[i].num_bytes;
+ }
+ /* Shit, gotta consult the MMU, this shouldn't happen... */
+ printk("sun4c_virt_to_phys: Could not make translation for vaddr %08lx\n", (unsigned long) vaddr);
+ SP_ENTER_DEBUGGER;
+}
+
+static inline unsigned long
+sun4c_phys_to_virt(unsigned long paddr)
+{
+ int i;
+ unsigned long offset = PAGE_OFFSET;
+
+ for (i=0; sp_banks[i].num_bytes != 0; i++)
+ {
+ if (paddr >= sp_banks[i].base_addr &&
+ paddr < (sp_banks[i].base_addr
+ + sp_banks[i].num_bytes)) {
+ return (paddr - sp_banks[i].base_addr) + offset;
+ } else
+ offset += sp_banks[i].num_bytes;
+ }
+ printk("sun4c_phys_to_virt: Could not make translation for paddr %08lx\n", (unsigned long) paddr);
+ SP_ENTER_DEBUGGER;
+}
+
+unsigned long
+sun4c_vmalloc_start(void)
+{
+ return ((high_memory + SUN4C_VMALLOC_OFFSET) & ~(SUN4C_VMALLOC_OFFSET-1));
+}
+
+/* Note that I have 16 page tables per page, thus four less
+ * bits of shifting than normal.
+ */
+
+unsigned long
+sun4c_pte_page(pte_t pte)
+{
+ unsigned long page;
+
+ page = ((pte_val(pte) & _SUN4C_PFN_MASK) << (PAGE_SHIFT));
+ return sun4c_phys_to_virt(page);
+}
+
+unsigned long
+sun4c_pmd_page(pmd_t pmd)
+{
+ return ((pmd_val(pmd) & _SUN4C_PGD_PFN_MASK) << (_SUN4C_PGD_PAGE_SHIFT));
+}
+
+unsigned long
+sun4c_pgd_page(pgd_t pgd)
+{
+ return ((pgd_val(pgd) & _SUN4C_PGD_PFN_MASK) << (_SUN4C_PGD_PAGE_SHIFT));
+}
+
+/* Update the root mmu directory on the sun4c mmu. */
+void
+sun4c_update_rootmmu_dir(struct task_struct *tsk, pgd_t *pgdir)
+{
+ (tsk)->tss.pgd_ptr = (unsigned long) (pgdir);
+
+ /* May have to do some flushing here. */
+
+ return;
+}
+
+int sun4c_pte_none(pte_t pte) { return !pte_val(pte); }
+int sun4c_pte_present(pte_t pte) { return pte_val(pte) & _SUN4C_PAGE_VALID; }
+int sun4c_pte_inuse(pte_t *ptep) { return mem_map[MAP_NR(ptep)] != 1; }
+void sun4c_pte_clear(pte_t *ptep) { pte_val(*ptep) = 0; }
+void sun4c_pte_reuse(pte_t *ptep)
+{
+ if(!(mem_map[MAP_NR(ptep)] & MAP_PAGE_RESERVED))
+ mem_map[MAP_NR(ptep)]++;
+}
+
+int sun4c_pmd_none(pmd_t pmd) { return !pmd_val(pmd); }
+int sun4c_pmd_bad(pmd_t pmd)
+{
+ return ((pmd_val(pmd) & _SUN4C_PGD_MMU_MASK) != _SUN4C_PAGE_TABLE);
+}
+
+int sun4c_pmd_present(pmd_t pmd) { return pmd_val(pmd) & _SUN4C_PAGE_VALID; }
+int sun4c_pmd_inuse(pmd_t *pmdp) { return 0; }
+void sun4c_pmd_clear(pmd_t *pmdp) { pmd_val(*pmdp) = 0; }
+void sun4c_pmd_reuse(pmd_t * pmdp) { }
+
+int sun4c_pgd_none(pgd_t pgd) { return 0; }
+int sun4c_pgd_bad(pgd_t pgd) { return 0; }
+int sun4c_pgd_present(pgd_t pgd) { return 1; }
+int sun4c_pgd_inuse(pgd_t *pgdp) { return mem_map[MAP_NR(pgdp)] != 1; }
+void sun4c_pgd_clear(pgd_t * pgdp) { }
+void sun4c_pgd_reuse(pgd_t *pgdp)
+{
+ if (!(mem_map[MAP_NR(pgdp)] & MAP_PAGE_RESERVED))
+ mem_map[MAP_NR(pgdp)]++;
+}
+
+/*
+ * The following only work if pte_present() is true.
+ * Undefined behaviour if not..
+ */
+int sun4c_pte_read(pte_t pte) { return pte_val(pte) & _SUN4C_PAGE_VALID; }
+int sun4c_pte_write(pte_t pte) { return pte_val(pte) & _SUN4C_PAGE_WRITE; }
+int sun4c_pte_exec(pte_t pte) { return pte_val(pte) & _SUN4C_PAGE_VALID; }
+int sun4c_pte_dirty(pte_t pte) { return pte_val(pte) & _SUN4C_PAGE_DIRTY; }
+int sun4c_pte_young(pte_t pte) { return pte_val(pte) & _SUN4C_PAGE_REF; }
+int sun4c_pte_cow(pte_t pte) { return pte_val(pte) & _SUN4C_PAGE_COW; }
+
+pte_t sun4c_pte_wrprotect(pte_t pte) { pte_val(pte) &= ~_SUN4C_PAGE_WRITE; return pte; }
+pte_t sun4c_pte_rdprotect(pte_t pte) { pte_val(pte) |= _SUN4C_PAGE_PRIV; return pte; }
+pte_t sun4c_pte_exprotect(pte_t pte) { pte_val(pte) |= _SUN4C_PAGE_PRIV; return pte; }
+pte_t sun4c_pte_mkclean(pte_t pte) { pte_val(pte) &= ~_SUN4C_PAGE_DIRTY; return pte; }
+pte_t sun4c_pte_mkold(pte_t pte) { pte_val(pte) &= ~_SUN4C_PAGE_REF; return pte; }
+pte_t sun4c_pte_uncow(pte_t pte) { pte_val(pte) &= ~_SUN4C_PAGE_COW; return pte; }
+pte_t sun4c_pte_mkwrite(pte_t pte) { pte_val(pte) |= _SUN4C_PAGE_WRITE; return pte; }
+pte_t sun4c_pte_mkread(pte_t pte) { pte_val(pte) &= ~_SUN4C_PAGE_PRIV; return pte; }
+pte_t sun4c_pte_mkexec(pte_t pte) { pte_val(pte) &= ~_SUN4C_PAGE_PRIV; return pte; }
+pte_t sun4c_pte_mkdirty(pte_t pte) { pte_val(pte) |= _SUN4C_PAGE_DIRTY; return pte; }
+pte_t sun4c_pte_mkyoung(pte_t pte) { pte_val(pte) |= _SUN4C_PAGE_REF; return pte; }
+pte_t sun4c_pte_mkcow(pte_t pte) { pte_val(pte) |= _SUN4C_PAGE_COW; return pte; }
+
+/*
+ * Conversion functions: convert a page and protection to a page entry,
+ * and a page entry and page directory to the page they refer to.
+ */
+pte_t
+sun4c_mk_pte(unsigned long page, pgprot_t pgprot)
+{
+ pte_t pte;
+
+ if(page & (~PAGE_MASK)) panic("sun4c_mk_pte() called with unaligned page");
+ page = sun4c_virt_to_phys(page);
+ pte_val(pte) = ((page>>PAGE_SHIFT)&_SUN4C_PFN_MASK);
+ pte_val(pte) |= (pgprot_val(pgprot) & _SUN4C_MMU_MASK);
+ return pte;
+}
+
+void
+sun4c_pgd_set(pgd_t * pgdp, pte_t * ptep)
+{
+ pgd_val(*pgdp) = (_SUN4C_PAGE_TABLE & _SUN4C_PGD_MMU_MASK);
+ pgd_val(*pgdp) |= (((((unsigned long) ptep)) >>
+ (_SUN4C_PGD_PAGE_SHIFT)) & _SUN4C_PGD_PFN_MASK);
+}
+
+pte_t
+sun4c_pte_modify(pte_t pte, pgprot_t newprot)
+{
+ pte_val(pte) = (pte_val(pte) & _SUN4C_PAGE_CHG_MASK);
+ pte_val(pte) |= pgprot_val(newprot);
+ return pte;
+}
+
+/* to find an entry in a page-table-directory */
+pgd_t *
+sun4c_pgd_offset(struct task_struct * tsk, unsigned long address)
+{
+ return ((pgd_t *) (tsk->tss.pgd_ptr)) +
+ (address >> SUN4C_PGDIR_SHIFT);
+}
+
+/* Find an entry in the second-level page table.. */
+pmd_t *
+sun4c_pmd_offset(pgd_t * dir, unsigned long address)
+{
+ return (pmd_t *) dir;
+}
+
+/* Find an entry in the third-level page table.. */
+pte_t *
+sun4c_pte_offset(pmd_t * dir, unsigned long address)
+{
+ return (pte_t *) sun4c_pmd_page(*dir) + ((address >> PAGE_SHIFT) & (SUN4C_PTRS_PER_PTE - 1));
+}
+
+/*
+ * Allocate and free page tables. The xxx_kernel() versions are
+ * used to allocate a kernel page table - this turns on ASN bits
+ * if any, and marks the page tables reserved.
+ */
+void
+sun4c_pte_free_kernel(pte_t *pte)
+{
+ mem_map[MAP_NR(pte)] = 1;
+ free_page((unsigned long) pte);
+}
+
+static inline void
+sun4c_pmd_set(pmd_t * pmdp, pte_t * ptep)
+{
+ pmd_val(*pmdp) = (_SUN4C_PAGE_TABLE & _SUN4C_PGD_MMU_MASK);
+ pmd_val(*pmdp) |= ((((unsigned long) ptep) >> (_SUN4C_PGD_PAGE_SHIFT)) & _SUN4C_PGD_PFN_MASK);
+}
+
+
+pte_t *
+sun4c_pte_alloc_kernel(pmd_t *pmd, unsigned long address)
+{
+ pte_t *page;
+
+
+ address = (address >> PAGE_SHIFT) & (SUN4C_PTRS_PER_PTE - 1);
+ if (sun4c_pmd_none(*pmd)) {
+ /* New scheme, use a whole page */
+ page = (pte_t *) get_free_page(GFP_KERNEL);
+ if (sun4c_pmd_none(*pmd)) {
+ if (page) {
+ sun4c_pmd_set(pmd, page);
+ mem_map[MAP_NR(page)] = MAP_PAGE_RESERVED;
+ return page + address;
+ }
+ sun4c_pmd_set(pmd, (pte_t *) BAD_PAGETABLE);
+ return NULL;
+ }
+ free_page((unsigned long) page);
+ }
+ if (sun4c_pmd_bad(*pmd)) {
+ printk("Bad pmd in pte_alloc_kernel: %08lx\n", pmd_val(*pmd));
+ sun4c_pmd_set(pmd, (pte_t *) BAD_PAGETABLE);
+ return NULL;
+ }
+
+ return (pte_t *) sun4c_pmd_page(*pmd) + address;
+}
+
+/*
+ * allocating and freeing a pmd is trivial: the 1-entry pmd is
+ * inside the pgd, so has no extra memory associated with it.
+ */
+void
+sun4c_pmd_free_kernel(pmd_t *pmd)
+{
+ return;
+}
+
+pmd_t *
+sun4c_pmd_alloc_kernel(pgd_t *pgd, unsigned long address)
+{
+ return (pmd_t *) pgd;
+}
+
+void
+sun4c_pte_free(pte_t *pte)
+{
+ free_page((unsigned long) pte);
+}
+
+pte_t *
+sun4c_pte_alloc(pmd_t * pmd, unsigned long address)
+{
+ pte_t *page;
+
+ address = (address >> PAGE_SHIFT) & (SUN4C_PTRS_PER_PTE - 1);
+ if (sun4c_pmd_none(*pmd)) {
+ page = (pte_t *) get_free_page(GFP_KERNEL);
+ if (sun4c_pmd_none(*pmd)) {
+ if (page) {
+ sun4c_pmd_set(pmd, page);
+ return page + address;
+ }
+ sun4c_pmd_set(pmd, (pte_t *) BAD_PAGETABLE);
+ return NULL;
+ }
+ free_page((unsigned long) page);
+ }
+ if (sun4c_pmd_bad(*pmd)) {
+ printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd));
+ sun4c_pmd_set(pmd, (pte_t *) BAD_PAGETABLE);
+ halt();
+ return NULL;
+ }
+
+ return (pte_t *) sun4c_pmd_page(*pmd) + address;
+}
+
+/*
+ * allocating and freeing a pmd is trivial: the 1-entry pmd is
+ * inside the pgd, so has no extra memory associated with it.
+ */
+void
+sun4c_pmd_free(pmd_t * pmd)
+{
+ return;
+}
+
+pmd_t *
+sun4c_pmd_alloc(pgd_t * pgd, unsigned long address)
+{
+ return (pmd_t *) pgd;
+}
+
+/* This now works, as both our pgd's and pte's have 1024 entries. */
+void
+sun4c_pgd_free(pgd_t *pgd)
+{
+ free_page((unsigned long) pgd);
+}
+
+/* A page directory on the sun4c needs 64k, thus we request an order of
+ * four. We must also clear it by hand, very inefficient.
+ */
+
+pgd_t *
+sun4c_pgd_alloc(void)
+{
+ return (pgd_t *) get_free_page(GFP_KERNEL);
+}
+
+void
+sun4c_invalidate(void)
+{
+ flush_vac_context();
+}
+
+void
+sun4c_switch_to_context(int context)
+{
+ __asm__ __volatile__("stba %0, [%1] %2" : :
+ "r" (context),
+ "r" (AC_CONTEXT), "i" (ASI_CONTROL));
+
+ return;
+}
+
+int
+sun4c_get_context(void)
+{
+ register int ctx;
+
+ __asm__ __volatile__("lduba [%1] %2, %0" :
+ "=r" (ctx) :
+ "r" (AC_CONTEXT), "i" (ASI_CONTROL));
+
+ return ctx;
+}
+
+/* Low level IO area allocation on the Sun4c MMU. This function is called
+ * for each page of IO area you need. Kernel code should not call this
+ * routine directly, use sparc_alloc_io() instead.
+ */
+void
+sun4c_mapioaddr(unsigned long physaddr, unsigned long virt_addr,
+ int bus_type, int rdonly)
+{
+ unsigned long page_entry;
+
+ page_entry = ((physaddr >> PAGE_SHIFT) & _SUN4C_PFN_MASK);
+
+ if(!rdonly)
+ page_entry |= (PTE_V | PTE_ACC | PTE_NC | PTE_IO); /* kernel io addr */
+ else
+ page_entry |= (PTE_V | PTE_P | PTE_NC | PTE_IO); /* readonly io addr */
+
+ page_entry &= (~PTE_RESV);
+
+ /* Maybe have to do something with the bus_type on sun4c's? */
+
+
+ put_pte(virt_addr, page_entry);
+ return;
+}
+
+/* Paging initialization on the Sun4c. */
+extern unsigned long free_area_init(unsigned long, unsigned long);
+extern unsigned long eintstack, intstack;
+
+/* This code was soooo krufty, I have to rewrite this now! XXX
+ * Ok, things are cleaning up. I have now decided that it makes
+ * a lot of sense to put the free page pool in upper ram right
+ * after the kernel. We map these free pages to be virtually
+ * contiguous, that way we don't get so many reserved pages
+ * during mem_init(). I think this will work out nicely.
+ */
+extern unsigned long start;
+
+static unsigned long mempool; /* This allows us to work with elf bootloaders */
+
+unsigned long
+sun4c_paging_init(unsigned long start_mem, unsigned long end_mem)
+{
+ unsigned long addr, vaddr, kern_begin, kern_end;
+ unsigned long prom_begin, prom_end;
+ int phys_seg, i, min_prom_segmap;
+ pgd_t *pgdp;
+ pmd_t *pmdp;
+ pte_t *ptep;
+
+ mempool = start_mem;
+
+ /* 127 on most sun4c's, 255 on SS2 and IPX. */
+ invalid_segment = (num_segmaps - 1);
+
+ memset(swapper_pg_dir, 0, PAGE_SIZE);
+ memset(pg0, 0, PAGE_SIZE);
+ /* Delete low mapping of the kernel and sanitize invalid segmap. */
+ for(vaddr=0; vaddr<(4*1024*1024); vaddr+=SUN4C_REAL_PGDIR_SIZE)
+ put_segmap(vaddr, invalid_segment);
+ for(vaddr=0; vaddr<(256*1024); vaddr+=PAGE_SIZE) put_pte(vaddr, 0);
+
+ /* Initialize phys_seg_map[] */
+ for(i=0; i<num_segmaps; i++) phys_seg_map[i] = PSEG_AVL;
+ for(i=num_segmaps; i<PSEG_ENTRIES; i++) phys_seg_map[i] = PSEG_RSV;
+
+ kern_begin = KERNBASE;
+ kern_end = ((unsigned long) &end);
+ prom_begin = LINUX_OPPROM_BEGVM;
+ prom_end = LINUX_OPPROM_ENDVM;
+
+ /* Set up swapper_pg_dir based upon three things:
+ * 1) Where the kernel lives (KERNBASE)
+ * 2) Where the PROM lives (PROM_BEGVM -> PROM_ENDVM)
+ * This is cheese, should do it dynamically XXX
+ * 3) Where the valid physical pages are (sp_banks[])
+ * This is done first.
+ *
+ * I'm trying to concentrate this into one big loop and localize
+ * the logic because it is so messy to do it in seperate loop
+ * stages. If anyone else has better ideas, let me know.
+ */
+
+ if(sp_banks[0].base_addr != 0)
+ panic("sun4c_paging_init: First physical address in first bank is not zero!\n");
+ /* First, linearly map all physical RAM to the equivalent virtual pages.
+ * Then, we invalidate everything the kernel uses by either invalidating
+ * the entire segmep (if the whole segment is used by the kernel) or
+ * just invalidating the relevant pte's.
+ */
+
+ for(vaddr = KERNBASE; vaddr < end_mem; vaddr+=PAGE_SIZE) {
+ pgdp = sun4c_pgd_offset(current, vaddr);
+ pmdp = sun4c_pmd_offset(pgdp, vaddr);
+ if(sun4c_pmd_none(*pmdp)) {
+ pgd_set(pgdp, (pte_t *) mempool);
+ mempool += PAGE_SIZE;
+ }
+ ptep = sun4c_pte_offset(pmdp, vaddr);
+ *ptep = sun4c_mk_pte(vaddr, SUN4C_PAGE_KERNEL);
+ }
+
+ /* Now map the kernel, and mark the segmaps as PSEG_KERN.
+ *
+ * NOTE: The first address of the upper kernel mapping must be
+ * segment aligned.
+ */
+ if(kern_begin & (~SUN4C_REAL_PGDIR_MASK)) {
+ panic("paging_init() Kernel not segmap aligned, halting...");
+ }
+
+ /* Mark the segmaps so that our phys_seg allocator doesn't try to
+ * use them for TLB misses.
+ */
+ for(addr=kern_begin; addr < kern_end; addr += SUN4C_REAL_PGDIR_SIZE) {
+ if(get_segmap(addr) == invalid_segment) {
+ panic("paging_init() AIEEE, Kernel has invalid mapping, halting...");
+ }
+ phys_seg = get_segmap(addr);
+ phys_seg_map[phys_seg] = PSEG_KERNEL;
+ /* Map this segment in every context */
+ for(i=0; i<num_contexts; i++)
+ (*romvec->pv_setctxt)(i, (char *) addr, phys_seg);
+ }
+
+ for(addr=((unsigned long) (&empty_zero_page)) + PAGE_SIZE;
+ addr < ((unsigned long) (&etext)); addr += PAGE_SIZE)
+ put_pte(addr, (get_pte(addr) & (~(PTE_W | PTE_NC))));
+
+ /* Finally map the prom's address space. Any segments that
+ * are not the invalid segment are marked as PSEG_RESV so
+ * they are never re-allocated. This guarentees the PROM
+ * a sane state if we have to return execution over to it.
+ * Our kernel static tables make it look like nothing is
+ * mapped in these segments, if we get a page fault for
+ * a prom address either the user is gonna die or the kernel
+ * is doing something *really* bad.
+ */
+ if(prom_begin & (~SUN4C_REAL_PGDIR_MASK)) {
+ panic("paging_init() Boot PROM not segmap aligned, halting...");
+ halt();
+ }
+
+ min_prom_segmap = 254;
+ for(addr=KADB_DEBUGGER_BEGVM; addr < prom_end; addr += SUN4C_REAL_PGDIR_SIZE) {
+ if(get_segmap(addr) == invalid_segment)
+ continue;
+ phys_seg = get_segmap(addr);
+ if(phys_seg < min_prom_segmap) min_prom_segmap = phys_seg;
+ phys_seg_map[phys_seg] = PSEG_RSV;
+ /* Make the prom pages unaccessible from userland. However, we
+ * don't touch debugger segmaps/ptes.
+ */
+ if((addr>=LINUX_OPPROM_BEGVM) && (addr<LINUX_OPPROM_ENDVM))
+ for(vaddr=addr; vaddr < (addr+SUN4C_REAL_PGDIR_SIZE); vaddr+=PAGE_SIZE)
+ put_pte(vaddr, (get_pte(vaddr) | PTE_P));
+
+ /* Map this segment in every context */
+ for(i=0; i<num_contexts; i++)
+ (*romvec->pv_setctxt)(i, (char *) addr, phys_seg);
+ }
+
+ /* Finally, unmap kernel page zero. */
+ put_pte(0x0, 0x0);
+
+ /* Hard pin down the IO area segmaps */
+ phys_seg = (min_prom_segmap - 1);
+ for(addr = (IOBASE_VADDR + SUN4C_REAL_PGDIR_SIZE); addr < (IOBASE_VADDR + IOBASE_LEN);
+ addr += SUN4C_REAL_PGDIR_SIZE) {
+ if(addr & (~SUN4C_REAL_PGDIR_MASK)) {
+ panic("paging_init() IO segment not aligned, halting...");
+ }
+ phys_seg_map[phys_seg] = PSEG_RSV; /* Don't touch */
+ put_segmap(addr, phys_seg--);
+ }
+ phys_seg_map[IOBASE_SUN4C_SEGMAP] = PSEG_RSV;
+
+ start_mem = PAGE_ALIGN(mempool);
+ start_mem = free_area_init(start_mem, end_mem);
+ start_mem = PAGE_ALIGN(start_mem);
+
+ /* That should be it. */
+ invalidate();
+
+ return start_mem;
+}
+
+/* Test the WP bit on the sun4c. */
+unsigned long
+sun4c_test_wp(unsigned long start_mem)
+{
+ unsigned long addr, segmap;
+ unsigned long page_entry;
+
+ wp_works_ok = -1;
+ page_entry = pte_val(sun4c_mk_pte(PAGE_OFFSET, SUN4C_PAGE_READONLY));
+ put_pte((unsigned long) 0x0, page_entry);
+
+ /* Let it rip... */
+ __asm__ __volatile__("st %%g0, [0x0]\n\t": : :"memory");
+ put_pte((unsigned long) 0x0, 0x0);
+ if (wp_works_ok < 0)
+ wp_works_ok = 0;
+
+ /* Make all kernet static segmaps PSEG_KERNEL. */
+ for(addr=PAGE_OFFSET; addr<start_mem; addr+=SUN4C_REAL_PGDIR_SIZE)
+ phys_seg_map[get_segmap(addr)]=PSEG_KERNEL;
+
+ /* Map all the segmaps not valid on this machine as reserved. */
+ for(segmap=invalid_segment; segmap<PSEG_ENTRIES; segmap++)
+ phys_seg_map[segmap]=PSEG_RSV;
+
+ return start_mem;
+}
+
+/* Real work gets done here. */
+
+/* Load up routines and constants for sun4c mmu */
+void
+ld_mmu_sun4c(void)
+{
+ printk("Loading sun4c MMU routines\n");
+
+ /* First the constants */
+ pmd_shift = SUN4C_PMD_SHIFT;
+ pmd_size = SUN4C_PMD_SIZE;
+ pmd_mask = SUN4C_PMD_MASK;
+ pgdir_shift = SUN4C_PGDIR_SHIFT;
+ pgdir_size = SUN4C_PGDIR_SIZE;
+ pgdir_mask = SUN4C_PGDIR_MASK;
+
+ ptrs_per_pte = SUN4C_PTRS_PER_PTE;
+ ptrs_per_pmd = SUN4C_PTRS_PER_PMD;
+ ptrs_per_pgd = SUN4C_PTRS_PER_PGD;
+
+ page_none = SUN4C_PAGE_NONE;
+ page_shared = SUN4C_PAGE_SHARED;
+ page_copy = SUN4C_PAGE_COPY;
+ page_readonly = SUN4C_PAGE_READONLY;
+ page_kernel = SUN4C_PAGE_KERNEL;
+ page_invalid = SUN4C_PAGE_INVALID;
+
+ /* Functions */
+ invalidate = sun4c_invalidate;
+ switch_to_context = sun4c_switch_to_context;
+ pmd_align = sun4c_pmd_align;
+ pgdir_align = sun4c_pgdir_align;
+ vmalloc_start = sun4c_vmalloc_start;
+
+ pte_page = sun4c_pte_page;
+ pmd_page = sun4c_pmd_page;
+ pgd_page = sun4c_pgd_page;
+
+ sparc_update_rootmmu_dir = sun4c_update_rootmmu_dir;
+
+ pte_none = sun4c_pte_none;
+ pte_present = sun4c_pte_present;
+ pte_inuse = sun4c_pte_inuse;
+ pte_clear = sun4c_pte_clear;
+ pte_reuse = sun4c_pte_reuse;
+
+ pmd_none = sun4c_pmd_none;
+ pmd_bad = sun4c_pmd_bad;
+ pmd_present = sun4c_pmd_present;
+ pmd_inuse = sun4c_pmd_inuse;
+ pmd_clear = sun4c_pmd_clear;
+ pmd_reuse = sun4c_pmd_reuse;
+
+ pgd_none = sun4c_pgd_none;
+ pgd_bad = sun4c_pgd_bad;
+ pgd_present = sun4c_pgd_present;
+ pgd_inuse = sun4c_pgd_inuse;
+ pgd_clear = sun4c_pgd_clear;
+ pgd_reuse = sun4c_pgd_reuse;
+
+ mk_pte = sun4c_mk_pte;
+ pgd_set = sun4c_pgd_set;
+ pte_modify = sun4c_pte_modify;
+ pgd_offset = sun4c_pgd_offset;
+ pmd_offset = sun4c_pmd_offset;
+ pte_offset = sun4c_pte_offset;
+ pte_free_kernel = sun4c_pte_free_kernel;
+ pmd_free_kernel = sun4c_pmd_free_kernel;
+ pte_alloc_kernel = sun4c_pte_alloc_kernel;
+ pmd_alloc_kernel = sun4c_pmd_alloc_kernel;
+ pte_free = sun4c_pte_free;
+ pte_alloc = sun4c_pte_alloc;
+ pmd_free = sun4c_pmd_free;
+ pmd_alloc = sun4c_pmd_alloc;
+ pgd_free = sun4c_pgd_free;
+ pgd_alloc = sun4c_pgd_alloc;
+
+ pte_read = sun4c_pte_read;
+ pte_write = sun4c_pte_write;
+ pte_exec = sun4c_pte_exec;
+ pte_dirty = sun4c_pte_dirty;
+ pte_young = sun4c_pte_young;
+ pte_cow = sun4c_pte_cow;
+ pte_wrprotect = sun4c_pte_wrprotect;
+ pte_rdprotect = sun4c_pte_rdprotect;
+ pte_exprotect = sun4c_pte_exprotect;
+ pte_mkclean = sun4c_pte_mkclean;
+ pte_mkold = sun4c_pte_mkold;
+ pte_uncow = sun4c_pte_uncow;
+ pte_mkwrite = sun4c_pte_mkwrite;
+ pte_mkread = sun4c_pte_mkread;
+ pte_mkexec = sun4c_pte_mkexec;
+ pte_mkdirty = sun4c_pte_mkdirty;
+ pte_mkyoung = sun4c_pte_mkyoung;
+ pte_mkcow = sun4c_pte_mkcow;
+
+ return;
+}
--- /dev/null
+#
+# Makefile for the Sun Boot PROM interface library under
+# Linux.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+OBJS = bootstr.o devmap.o devops.o init.o memory.o misc.o mp.o \
+ palloc.o ranges.o segment.o tree.o console.o printf.o
+
+all: promlib.a
+
+promlib.a: $(OBJS)
+ $(AR) rcs promlib.a $(OBJS)
+ sync
+
+dep:
+ $(CPP) -M *.c > .depend
+
+dummy:
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
--- /dev/null
+/* bootstr.c: Boot string/argument acquisition from the PROM.
+ *
+ * Copyright(C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/oplib.h>
+
+static char barg_buf[128];
+
+char *
+prom_getbootargs(void)
+{
+ int iter;
+ char *cp;
+
+ switch(prom_vers) {
+ case PROM_V0:
+ cp = barg_buf;
+ for(iter = 0; iter < 8; iter++) {
+ strcpy(cp, (*(romvec->pv_v0bootargs))->argv[iter]);
+ cp += strlen(cp); *cp++=' ';
+ }
+ *cp = 0;
+ break;
+ case PROM_V2:
+ case PROM_V3:
+ cp = barg_buf;
+ strcpy(cp, *romvec->pv_v2bootargs.bootpath);
+ cp += strlen(cp);
+ *cp++ = ' ';
+ strcpy(cp, *romvec->pv_v2bootargs.bootargs);
+ cp += strlen(cp);
+ *cp = 0;
+ break;
+ default:
+ barg_buf[0] = 0;
+ break;
+ }
+ return barg_buf;
+}
--- /dev/null
+/* console.c: Routines that deal with sending and receiving IO
+ * to/from the current console device using the PROM.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+
+/* Non blocking get character from console input device, returns -1
+ * if no input was taken. This can be used for polling.
+ */
+int
+prom_nbgetchar(void)
+{
+ static char inc;
+
+ switch(prom_vers) {
+ case PROM_V0:
+ return (*(romvec->pv_nbgetchar))();
+ break;
+ case PROM_V2:
+ case PROM_V3:
+ case PROM_P1275:
+ if( (*(romvec->pv_v2devops).v2_dev_read)(*romvec->pv_v2bootargs.fd_stdin , &inc, 0x1) == 1)
+ return inc;
+ return -1;
+ break;
+ };
+ return 0; /* Ugh, we could spin forever on unsupported proms ;( */
+}
+
+/* Non blocking put character to console device, returns -1 if
+ * unsuccessful.
+ */
+int
+prom_nbputchar(char c)
+{
+ static char outc;
+
+ switch(prom_vers) {
+ case PROM_V0:
+ return (*(romvec->pv_nbputchar))(c);
+ break;
+ case PROM_V2:
+ case PROM_V3:
+ case PROM_P1275:
+ outc = c;
+ if( (*(romvec->pv_v2devops).v2_dev_write)(*romvec->pv_v2bootargs.fd_stdout, &outc, 0x1) == 1)
+ return 0;
+ return -1;
+ break;
+ };
+ return 0; /* Ugh, we could spin forever on unsupported proms ;( */
+}
+
+/* Blocking version of get character routine above. */
+char
+prom_getchar(void)
+{
+ int character;
+ while((character = prom_nbgetchar()) == -1) ;
+ return (char) character;
+}
+
+/* Blocking version of put character routine above. */
+void
+prom_putchar(char c)
+{
+ while(prom_nbputchar(c) == -1) ;
+ return;
+}
+
--- /dev/null
+/* promdevmap.c: Map device/IO areas to virtual addresses.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+
+/* Just like the routines in palloc.c, these should not be used
+ * by the kernel at all. Bootloader facility mainly. And again,
+ * this is only available on V2 proms and above.
+ */
+
+/* Map physical device address 'paddr' in IO space 'ios' of size
+ * 'num_bytes' to a virtual address, with 'vhint' being a hint to
+ * the prom as to where you would prefer the mapping. We return
+ * where the prom actually mapped it.
+ */
+char *
+prom_mapio(char *vhint, int ios, unsigned int paddr, unsigned int num_bytes)
+{
+ if((num_bytes == 0) || (paddr == 0)) return (char *) 0x0;
+ return (*(romvec->pv_v2devops.v2_dumb_mmap))(vhint, ios, paddr,
+ num_bytes);
+}
+
+/* Unmap an IO/device area that was mapped using the above routine. */
+void
+prom_unmapio(char *vaddr, unsigned int num_bytes)
+{
+ if(num_bytes == 0x0) return;
+ (*(romvec->pv_v2devops.v2_dumb_munmap))(vaddr, num_bytes);
+ return;
+}
--- /dev/null
+/* devops.c: Device operations using the PROM.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+
+/* Open the device described by the string 'dstr'. Returns the handle
+ * to that device used for subsequent operations on that device.
+ * Returns -1 on failure.
+ */
+int
+prom_devopen(char *dstr)
+{
+ int handle;
+ switch(prom_vers) {
+ case PROM_V0:
+ handle = (*(romvec->pv_v0devops.v0_devopen))(dstr);
+ if(handle == 0) return -1;
+ return handle;
+ break;
+ case PROM_V2:
+ case PROM_V3:
+ case PROM_P1275:
+ handle = (*(romvec->pv_v2devops.v2_dev_open))(dstr);
+ return handle;
+ break;
+ };
+
+ return -1;
+}
+
+/* Close the device described by device handle 'dhandle'. */
+void
+prom_close(int dhandle)
+{
+ switch(prom_vers) {
+ case PROM_V0:
+ (*(romvec->pv_v0devops.v0_devclose))(dhandle);
+ return;
+ case PROM_V2:
+ case PROM_V3:
+ case PROM_P1275:
+ (*(romvec->pv_v2devops.v2_dev_close))(dhandle);
+ return;
+ };
+ return;
+}
+
+/* Seek to specified location described by 'seekhi' and 'seeklo'
+ * for device 'dhandle'.
+ */
+void
+prom_seek(int dhandle, unsigned int seekhi, unsigned int seeklo)
+{
+ switch(prom_vers) {
+ case PROM_V0:
+ (*(romvec->pv_v0devops.v0_seekdev))(dhandle, seekhi, seeklo);
+ break;
+ case PROM_V2:
+ case PROM_V3:
+ case PROM_P1275:
+ (*(romvec->pv_v2devops.v2_dev_seek))(dhandle, seekhi, seeklo);
+ break;
+ };
+
+ return;
+}
--- /dev/null
+/* init.c: Initialize internal variables used by the PROM
+ * library functions.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+
+struct linux_romvec *romvec;
+enum prom_major_version prom_vers;
+unsigned int prom_rev, prom_prev;
+
+/* The root node of the prom device tree. */
+int prom_root_node;
+
+/* Pointer to the device tree operations structure. */
+struct linux_nodeops *prom_nodeops;
+
+/* You must call prom_init() before you attempt to use any of the
+ * routines in the prom library. It returns 0 on success, 1 on
+ * failure. It gets passed the pointer to the PROM vector.
+ */
+
+extern void prom_meminit(void);
+extern void prom_ranges_init(void);
+
+int
+prom_init(struct linux_romvec *rp)
+{
+ if(!rp) return 1;
+ romvec = rp;
+ if(romvec->pv_magic_cookie != LINUX_OPPROM_MAGIC)
+ return 1;
+
+ /* Ok, we seem to have a sane romvec here. */
+ switch(romvec->pv_romvers) {
+ case 0:
+ prom_vers = PROM_V0;
+ break;
+ case 2:
+ prom_vers = PROM_V2;
+ break;
+ case 3:
+ prom_vers = PROM_V3;
+ break;
+ case 4:
+ prom_vers = PROM_P1275;
+ prom_printf("PROMLIB: Sun IEEE Prom not supported yet\n");
+ return 1;
+ break;
+ default:
+ prom_printf("PROMLIB: Bad PROM version %d\n",
+ romvec->pv_romvers);
+ return 1;
+ break;
+ };
+
+ prom_rev = romvec->pv_plugin_revision;
+ prom_prev = romvec->pv_printrev;
+ prom_nodeops = romvec->pv_nodeops;
+
+ prom_root_node = prom_getsibling(0);
+ if((prom_root_node == 0) || (prom_root_node == -1))
+ return 1;
+
+ if((((unsigned long) prom_nodeops) == 0) ||
+ (((unsigned long) prom_nodeops) == -1))
+ return 1;
+
+ prom_meminit();
+ prom_ranges_init();
+
+ prom_printf("PROMLIB: Sun Boot Prom Version %d Revision %d\n",
+ romvec->pv_romvers, prom_rev);
+
+ /* Initialization successful. */
+ return 0;
+}
--- /dev/null
+/* memory.c: Prom routine for acquiring various bits of information
+ * about RAM on the machine, both virtual and physical.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+
+/* This routine, for consistancy, returns the ram parameters in the
+ * V0 prom memory descriptor format. I choose this format becuase I
+ * think it was the easiest to work with. I feel the religious
+ * arguments now... ;) Also, I return the linked lists sorted to
+ * prevent paging_init() upset stomache as I have not yet written
+ * the pepto-bismal kernel module yet.
+ */
+
+struct linux_prom_registers prom_reg_memlist[64];
+struct linux_prom_registers prom_reg_tmp[64];
+
+struct linux_mlist_v0 prom_phys_total[64];
+struct linux_mlist_v0 prom_prom_taken[64];
+struct linux_mlist_v0 prom_phys_avail[64];
+
+struct linux_mlist_v0 *prom_ptot_ptr = prom_phys_total;
+struct linux_mlist_v0 *prom_ptak_ptr = prom_prom_taken;
+struct linux_mlist_v0 *prom_pavl_ptr = prom_phys_avail;
+
+struct linux_mem_v0 prom_memlist;
+
+
+/* Internal Prom library routine to sort a linux_mlist_v0 memory
+ * list. Used below in initialization.
+ */
+void
+prom_sortmemlist(struct linux_mlist_v0 *thislist)
+{
+ int swapi = 0;
+ int i, mitr, tmpsize;
+ char *tmpaddr;
+ char *lowest;
+
+ for(i=0; thislist[i].theres_more != 0; i++) {
+ lowest = thislist[i].start_adr;
+ for(mitr = i+1; thislist[mitr-1].theres_more != 0; mitr++)
+ if(thislist[mitr].start_adr < lowest) {
+ lowest = thislist[mitr].start_adr;
+ swapi = mitr;
+ }
+ if(lowest == thislist[i].start_adr) continue;
+ tmpaddr = thislist[swapi].start_adr;
+ tmpsize = thislist[swapi].num_bytes;
+ for(mitr = swapi; mitr > i; mitr--) {
+ thislist[mitr].start_adr = thislist[mitr-1].start_adr;
+ thislist[mitr].num_bytes = thislist[mitr-1].num_bytes;
+ }
+ thislist[i].start_adr = tmpaddr;
+ thislist[i].num_bytes = tmpsize;
+ }
+
+ return;
+}
+
+/* Initialize the memory lists based upon the prom version. */
+void
+prom_meminit(void)
+{
+ int node = 0;
+ unsigned int iter, num_regs;
+ struct linux_mlist_v0 *mptr; /* ptr for traversal */
+
+ switch(prom_vers) {
+ case PROM_V0:
+ /* Nice, kind of easier to do in this case. */
+ /* First, the total physical descriptors. */
+ for(mptr = (*(romvec->pv_v0mem.v0_totphys)), iter=0;
+ mptr; mptr=mptr->theres_more, iter++) {
+ prom_phys_total[iter].start_adr = mptr->start_adr;
+ prom_phys_total[iter].num_bytes = mptr->num_bytes;
+ prom_phys_total[iter].theres_more = &prom_phys_total[iter+1];
+ }
+ prom_phys_total[iter-1].theres_more = 0x0;
+ /* Second, the total prom taken descriptors. */
+ for(mptr = (*(romvec->pv_v0mem.v0_prommap)), iter=0;
+ mptr; mptr=mptr->theres_more, iter++) {
+ prom_prom_taken[iter].start_adr = mptr->start_adr;
+ prom_prom_taken[iter].num_bytes = mptr->num_bytes;
+ prom_prom_taken[iter].theres_more = &prom_prom_taken[iter+1];
+ }
+ prom_prom_taken[iter-1].theres_more = 0x0;
+ /* Last, the available physical descriptors. */
+ for(mptr = (*(romvec->pv_v0mem.v0_available)), iter=0;
+ mptr; mptr=mptr->theres_more, iter++) {
+ prom_phys_avail[iter].start_adr = mptr->start_adr;
+ prom_phys_avail[iter].num_bytes = mptr->num_bytes;
+ prom_phys_avail[iter].theres_more = &prom_phys_avail[iter+1];
+ }
+ prom_phys_avail[iter-1].theres_more = 0x0;
+ /* Sort all the lists. */
+ prom_sortmemlist(prom_phys_total);
+ prom_sortmemlist(prom_prom_taken);
+ prom_sortmemlist(prom_phys_avail);
+ break;
+ case PROM_V2:
+ case PROM_V3:
+ case PROM_P1275:
+ /* Grrr, have to traverse the prom device tree ;( */
+ node = prom_getchild(prom_root_node);
+ node = prom_searchsiblings(node, "memory");
+ num_regs = prom_getproperty(node, "available",
+ (char *) prom_reg_memlist,
+ sizeof(prom_reg_memlist));
+ num_regs = (num_regs/sizeof(struct linux_prom_registers));
+ for(iter=0; iter<num_regs; iter++) {
+ prom_phys_avail[iter].start_adr =
+ prom_reg_memlist[iter].phys_addr;
+ prom_phys_avail[iter].num_bytes =
+ (unsigned long) prom_reg_memlist[iter].reg_size;
+ prom_phys_avail[iter].theres_more =
+ &prom_phys_avail[iter+1];
+ }
+ prom_phys_avail[iter-1].theres_more = 0x0;
+
+ num_regs = prom_getproperty(node, "reg",
+ (char *) prom_reg_memlist,
+ sizeof(prom_reg_memlist));
+ num_regs = (num_regs/sizeof(struct linux_prom_registers));
+ for(iter=0; iter<num_regs; iter++) {
+ prom_phys_total[iter].start_adr =
+ prom_reg_memlist[iter].phys_addr;
+ prom_phys_total[iter].num_bytes =
+ (unsigned long) prom_reg_memlist[iter].reg_size;
+ prom_phys_total[iter].theres_more =
+ &prom_phys_total[iter+1];
+ }
+ prom_phys_total[iter-1].theres_more = 0x0;
+
+ node = prom_getchild(prom_root_node);
+ node = prom_searchsiblings(node, "virtual-memory");
+ num_regs = prom_getproperty(node, "available",
+ (char *) prom_reg_memlist,
+ sizeof(prom_reg_memlist));
+ num_regs = (num_regs/sizeof(struct linux_prom_registers));
+
+ /* Convert available virtual areas to taken virtual
+ * areas. First sort, then convert.
+ */
+ for(iter=0; iter<num_regs; iter++) {
+ prom_prom_taken[iter].start_adr =
+ prom_reg_memlist[iter].phys_addr;
+ prom_prom_taken[iter].num_bytes =
+ (unsigned long) prom_reg_memlist[iter].reg_size;
+ prom_prom_taken[iter].theres_more =
+ &prom_phys_total[iter+1];
+ }
+ prom_prom_taken[iter-1].theres_more = 0x0;
+
+ prom_sortmemlist(prom_prom_taken);
+
+ /* Finally, convert. */
+ for(iter=0; iter<num_regs; iter++) {
+ prom_prom_taken[iter].start_adr =
+ prom_prom_taken[iter].start_adr +
+ prom_prom_taken[iter].num_bytes;
+ prom_prom_taken[iter].num_bytes =
+ prom_prom_taken[iter+1].start_adr -
+ prom_prom_taken[iter].start_adr;
+ }
+ prom_prom_taken[iter-1].num_bytes =
+ 0xffffffff - (unsigned long) prom_prom_taken[iter-1].start_adr;
+
+ /* Sort the other two lists. */
+ prom_sortmemlist(prom_phys_total);
+ prom_sortmemlist(prom_phys_avail);
+
+ };
+
+ /* Link all the lists into the top-level descriptor. */
+ prom_memlist.v0_totphys=&prom_ptot_ptr;
+ prom_memlist.v0_prommap=&prom_ptak_ptr;
+ prom_memlist.v0_available=&prom_pavl_ptr;
+
+ return;
+}
+
+/* This returns a pointer to our libraries internal v0 format
+ * memory descriptor.
+ */
+struct linux_mem_v0 *
+prom_meminfo(void)
+{
+ return &prom_memlist;
+}
--- /dev/null
+/* misc.c: Miscellaneous prom functions that don't belong
+ * anywhere else.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+
+/* Reset and reboot the machine with the command 'bcommand'. */
+void
+prom_reboot(char *bcommand)
+{
+ (*(romvec->pv_reboot))(bcommand);
+ /* Never get here. */
+ return;
+}
+
+/* Forth evaluate the expression contained in 'fstring'. */
+void
+prom_feval(char *fstring)
+{
+ if(!fstring || fstring[0] == 0) return;
+ if(prom_vers == PROM_V0)
+ (*(romvec->pv_fortheval.v0_eval))(strlen(fstring), fstring);
+ else
+ (*(romvec->pv_fortheval.v2_eval))(fstring);
+ return;
+}
+
+/* Drop into the prom, with the chance to continue with the 'go'
+ * prom command.
+ */
+void
+prom_halt(void)
+{
+ (*(romvec->pv_abort))();
+ return;
+}
+
+/* Drop into the prom, but completely terminate the program.
+ * No chance of continuing.
+ */
+void
+prom_die(void)
+{
+ (*(romvec->pv_halt))();
+ /* Never get here. */
+ return;
+}
+
+typedef void (*sfunc_t)(void);
+
+/* Set prom sync handler to call function 'funcp'. */
+void
+prom_setsync(sfunc_t funcp)
+{
+ if(!funcp) return;
+ *romvec->pv_synchook = funcp;
+ return;
+}
+
+/* Get the idprom and stuff it into buffer 'idbuf'. Returns the
+ * format type. 'num_bytes' is the number of bytes that your idbuf
+ * has space for. Returns 0xff on error.
+ */
+unsigned char
+prom_getidp(char *idbuf, int num_bytes)
+{
+ int len;
+
+ len = prom_getproplen(prom_root_node, "idprom");
+ if((len>num_bytes) || (len==-1)) return 0xff;
+ if(!prom_getproperty(prom_root_node, "idprom", idbuf, num_bytes))
+ return idbuf[0];
+
+ return 0xff;
+}
+
+/* Get the major prom version number. */
+int
+prom_version(void)
+{
+ return romvec->pv_romvers;
+}
+
+/* Get the prom plugin-revision. */
+int
+prom_getrev(void)
+{
+ return prom_rev;
+}
+
+/* Get the prom firmware print revision. */
+int
+prom_getprev(void)
+{
+ return prom_prev;
+}
--- /dev/null
+/* mp.c: OpenBoot Prom Multiprocessor support routines. Don't call
+ * these on a UP or else you will halt and catch fire. ;)
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+
+/* Start cpu with prom-tree node 'cpunode' using context described
+ * by 'ctable_reg' in context 'ctx' at program counter 'pc'.
+ *
+ * XXX Have to look into what the return values mean. XXX
+ */
+int
+prom_startcpu(int cpunode, struct linux_prom_registers *ctable_reg, int ctx, char *pc)
+{
+ switch(prom_vers) {
+ case PROM_V0:
+ case PROM_V2:
+ break;
+ case PROM_V3:
+ case PROM_P1275:
+ return (*(romvec->v3_cpustart))(cpunode, (int) ctable_reg, ctx, pc);
+ break;
+ };
+
+ return -1;
+}
+
+/* Stop CPU with device prom-tree node 'cpunode'.
+ * XXX Again, what does the return value really mean? XXX
+ */
+int
+prom_stopcpu(int cpunode)
+{
+ switch(prom_vers) {
+ case PROM_V0:
+ case PROM_V2:
+ break;
+ case PROM_V3:
+ case PROM_P1275:
+ return (*(romvec->v3_cpustop))(cpunode);
+ break;
+ };
+
+ return -1;
+}
+
+/* Make CPU with device prom-tree node 'cpunode' idle.
+ * XXX Return value, anyone? XXX
+ */
+int
+prom_idlecpu(int cpunode)
+{
+ switch(prom_vers) {
+ case PROM_V0:
+ case PROM_V2:
+ break;
+ case PROM_V3:
+ case PROM_P1275:
+ return (*(romvec->v3_cpuidle))(cpunode);
+ break;
+ };
+
+ return -1;
+}
+
+/* Resume the execution of CPU with nodeid 'cpunode'.
+ * XXX Come on, somebody has to know... XXX
+ */
+int
+prom_restartcpu(int cpunode)
+{
+ switch(prom_vers) {
+ case PROM_V0:
+ case PROM_V2:
+ break;
+ case PROM_V3:
+ case PROM_P1275:
+ return (*(romvec->v3_cpuresume))(cpunode);
+ break;
+ };
+
+ return -1;
+}
--- /dev/null
+/* palloc.c: Memory allocation from the Sun PROM.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+
+/* You should not call these routines after memory management
+ * has been initialized in the kernel, if fact you should not
+ * use these if at all possible in the kernel. They are mainly
+ * to be used for a bootloader for temporary allocations which
+ * it will free before jumping into the kernel it has loaded.
+ *
+ * Also, these routines don't work on V0 proms, only V2 and later.
+ */
+
+/* Allocate a chunk of memory of size 'num_bytes' giving a suggestion
+ * of virtual_hint as the preferred virtual base address of this chunk.
+ * There are no guarentees that you will get the allocation, or that
+ * the prom will abide by your "hint". So check your return value.
+ */
+char *
+prom_alloc(char *virtual_hint, unsigned int num_bytes)
+{
+ if(prom_vers == PROM_V0) return (char *) 0x0;
+ if(num_bytes == 0x0) return (char *) 0x0;
+ return (*(romvec->pv_v2devops.v2_dumb_mem_alloc))(virtual_hint, num_bytes);
+}
+
+/* Free a previously allocated chunk back to the prom at virtual address
+ * 'vaddr' of size 'num_bytes'. NOTE: This vaddr is not the hint you
+ * used for the allocation, but the virtual address the prom actually
+ * returned to you. They may be have been the same, they may have not,
+ * doesn't matter.
+ */
+void
+prom_free(char *vaddr, unsigned int num_bytes)
+{
+ if((prom_vers == PROM_V0) || (num_bytes == 0x0)) return;
+ (*(romvec->pv_v2devops.v2_dumb_mem_free))(vaddr, num_bytes);
+ return;
+}
--- /dev/null
+/* printf.c: Internal prom library printf facility.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+/* This routine is internal to the prom library, no one else should know
+ * about or use it! It's simple and smelly anyway....
+ */
+
+#include <stdarg.h>
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+
+char hexstring[] = "0123456789abcdef";
+
+void
+prom_printf(char *fmt, ...)
+{
+ va_list args;
+ unsigned int ui_val;
+ int i_val, n_ctr;
+ char c_val;
+ char nstr_buf[32];
+ char *s_val;
+
+ va_start(args, fmt);
+ while(*fmt) {
+ if(*fmt != '%') {
+ if(*fmt == '\n')
+ prom_putchar('\r');
+ prom_putchar(*fmt++);
+ continue;
+ }
+
+ fmt++;
+ if(!*fmt) break;
+ n_ctr = 0;
+ switch(*fmt) {
+ case 'c':
+ c_val = va_arg(args, char);
+ if(c_val == '\n')
+ prom_putchar('\r');
+ prom_putchar(c_val);
+ fmt++;
+ break;
+ case 's':
+ s_val = va_arg(args, char *);
+ while(*s_val != 0) {
+ prom_putchar(*s_val);
+ s_val++;
+ }
+ fmt++;
+ break;
+ case 'd':
+ /* Base 10 */
+ i_val = va_arg(args, int);
+ if(i_val==0x0)
+ prom_putchar('0');
+ else
+ while(i_val != 0x0) {
+ nstr_buf[n_ctr] = hexstring[i_val%0xa];
+ i_val = ((unsigned long)i_val) / (unsigned) 0xa;
+ n_ctr++;
+ };
+ while(--n_ctr >= 0)
+ prom_putchar(nstr_buf[n_ctr]);
+ fmt++;
+ break;
+ case 'x':
+ /* Base 16 */
+ ui_val = va_arg(args, unsigned int);
+ if(ui_val==0x0)
+ prom_putchar('0');
+ else
+ while(ui_val != 0x0) {
+ nstr_buf[n_ctr] = hexstring[ui_val%0x10];
+ ui_val = ((unsigned long) ui_val) / (unsigned) 0x10;
+ n_ctr++;
+ };
+ while(--n_ctr >= 0)
+ prom_putchar(nstr_buf[n_ctr]);
+ fmt++;
+ break;
+ case 'o':
+ /* Base 8 */
+ ui_val = va_arg(args, unsigned int);
+ if(ui_val==0x0)
+ prom_putchar('0');
+ else
+ while(ui_val != 0x0) {
+ nstr_buf[n_ctr] = hexstring[ui_val%0x8];
+ ui_val = ((unsigned long) ui_val) / (unsigned) 0x8;
+ };
+ while(--n_ctr >= 0)
+ prom_putchar(nstr_buf[n_ctr]);
+ fmt++;
+ break;
+ default:
+ /* Uh oh, something we can't handle... skip it */
+ fmt++;
+ break;
+ };
+ }
+
+ /* We are done... */
+ return;
+}
--- /dev/null
+/* ranges.c: Handle ranges in newer proms for obio.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+
+struct linux_prom_ranges promlib_obio_ranges[PROMREG_MAX];
+struct linux_prom_ranges promlib_sbus_ranges[PROMREG_MAX];
+int num_obio_ranges, num_sbus_ranges;
+
+/* Adjust register values based upon the ranges parameters. */
+void
+prom_adjust_regs(struct linux_prom_registers *regp, int nregs,
+ struct linux_prom_ranges *rangep, int nranges)
+{
+ int regc, rngc;
+
+ for(regc=0; regc < nregs; regc++) {
+ for(rngc=0; rngc < nranges; rngc++)
+ if(regp[regc].which_io == rangep[rngc].ot_child_space)
+ break; /* Fount it */
+ if(rngc==nranges) /* oops */
+ prom_printf("adjust_regs: Could not find range with matching bus type...\n");
+ regp[regc].which_io = rangep[rngc].ot_parent_space;
+ regp[regc].phys_addr += rangep[rngc].ot_parent_base;
+ }
+
+ return;
+}
+
+void
+prom_adjust_ranges(struct linux_prom_ranges *ranges1, int nranges1,
+ struct linux_prom_ranges *ranges2, int nranges2)
+{
+ int rng1c, rng2c;
+
+ for(rng1c=0; rng1c < nranges1; rng1c++) {
+ for(rng2c=0; rng2c < nranges2; rng2c++)
+ if(ranges1[rng1c].ot_child_space ==
+ ranges2[rng2c].ot_child_space) break;
+ if(rng2c == nranges2) /* oops */
+ prom_printf("adjust_ranges: Could not find matching bus type...\n");
+ ranges1[rng1c].ot_parent_space = ranges2[rng2c].ot_parent_space;
+ ranges1[rng1c].ot_parent_base += ranges2[rng2c].ot_parent_base;
+ }
+
+ return;
+}
+
+/* Apply probed obio ranges to registers passed, if no ranges return. */
+void
+prom_apply_obio_ranges(struct linux_prom_registers *regs, int nregs)
+{
+ if(!num_obio_ranges) return;
+ prom_adjust_regs(regs, nregs, promlib_obio_ranges, num_obio_ranges);
+ return;
+}
+
+/* Apply probed sbus ranges to registers passed, if no ranges return. */
+void
+prom_apply_sbus_ranges(struct linux_prom_registers *regs, int nregs)
+{
+ if(!num_sbus_ranges) return;
+ prom_adjust_regs(regs, nregs, promlib_sbus_ranges, num_sbus_ranges);
+ return;
+}
+
+void
+prom_ranges_init(void)
+{
+ int node, obio_node, sbus_node;
+ int success;
+
+ num_obio_ranges = 0;
+ num_sbus_ranges = 0;
+
+ /* Check for obio and sbus ranges. */
+ node = prom_getchild(prom_root_node);
+ obio_node = prom_searchsiblings(node, "obio");
+ sbus_node = prom_searchsiblings(node, "iommu");
+ if(sbus_node) {
+ sbus_node = prom_getchild(sbus_node);
+ sbus_node = prom_searchsiblings(sbus_node, "sbus");
+ }
+
+ if(obio_node) {
+ success = prom_getproperty(obio_node, "ranges",
+ (char *) promlib_obio_ranges,
+ sizeof(promlib_obio_ranges));
+ if(success != -1)
+ num_obio_ranges = (success/sizeof(struct linux_prom_ranges));
+ }
+
+ if(sbus_node) {
+ success = prom_getproperty(sbus_node, "ranges",
+ (char *) promlib_sbus_ranges,
+ sizeof(promlib_sbus_ranges));
+ if(success != -1)
+ num_sbus_ranges = (success/sizeof(struct linux_prom_ranges));
+ }
+
+ if(num_obio_ranges || num_sbus_ranges)
+ prom_printf("PROMLIB: obio_ranges %d sbus_ranges %d\n",
+ num_obio_ranges, num_sbus_ranges);
+
+ return;
+}
--- /dev/null
+/* segment.c: Prom routine to map segments in other contexts before
+ * a standalone is completely mapped. This is for sun4 and
+ * sun4c architectures only.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+
+/* Set physical segment 'segment' at virtual address 'vaddr' in
+ * context 'ctx'.
+ */
+void
+prom_putsegment(int ctx, unsigned long vaddr, int segment)
+{
+ (*(romvec->pv_setctxt))(ctx, (char *) vaddr, segment);
+ return;
+}
--- /dev/null
+/* tree.c: Basic device tree traversal/scanning for the Linux
+ * prom library.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/string.h>
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+
+static char promlib_buf[128];
+
+/* Return the child of node 'node' or zero if no this node has no
+ * direct descendent.
+ */
+int
+prom_getchild(int node)
+{
+ int cnode;
+
+ if(node == -1) return 0;
+ cnode = prom_nodeops->no_child(node);
+ if((cnode == 0) || (cnode == -1)) return 0;
+ return cnode;
+}
+
+/* Return the next sibling of node 'node' or zero if no more siblings
+ * at this level of depth in the tree.
+ */
+int
+prom_getsibling(int node)
+{
+ int sibnode;
+
+ if(node == -1) return 0;
+ sibnode = prom_nodeops->no_nextnode(node);
+ if((sibnode == 0) || (sibnode == -1)) return 0;
+ return sibnode;
+}
+
+/* Return the length in bytes of property 'prop' at node 'node'.
+ * Return -1 on error.
+ */
+int
+prom_getproplen(int node, char *prop)
+{
+ if((!node) || (!prop)) return -1;
+ return prom_nodeops->no_proplen(node, prop);
+}
+
+/* Acquire a property 'prop' at node 'node' and place it in
+ * 'buffer' which has a size of 'bufsize'. If the acquisition
+ * was successful the length will be returned, else -1 is returned.
+ */
+int
+prom_getproperty(int node, char *prop, char *buffer, int bufsize)
+{
+ int plen;
+
+ plen = prom_getproplen(node, prop);
+ if((plen > bufsize) || (plen == 0) || (plen == -1)) return -1;
+
+ /* Ok, things seem all right. */
+ return prom_nodeops->no_getprop(node, prop, buffer);
+}
+
+/* Acquire an integer property and return it's value. Returns -1
+ * on failure.
+ */
+int
+prom_getint(int node, char *prop)
+{
+ static int intprop;
+
+ if(prom_getproperty(node, prop, (char *) &intprop, sizeof(int)) != -1)
+ return intprop;
+
+ return -1;
+}
+
+/* Acquire an integer property, upon error return the passed default
+ * integer.
+ */
+
+int
+prom_getintdefault(int node, char *property, int deflt)
+{
+ int retval;
+
+ retval = prom_getint(node, property);
+ if(retval == -1) return deflt;
+
+ return retval;
+}
+
+/* Acquire a boolean property, 1=TRUE 0=FALSE. */
+int
+prom_getbool(int node, char *prop)
+{
+ int retval;
+
+ retval = prom_getproplen(node, prop);
+ if(retval == -1) return 0;
+ return 1;
+}
+
+/* Acquire a property whose value is a string, returns a null
+ * string on error. The char pointer is the user supplied string
+ * buffer.
+ */
+void
+prom_getstring(int node, char *prop, char *user_buf, int ubuf_size)
+{
+ int len;
+
+ len = prom_getproperty(node, prop, user_buf, ubuf_size);
+ if(len != -1) return;
+ user_buf[0] = 0;
+ return;
+}
+
+
+/* Does the device at node 'node' have name 'name'?
+ * YES = 1 NO = 0
+ */
+int
+prom_nodematch(int node, char *name)
+{
+ static char namebuf[128];
+ prom_getproperty(node, "name", namebuf, sizeof(namebuf));
+ if(strcmp(namebuf, name) == 0) return 1;
+ return 0;
+}
+
+/* Search siblings at 'node_start' for a node with name
+ * 'nodename'. Return node if successful, zero if not.
+ */
+int
+prom_searchsiblings(int node_start, char *nodename)
+{
+ int thisnode, error;
+
+ for(thisnode = node_start; thisnode;
+ thisnode=prom_getsibling(thisnode)) {
+ error = prom_getproperty(thisnode, "name", promlib_buf,
+ sizeof(promlib_buf));
+ /* Should this ever happen? */
+ if(error == -1) continue;
+ if(strcmp(nodename, promlib_buf)==0) return thisnode;
+ }
+
+ return 0;
+}
+
+/* Return the first property type for node 'node'.
+ */
+char *
+prom_firstprop(int node)
+{
+ if(node == -1) return "";
+ return prom_nodeops->no_nextprop(node, (char *) 0x0);
+}
+
+/* Return the property type string after property type 'oprop'
+ * at node 'node' . Returns NULL string if no more
+ * property types for this node.
+ */
+char *
+prom_nextprop(int node, char *oprop)
+{
+ if(node == -1) return "";
+ return prom_nodeops->no_nextprop(node, oprop);
+}
+
+/* Set property 'pname' at node 'node' to value 'value' which has a length
+ * of 'size' bytes. Return the number of bytes the prom accepted.
+ */
+int
+prom_setprop(int node, char *pname, char *value, int size)
+{
+ if(size == 0) return 0;
+ if((pname == 0) || (value == 0)) return 0;
+ return prom_nodeops->no_setprop(node, pname, value, size);
+}
ifdef CONFIG_BLK_DEV_FD
OBJS := $(OBJS) floppy.o
SRCS := $(SRCS) floppy.c
+else
+BLOCK_MODULE_OBJS := $(MODULES) floppy.o
endif
ifdef CONFIG_CDU31A
#define MAJOR_NR AZTECH_CDROM_MAJOR
-#ifdef MODULE
-# include "/usr/src/linux/drivers/block/blk.h"
-#else
-# include "blk.h"
+#include "blk.h"
+#ifndef MODULE
# define MOD_INC_USE_COUNT
# define MOD_DEC_USE_COUNT
#endif
#endif CONFIG_SBPCD
extern void set_device_ro(int dev,int flag);
-extern void floppy_init(void);
-#ifdef FD_MODULE
-static
-#else
-extern
-#endif
-int new_floppy_init(void);
+extern int floppy_init(void);
extern void rd_load(void);
extern long rd_init(long mem_start, int length);
extern int ramdisk_size;
* errors to allow safe writing by specialized programs.
*/
+/* 1994/4/24 -- Dan Fandrich -- added support for Commodore 1581 3.5" disks
+ * by defining bit 1 of the "stretch" parameter to mean put sectors on the
+ * opposite side of the disk, leaving the sector IDs alone (i.e. Commodore's
+ * drives are "upside-down").
+ */
+
#define CONFIG_FLOPPY_SANITY
#undef CONFIG_FLOPPY_SILENT_DCL_CLEAR
/* do print messages for unexpected interrupts */
static int print_unex=1;
+#ifdef MODULE
+#define FD_MODULE
+
+#include <linux/module.h>
+/*
+ * NB. we must include the kernel idenfication string in to install the module.
+ */
+#include <linux/version.h>
+char kernel_version[] = UTS_RELEASE;
+
+int FLOPPY_IRQ=6;
+int FLOPPY_DMA=2;
+int ALLOWED_DRIVE_MASK = 0x33;
+int FDC1 = 0x3f0;
+int FDC2 = -1;
+
+#endif
+
#ifndef FD_MODULE
/* the following is the mask of allowed drives. By default units 2 and
* 3 of both floppy controllers are disabled, because switching on the
#include <linux/fd.h>
#include <linux/errno.h>
#include <linux/malloc.h>
+#include <linux/mm.h>
#include <linux/string.h>
#include <linux/fcntl.h>
#include <linux/delay.h>
#include <linux/mc146818rtc.h> /* CMOS defines */
+#include <linux/ioport.h>
#include <asm/dma.h>
#include <asm/irq.h>
#define MAJOR_NR FLOPPY_MAJOR
#include "blk.h"
+
+/* Dma Memory related stuff */
+
+/* Pure 2^n version of get_order */
+static inline int __get_order (int size)
+{
+ int order;
+
+#ifdef _ASM_IO_H2
+ __asm__ __volatile__("bsr %1,%0"
+ : "=r" (order)
+ : "r" (size / PAGE_SIZE) );
+#else
+ for (order = 0; order < NR_MEM_LISTS; ++order)
+ if (size <= (PAGE_SIZE << order))
+ return order;
+#endif
+ return NR_MEM_LISTS;
+}
+
+static unsigned long dma_mem_alloc(int size)
+{
+ int order = __get_order(size);
+
+ if (order >= NR_MEM_LISTS)
+ return(0);
+ return __get_dma_pages(GFP_KERNEL,order);
+}
+
+/* End dma memory related stuff */
+
static unsigned int fake_change = 0;
static int initialising=1;
-#define FLOPPY0_TYPE ((CMOS_READ(0x10) >> 4) & 15)
-#define FLOPPY1_TYPE (CMOS_READ(0x10) & 15)
-
/*
* Again, the CMOS information doesn't work on the alpha..
*/
#ifdef __alpha__
-#undef FLOPPY0_TYPE
-#undef FLOPPY1_TYPE
#define FLOPPY0_TYPE 6
#define FLOPPY1_TYPE 0
+#else
+#define FLOPPY0_TYPE ((CMOS_READ(0x10) >> 4) & 15)
+#define FLOPPY1_TYPE (CMOS_READ(0x10) & 15)
#endif
#define N_FDC 2
#define DPRINT3(x,x1,x2,x3) \
printk(DEVICE_NAME "%d: " x,current_drive,(x1),(x2),(x3))
+#define PH_HEAD(floppy,head) (((((floppy)->stretch & 2) >>1) ^ head) << 2)
+#define STRETCH(floppy) ((floppy)->stretch & FD_STRETCH)
+
/* read/write */
#define COMMAND raw_cmd.cmd[0]
#define DR_SELECT raw_cmd.cmd[1]
#define MAX_DISK_SIZE 2 /* 3984*/
+#define K_64 0x10000 /* 64KB */
/*
* The DMA channel used by the floppy controller cannot access data at
* driver otherwise. It doesn't matter much for performance anyway, as most
* floppy accesses go through the track buffer.
*/
-#define LAST_DMA_ADDR (0x1000000)
-#define K_64 (0x10000) /* 64 k */
+#ifdef __alpha__
+# define CROSS_64KB(a,s) (0)
+#else
+# define CROSS_64KB(a,s) \
+ ((unsigned long)(a)/K_64 != ((unsigned long)(a) + (s) - 1) / K_64)
+#endif
/*
* globals used by 'result()'
/*
* This struct defines the different floppy types.
*
- * The 'stretch' tells if the tracks need to be doubled for some
- * types (ie 360kB diskette in 1.2MB drive etc). Others should
- * be self-explanatory.
+ * Bit 0 of 'stretch' tells if the tracks need to be doubled for some
+ * types (e.g. 360kB diskette in 1.2MB drive, etc.). Bit 1 of 'stretch'
+ * tells if the disk is in Commodore 1581 format, which means side 0 sectors
+ * are located on side 1 of the disk but with a side 0 ID, and vice-versa.
+ * This is the same as the Sharp MZ-80 5.25" CP/M disk format, except that the
+ * 1581's logical side 0 is on physical side 1, whereas the Sharp's logical
+ * side 0 is on physical side 0 (but with the misnamed sector IDs).
+ * 'stretch' should probably be renamed to something more general, like
+ * 'options'. Other parameters should be self-explanatory (see also
+ * setfdprm(8)).
*/
static struct floppy_struct floppy_type[32] = {
{ 0, 0,0, 0,0,0x00,0x00,0x00,0x00,NULL }, /* 0 no testing */
* corrupted/lost. Alignment of these is enforced in boot/head.S.
* Note that you must not change the sizes below without updating head.S.
*/
-extern char floppy_track_buffer[512*2*MAX_BUFFER_SECTORS];
-#define max_buffer_sectors MAX_BUFFER_SECTORS
+char *floppy_track_buffer=0;
+int max_buffer_sectors=0;
int *errors;
typedef void (*done_f)(int);
void (*redo)(void); /* this is called to retry the operation */
void (*error)(void); /* this is called to tally an error */
done_f done; /* this is called to say if the operation has succeeded/failed */
-} *cont;
+} *cont=NULL;
static void floppy_ready(void);
static void floppy_start(void);
#endif
}
+typedef void (*timeout_fn)(unsigned long);
+static struct timer_list fd_timeout ={ NULL, NULL, 0, 0,
+ (timeout_fn) floppy_shutdown };
+
+static char *timeout_message;
+
+#ifdef CONFIG_FLOPPY_SANITY
+static void is_alive(char *message)
+{
+ /* this routine checks whether the floppy driver is "alive" */
+ if (fdc_busy && command_status < 2 && !fd_timeout.prev){
+ DPRINT1("timeout handler died: %s\n",message);
+ }
+}
+#endif
+
+#ifdef CONFIG_FLOPPY_SANITY
+
+#define OLOGSIZE 20
+
+void (*lasthandler)(void) = NULL;
+int interruptjiffies=0;
+int resultjiffies=0;
+int resultsize=0;
+int lastredo=0;
+
+static struct output_log {
+ unsigned char data;
+ unsigned char status;
+ unsigned long jiffies;
+} output_log[OLOGSIZE];
+
+static int output_log_pos=0;
+#endif
+
+#define CURRENTD -1
+#define MAXTIMEOUT -2
+
+
+
+static void reschedule_timeout(int drive, char *message, int marg)
+{
+ if (drive == CURRENTD )
+ drive = current_drive;
+ del_timer(&fd_timeout);
+ if (drive < 0 || drive > N_DRIVE) {
+ fd_timeout.expires = 2000;
+ drive=0;
+ } else
+ fd_timeout.expires = UDP->timeout;
+ add_timer(&fd_timeout);
+ if (UDP->flags & FD_DEBUG){
+ DPRINT("reschedule timeout ");
+ printk(message, marg);
+ printk("\n");
+ }
+ timeout_message = message;
+}
+
/*
* Bottom half floppy driver.
* ==========================
sti();
command_status = FD_COMMAND_NONE;
set_fdc(drive);
+ reschedule_timeout(drive, "lock fdc", 0);
return 0;
}
#define LOCK_FDC(drive,interruptible) \
if(lock_fdc(drive,interruptible)) return -EINTR;
-typedef void (*timeout_fn)(unsigned long);
-static struct timer_list fd_timeout ={ NULL, NULL, 0, 0,
- (timeout_fn) floppy_shutdown };
/* unlocks the driver */
static inline void unlock_fdc(void)
DEVICE_INTR);
command_status = FD_COMMAND_NONE;
del_timer(&fd_timeout);
+ cont = NULL;
fdc_busy = 0;
floppy_release_irq_and_dma();
wake_up(&fdc_wait);
FDCS->reset=1;
return;
}
- if ( ( (long)current_addr & ~(64*1024-1) ) !=
- ((long)(current_addr + raw_cmd.length-1) & ~(64*1024-1))){
+ if (CROSS_64KB(current_addr, raw_cmd.length)) {
printk("DMA crossing 64-K boundary %p-%p\n",
current_addr, current_addr + raw_cmd.length);
cont->done(0);
FDCS->reset=1;
return;
}
-
#endif
cli();
disable_dma(FLOPPY_DMA);
set_dma_mode(FLOPPY_DMA,
(raw_cmd.flags & FD_RAW_READ)?
DMA_MODE_READ : DMA_MODE_WRITE);
- set_dma_addr(FLOPPY_DMA, (long) current_addr);
+ set_dma_addr(FLOPPY_DMA, virt_to_bus(current_addr));
set_dma_count(FLOPPY_DMA, raw_cmd.length);
enable_dma(FLOPPY_DMA);
sti();
{
int counter;
unsigned char status;
+ unsigned char rstatus;
if (FDCS->reset)
return -1;
for(counter = 0 ; counter < 10000 && !FDCS->reset ; counter++) {
- status = inb_p(FD_STATUS) &(STATUS_READY|STATUS_DIR|STATUS_DMA);
+ rstatus = inb_p(FD_STATUS);
+ status = rstatus &(STATUS_READY|STATUS_DIR|STATUS_DMA);
if (!(status & STATUS_READY))
continue;
if (status == STATUS_READY){
outb_p(byte,FD_DATA);
+
+#ifdef CONFIG_FLOPPY_SANITY
+ output_log[output_log_pos].data = byte;
+ output_log[output_log_pos].status = rstatus;
+ output_log[output_log_pos].jiffies = jiffies;
+ output_log_pos = (output_log_pos + 1) % OLOGSIZE;
+#endif
return 0;
} else
break;
(STATUS_DIR|STATUS_READY|STATUS_BUSY|STATUS_DMA);
if (!(status & STATUS_READY))
continue;
- if (status == STATUS_READY)
+ if (status == STATUS_READY){
+#ifdef CONFIG_FLOPPY_SANITY
+ resultjiffies = jiffies;
+ resultsize = i;
+#endif
return i;
+ }
if (status & STATUS_DMA )
break;
if (status == (STATUS_DIR|STATUS_READY|STATUS_BUSY)) {
* Pause 5 msec to avoid trouble. (Needs to be 2 jiffies)
*/
FDCS->dtr = raw_cmd.rate;
- return(wait_for_completion(jiffies+2,
+ return(wait_for_completion(jiffies+2*HZ/100,
(timeout_fn) floppy_ready));
} /* fdc_dtr */
{
void (*handler)(void) = DEVICE_INTR;
+ lasthandler = handler;
+ interruptjiffies = jiffies;
+
floppy_enable_hlt();
CLEAR_INTR;
if ( fdc >= N_FDC || FDCS->address == -1){
printk("DOR0=%x\n", fdc_state[0].dor);
printk("floppy interrupt on bizarre fdc %d\n",fdc);
printk("handler=%p\n", handler);
+ is_alive("bizarre fdc");
return;
}
inr = result();
if (!handler){
unexpected_floppy_interrupt();
+ is_alive("unexpected");
return;
}
if ( inr == 0 ){
}
floppy_tq.routine = (void *)(void *) handler;
queue_task_irq(&floppy_tq, &tq_timer);
+ is_alive("normal interrupt end");
}
static void recalibrate_floppy(void)
printk("\n");
printk("floppy driver state\n");
printk("-------------------\n");
+ printk("now=%ld last interrupt=%d last called handler=%p\n",
+ jiffies, interruptjiffies, lasthandler);
+
+
+#ifdef CONFIG_FLOPPY_SANITY
+ printk("timeout_message=%s\n", timeout_message);
+ printk("last output bytes:\n");
+ for(i=0; i < OLOGSIZE; i++)
+ printk("%2x %2x %ld\n",
+ output_log[(i+output_log_pos) % OLOGSIZE].data,
+ output_log[(i+output_log_pos) % OLOGSIZE].status,
+ output_log[(i+output_log_pos) % OLOGSIZE].jiffies);
+ printk("last result at %d\n", resultjiffies);
+ printk("last redo_fd_request at %d\n", lastredo);
+ for(i=0; i<resultsize; i++){
+ printk("%2x ", reply_buffer[i]);
+ }
+ printk("\n");
+#endif
+
+#if 0
for(i=0; i<N_FDC; i++){
if(FDCS->address != -1){
printk("dor %d = %x\n", i, fdc_state[i].dor );
udelay(1000); /* maybe we'll catch an interrupt... */
}
}
+#endif
printk("status=%x\n", inb_p(FD_STATUS));
printk("fdc_busy=%d\n", fdc_busy);
if( DEVICE_INTR)
static void floppy_shutdown(void)
{
+ if(!initialising)
+ show_floppy();
CLEAR_INTR;
floppy_tq.routine = (void *)(void *) empty;
del_timer( &fd_timer);
if(!initialising)
DPRINT("floppy timeout\n");
FDCS->reset = 1;
- cont->done(0);
- cont->redo(); /* this will recall reset when needed */
+ if (cont){
+ cont->done(0);
+ cont->redo(); /* this will recall reset when needed */
+ } else {
+ printk("no cont in shutdown!\n");
+ process_fd_request();
+ }
+ is_alive("floppy shutdown");
}
/*typedef void (*timeout_fn)(unsigned long);*/
static void floppy_start(void)
{
- del_timer(&fd_timeout);
- fd_timeout.expires = DP->timeout;
- add_timer(&fd_timeout);
+ reschedule_timeout(CURRENTD, "floppy start", 0);
scandrives();
#ifdef DCL_DEBUG
/*
* ========================================================================
* here ends the bottom half. Exported routines are:
- * floppy_start, floppy_off, floppy_ready, lock_fdc, unlock_fdc, set_fdc,
+ * floppy_start, floppy_off, floppy_ready, lock_fdc, unlock_fdc, set_fdc,
* start_motor, reset_fdc, reset_fdc_info, interpret_errors.
* Initialisation also uses output_byte, result, set_dor, floppy_interrupt
* and set_dor.
static void do_wakeup(void)
{
- del_timer(&fd_timeout);
+ reschedule_timeout(MAXTIMEOUT, "do wakeup", 0);
cont = 0;
command_status += 2;
wake_up(&command_done);
queue_task(&floppy_tq, &tq_timer);
cli();
- while(command_status < 2 && NO_SIGNAL)
+ while(command_status < 2 && NO_SIGNAL){
+ is_alive("wait_til_done");
if (current->pid)
interruptible_sleep_on(&command_done);
else {
run_task_queue(&tq_timer);
cli();
}
+ }
if(command_status < 2){
sti();
floppy_shutdown();
raw_cmd.rate = floppy->rate & 0x3;
raw_cmd.cmd_count = NR_F;
COMMAND = FM_MODE(floppy,FD_FORMAT);
- DR_SELECT = UNIT(current_drive) + ( format_req.head << 2 );
+ DR_SELECT = UNIT(current_drive) + PH_HEAD(floppy,format_req.head);
F_SIZECODE = FD_SIZECODE(floppy);
F_SECT_PER_TRACK = floppy->sect << 2 >> F_SIZECODE;
F_GAP = floppy->fmt_gap;
static void redo_format(void)
{
- raw_cmd.track = format_req.track << floppy->stretch;
+ raw_cmd.track = format_req.track << STRETCH(floppy);
buffer_track = -1;
setup_format_params();
floppy_start();
int block;
probing = 0;
- del_timer(&fd_timeout);
+ reschedule_timeout(MAXTIMEOUT, "request done %d", uptodate);
if (!CURRENT){
DPRINT("request list destroyed in floppy request done\n");
}
max_sector = floppy->sect * floppy->head;
+
TRACK = CURRENT->sector / max_sector;
sector_t = CURRENT->sector % max_sector;
if ( floppy->track && TRACK >= floppy->track )
return 0;
HEAD = sector_t / floppy->sect;
- if ( TESTF( FD_NEED_TWADDLE) && sector_t < floppy->sect )
+ if (((floppy->stretch & FD_SWAPSIDES) || TESTF( FD_NEED_TWADDLE)) &&
+ sector_t < floppy->sect )
max_sector = floppy->sect;
/* 2M disks have phantom sectors on the first track */
SIZECODE2 = 0xff;
else
SIZECODE2 = 0x80;
- raw_cmd.track = TRACK << floppy->stretch;
- DR_SELECT = UNIT(current_drive) + ( HEAD << 2 );
+ raw_cmd.track = TRACK << STRETCH(floppy);
+ DR_SELECT = UNIT(current_drive) + PH_HEAD(floppy,HEAD);
GAP = floppy->gap;
CODE2SIZE;
SECT_PER_TRACK = floppy->sect << 2 >> SIZECODE;
raw_cmd.flags &= ~FD_RAW_WRITE;
raw_cmd.flags |= FD_RAW_READ;
COMMAND = FM_MODE(floppy,FD_READ);
- } else if ((long)CURRENT->buffer <= LAST_DMA_ADDR ) {
+ } else if ((unsigned long)CURRENT->buffer < MAX_DMA_ADDRESS ) {
int direct, indirect;
indirect= transfer_size(ssize,max_sector,max_buffer_sectors*2) -
sector_t;
max_size = buffer_chain_size();
- if ( max_size > ( LAST_DMA_ADDR - ((long) CURRENT->buffer))>>9)
- max_size=(LAST_DMA_ADDR - ((long)CURRENT->buffer))>>9;
+ if ( max_size > ( MAX_DMA_ADDRESS - ((unsigned long) CURRENT->buffer))>>9)
+ max_size=(MAX_DMA_ADDRESS - ((unsigned long) CURRENT->buffer))>>9;
/* 64 kb boundaries */
- if ( ((max_size << 9) + ((long) CURRENT->buffer)) / K_64 !=
- ((long) CURRENT->buffer ) / K_64 )
+ if (CROSS_64KB(CURRENT->buffer, max_size << 9))
max_size = ( K_64 - ((long) CURRENT->buffer) % K_64)>>9;
direct = transfer_size(ssize,max_sector,max_size) - sector_t;
/*
int tmp;
int error;
+
error = -1;
+ lastredo = jiffies;
if (current_drive < N_DRIVE)
floppy_off(current_drive);
- if (CURRENT && CURRENT->dev < 0) return;
+ if (CURRENT && CURRENT->dev < 0){
+ DPRINT("current dev < 0!\n");
+ return;
+ }
while(1){
if (!CURRENT) {
error=-1;
device = CURRENT->dev;
set_fdc( DRIVE(device));
-
- del_timer(&fd_timeout);
- fd_timeout.expires = DP->timeout;
- add_timer(&fd_timeout);
+ reschedule_timeout(CURRENTD, "redo fd request", 0);
set_floppy(device);
if(start_motor(redo_fd_request)) return;
static void do_fd_request(void)
{
- if (fdc_busy)
+ if (fdc_busy){
/* fdc busy, this new request will be treated when the
current one is done */
+ is_alive("do fd request, old request running");
return;
+ }
/* fdc_busy cannot be set by an interrupt or a bh */
floppy_grab_irq_and_dma();
fdc_busy=1;
+ reschedule_timeout(MAXTIMEOUT, "do fd request",0);
process_fd_request();
+ is_alive("do fd request");
}
static struct cont_t poll_cont={
FDCS->reset=1;
if ( FDCS->reset ){
cont = &reset_cont;
- del_timer(&fd_timeout);
- fd_timeout.expires = DP->timeout;
- add_timer(&fd_timeout);
+ reschedule_timeout(CURRENTD, "user reset fdc", 0);
WAIT(reset_fdc);
}
process_fd_request();
return i;
name = drive_name(type,drive);
for ( cnt=0; cnt<16; cnt++){
- put_fs_byte(name[cnt],
- ((char*)param)+cnt);
+ put_user(name[cnt], ((char*)param)+cnt);
if ( ! *name )
break;
}
if(newparams.sect <= 0 ||
newparams.head <= 0 ||
newparams.track <= 0 ||
- newparams.track >
- UDP->tracks>>newparams.stretch)
+ newparams.track > UDP->tracks>> STRETCH(&newparams) ||
+ /* check if reserved bits are set */
+ (newparams.stretch & ~(FD_STRETCH | FD_SWAPSIDES)) != 0)
return -EINVAL;
if ( type){
if ( !suser() )
{
int drive;
int old_dev;
+ int try;
+ char *tmp;
if (!filp) {
DPRINT("Weird, open called with filp=0\n");
}
drive = DRIVE(inode->i_rdev);
+
if (drive >= N_DRIVE ||
!( ALLOWED_DRIVE_MASK & ( 1 << drive)) ||
fdc_state[FDC(drive)].version == FDC_NONE)
else
UDRS->fd_ref++;
+ if (!floppy_track_buffer){
+ /* if opening an ED drive, reserve a big buffer,
+ * else reserve a small one */
+ if ((UDP->cmos == 6) || (UDP->cmos == 5))
+ try = 64; /* Only 48 actually useful */
+ else
+ try = 32; /* Only 24 actually useful */
+
+ tmp=(char *)dma_mem_alloc(1024 * try);
+ if (!tmp) {
+ try >>= 1; /* buffer only one side */
+ if (try < 16)
+ try=16;
+ tmp= (char *)dma_mem_alloc(1024*try);
+ }
+ if (!tmp) {
+ DPRINT("Unable to allocate DMA memory\n");
+ RETERR(ENXIO);
+ }
+ if (floppy_track_buffer){
+ free_pages((unsigned long)tmp,__get_order(try*1024));
+ }else {
+ floppy_track_buffer = tmp;
+ max_buffer_sectors = try;
+ }
+ }
+
UDRS->fd_device = inode->i_rdev;
if (old_dev && old_dev != inode->i_rdev) {
DPRINT("Read linux/drivers/block/README.fd\n");
}
-#ifdef FD_MODULE
-static
-#endif
-int new_floppy_init(void)
+int floppy_init(void)
{
int i,drive;
int have_no_fdc=0;
blk_size[MAJOR_NR] = floppy_sizes;
blksize_size[MAJOR_NR] = floppy_blocksizes;
blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
- del_timer(&fd_timeout);
+ reschedule_timeout(MAXTIMEOUT, "floppy init", MAXTIMEOUT);
config_types();
fdc_state[0].address = FDC1;
UDRS->keep_data = 0;
UDRS->fd_ref = 0;
UDRS->fd_device = 0;
+ floppy_track_buffer = NULL;
+ max_buffer_sectors = 0;
UDRWE->write_errors = 0;
UDRWE->first_error_sector = 0;
UDRWE->first_error_generation = 0;
FDCS->address = -1;
continue;
}
+
+ request_region(FDCS->address, 6, "floppy");
+ request_region(FDCS->address+7, 1, "floppy DIR");
+ /* address + 6 is reserved, and may be taken by IDE.
+ * Unfortunately, Adaptec doesn't know this :-(, */
have_no_fdc = 0;
/* Not all FDCs seem to be able to handle the version command
user_reset_fdc(-1,FD_RESET_ALWAYS,0);
}
fdc=0;
+ del_timer(&fd_timeout);
current_drive = 0;
floppy_release_irq_and_dma();
initialising=0;
return have_no_fdc;
}
-/* stupid compatibility hack... */
-void floppy_init(void)
-{
- new_floppy_init();
-}
-
static int floppy_grab_irq_and_dma(void)
{
int i;
#ifdef CONFIG_FLOPPY_SANITY
int drive;
#endif
+ long tmpsize;
+ void *tmpaddr;
+
cli();
if (--usage_count){
sti();
set_dor(1, ~8, 0);
#endif
floppy_enable_hlt();
+
+ if (floppy_track_buffer && max_buffer_sectors) {
+ tmpsize = max_buffer_sectors*1024;
+ tmpaddr = (void *)floppy_track_buffer;
+ floppy_track_buffer = 0;
+ max_buffer_sectors = 0;
+ free_pages((unsigned long)tmpaddr, __get_order(tmpsize));
+ }
+
#ifdef CONFIG_FLOPPY_SANITY
for(drive=0; drive < N_FDC * 4; drive++)
if( motor_off_timer[drive].next )
printk("motor off timer %d still active\n", drive);
if(fd_timeout.next)
- printk("floppy timer still active\n");
+ printk("floppy timer still active:%s\n", timeout_message);
if (fd_timer.next)
printk("auxiliary floppy timer still active\n");
if(floppy_tq.sync)
#endif
}
+
+#ifdef MODULE
+
+extern char *get_options(char *str, int *ints);
+
+#if 0
+/* assuming that insmod is compiled as a.out binary using a shared
+ C library ... */
+int ENVIRON = 0x60090b34;
+
+static void
+mod_setup(char *name,
+ void (*setup)(char *, int *)) {
+ char **environ,*env,*ptr,c,i;
+ char line[100];
+ int ints[11];
+
+ environ = (char **) get_fs_long( ENVIRON );
+
+ while((env = (char *)get_fs_long(environ))){
+ for(i=0; i<strlen(name); i++)
+ if ( (char) get_fs_byte(env++) != name[i] )
+ break;
+ if(i == strlen(name)){
+ ptr=line;
+ while(ptr < line+99){
+ c = (char)get_fs_byte(env++);
+ if ( c== ' ' || !c ){
+ *ptr='\0';
+ if(ptr!=line)
+ setup(get_options(line,ints),
+ ints);
+ ptr=line;
+ if (!c)
+ break;
+ } else
+ *ptr++ = c;
+ }
+ }
+ environ++;
+ }
+}
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+int init_module(void)
+{
+ int ret;
+ printk("inserting floppy driver for %s\n", kernel_version);
+
+ /*mod_setup("floppy=", floppy_setup);*/
+ /* Can't do that any more, insmod is now ELF */
+
+ ret = floppy_init();
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ int fdc;
+
+ for(fdc=0; fdc<2; fdc++)
+ if (FDCS->address != -1){
+ release_region(FDCS->address, 6);
+ release_region(FDCS->address+7, 1);
+ }
+
+ unregister_blkdev(MAJOR_NR, "fd");
+
+ blk_dev[MAJOR_NR].request_fn = 0;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
int i;
outb_p(4,HD_CMD);
- for(i = 0; i < 1000; i++) nop();
+ for(i = 0; i < 1000; i++) barrier();
outb_p(hd_info[0].ctl & 0x0f,HD_CMD);
- for(i = 0; i < 1000; i++) nop();
+ for(i = 0; i < 1000; i++) barrier();
if (drive_busy())
printk("hd: controller still busy\n");
else if ((hd_error = inb(HD_ERROR)) != 1)
err = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
if (err)
return err;
- put_fs_byte(bios_info[dev].head,
+ put_user(bios_info[dev].head,
(char *) &loc->heads);
- put_fs_byte(bios_info[dev].sect,
+ put_user(bios_info[dev].sect,
(char *) &loc->sectors);
- put_fs_word(bios_info[dev].cyl,
+ put_user(bios_info[dev].cyl,
(short *) &loc->cylinders);
- put_fs_long(hd[MINOR(inode->i_rdev)].start_sect,
+ put_user(hd[MINOR(inode->i_rdev)].start_sect,
(long *) &loc->start);
return 0;
case BLKRASET:
err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
if (err)
return err;
- put_fs_long(read_ahead[MAJOR(inode->i_rdev)],(long *) arg);
+ put_user(read_ahead[MAJOR(inode->i_rdev)],(long *) arg);
return 0;
case BLKGETSIZE: /* Return device size */
if (!arg) return -EINVAL;
err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
if (err)
return err;
- put_fs_long(hd[MINOR(inode->i_rdev)].nr_sects, (long *) arg);
+ put_user(hd[MINOR(inode->i_rdev)].nr_sects, (long *) arg);
return 0;
case BLKFLSBUF:
if(!suser()) return -EACCES;
err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
if (err)
return err;
- put_fs_long(unmask_intr[dev], (long *) arg);
+ put_user(unmask_intr[dev], (long *) arg);
return 0;
case HDIO_GET_MULTCOUNT:
err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
if (err)
return err;
- put_fs_long(mult_count[dev], (long *) arg);
+ put_user(mult_count[dev], (long *) arg);
return 0;
case HDIO_SET_MULTCOUNT:
*/
static void hd_geninit(void)
{
- int drive, i;
- extern struct drive_info drive_info;
- unsigned char *BIOS = (unsigned char *) &drive_info;
- int cmos_disks;
+ int i;
+
+#ifdef __i386__
+ if (!NR_HD) {
+ extern struct drive_info drive_info;
+ unsigned char *BIOS = (unsigned char *) &drive_info;
+ int cmos_disks, drive;
- if (!NR_HD) {
for (drive=0 ; drive<2 ; drive++) {
bios_info[drive].cyl = hd_info[drive].cyl = *(unsigned short *) BIOS;
bios_info[drive].head = hd_info[drive].head = *(2+BIOS);
else
NR_HD = 1;
}
+#endif /* __i386__ */
i = NR_HD;
while (i-- > 0) {
/*
#define REALLY_SLOW_IO /* most systems can safely undef this */
#include <asm/io.h>
+#ifdef __alpha__
+# ifndef SUPPORT_VLB_SYNC
+# define SUPPORT_VLB_SYNC 0
+# endif
+#endif
+
#undef REALLY_FAST_IO /* define if ide ports are perfect */
#define INITIAL_MULT_COUNT 0 /* off=0; on=2,4,8,16,32, etc.. */
#ifndef SUPPORT_VLB_32BIT /* 1 to support 32bit I/O on VLB */
/*
* Timeouts for various operations:
*/
-#define WAIT_DRQ 5 /* 50msec - spec allows up to 20ms */
-#define WAIT_READY 3 /* 30msec - should be instantaneous */
-#define WAIT_PIDENTIFY 100 /* 1sec - should be less than 3ms (?) */
-#define WAIT_WORSTCASE 3000 /* 30sec - worst case when spinning up */
-#define WAIT_CMD 1000 /* 10sec - maximum wait for an IRQ to happen */
+#define WAIT_DRQ (5*HZ/100) /* 50msec - spec allows up to 20ms */
+#define WAIT_READY (3*HZ/100) /* 30msec - should be instantaneous */
+#define WAIT_PIDENTIFY (1*HZ) /* 1sec - should be less than 3ms (?) */
+#define WAIT_WORSTCASE (30*HZ) /* 30sec - worst case when spinning up */
+#define WAIT_CMD (10*HZ) /* 10sec - maximum wait for an IRQ to happen */
/*
* Now for the data we need to maintain per-device: ide_dev_t
#include "blk.h"
/*
- * For really screwy hardware (hey, at least it *can* be used with Linux!
+ * For really screwy hardware (hey, at least it *can* be used with Linux!)
*/
#if (DISK_RECOVERY_TIME > 0)
static unsigned long ide_lastreq[] = {0,0}; /* completion time of last I/O */
return -EINVAL;
if ((err = verify_area(VERIFY_WRITE, (long *)useraddr, sizeof(long))))
return err;
- put_fs_long((unsigned)value, (long *) useraddr);
+ put_user((unsigned)value, (long *) useraddr);
return 0;
}
if (!loc || dev->type != disk) return -EINVAL;
err = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
if (err) return err;
- put_fs_byte(dev->bios_head,
+ put_user(dev->bios_head,
(char *) &loc->heads);
- put_fs_byte(dev->bios_sect,
+ put_user(dev->bios_sect,
(char *) &loc->sectors);
- put_fs_word(dev->bios_cyl,
+ put_user(dev->bios_cyl,
(short *) &loc->cylinders);
- put_fs_long((unsigned)ide_hd[DEV_HWIF][MINOR(inode->i_rdev)].start_sect,
+ put_user((unsigned)ide_hd[DEV_HWIF][MINOR(inode->i_rdev)].start_sect,
(long *) &loc->start);
return 0;
else {
if (!(err = verify_area(VERIFY_WRITE,(long *)arg,sizeof(long))))
{
- args = get_fs_long((long *)arg);
+ args = get_user((long *)arg);
err = do_drive_cmd(inode->i_rdev,(char *)&args);
- put_fs_long(args,(long *)arg);
+ put_user(args,(long *)arg);
}
}
return err;
/* check for word-swapped "capacity" field in id information */
check = (id->cur_capacity0 << 16) | id->cur_capacity1;
- if (check == capacity) /* was it swapped? */
- *((int *)&id->cur_capacity0) = capacity; /* fix it */
+ if (check == capacity) { /* was it swapped? */
+ /* yes, bring it into little-endian order: */
+ id->cur_capacity0 = (capacity >> 0) & 0xffff;
+ id->cur_capacity1 = (capacity >> 16) & 0xffff;
+ }
}
/* Use physical geometry if what we have still makes no sense */
if ((!dev->head || dev->head > 16) && id->heads && id->heads <= 16) {
printk("\n");
}
+/*
+ * Delay for *at least* 10ms. As we don't know how much time is left
+ * until the next tick occurs, we wait an extra tick to be safe.
+ */
static void delay_10ms (void)
{
- unsigned long timer = jiffies + 2;
+ unsigned long timer = jiffies + (HZ + 99)/100 + 1;
while (timer > jiffies);
}
OUT_BYTE(dev->ctl|2,HD_CMD); /* disable device irq */
#if PROBE_FOR_IRQS
if (!irq_probed[DEV_HWIF]) { /* already probed for IRQ? */
+ probe_irq_off(probe_irq_on()); /* clear dangling irqs */
irqs = probe_irq_on(); /* start monitoring irqs */
OUT_BYTE(dev->ctl,HD_CMD); /* enable device irq */
}
* 0x19 for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS. A non-zero value
* means we have an AT controller hard disk for that drive.
*/
-extern struct drive_info_struct drive_info;
static void probe_cmos_for_drives (void)
{
+#ifdef __i386__
+ extern struct drive_info_struct drive_info;
byte drive, cmos_disks, *BIOS = (byte *) &drive_info;
outb_p(0x12,0x70); /* specify CMOS address 0x12 */
}
BIOS += 16;
}
+#endif
}
#endif /* CONFIG_BLK_DEV_HD */
continue;
#else
probe_cmos_for_drives ();
-#endif /* CONFIG_BLJ_DEV_HD */
+#endif /* CONFIG_BLK_DEV_HD */
probe_mem_start = (mem_start + 3uL) & ~3uL;
probe_for_drives (hwif);
mem_start = probe_mem_start;
void ll_rw_swap_file(int rw, int dev, unsigned int *b, int nb, char *buf)
{
- int i;
+ int i, j;
int buffersize;
- struct request * req;
+ struct request * req[8];
unsigned int major = MAJOR(dev);
struct semaphore sem = MUTEX_LOCKED;
buffersize = PAGE_SIZE / nb;
- for (i=0; i<nb; i++, buf += buffersize)
+ for (j=0, i=0; i<nb;)
{
- req = get_request_wait(NR_REQUEST, dev);
- req->cmd = rw;
- req->errors = 0;
- req->sector = (b[i] * buffersize) >> 9;
- req->nr_sectors = buffersize >> 9;
- req->current_nr_sectors = buffersize >> 9;
- req->buffer = buf;
- req->sem = &sem;
- req->bh = NULL;
- req->next = NULL;
- add_request(major+blk_dev,req);
- down(&sem);
+ for (; j < 8 && i < nb; j++, i++, buf += buffersize)
+ {
+ if (j == 0) {
+ req[j] = get_request_wait(NR_REQUEST, dev);
+ } else {
+ cli();
+ req[j] = get_request(NR_REQUEST, dev);
+ sti();
+ if (req[j] == NULL)
+ break;
+ }
+ req[j]->cmd = rw;
+ req[j]->errors = 0;
+ req[j]->sector = (b[i] * buffersize) >> 9;
+ req[j]->nr_sectors = buffersize >> 9;
+ req[j]->current_nr_sectors = buffersize >> 9;
+ req[j]->buffer = buf;
+ req[j]->sem = &sem;
+ req[j]->bh = NULL;
+ req[j]->next = NULL;
+ add_request(major+blk_dev,req[j]);
+ }
+ while (j > 0) {
+ j--;
+ down(&sem);
+ }
}
}
if (arg) {
if ((err = verify_area(VERIFY_WRITE,geometry,sizeof(*geometry))))
return (err);
- put_fs_byte(xd_info[dev].heads,(char *) &geometry->heads);
- put_fs_byte(xd_info[dev].sectors,(char *) &geometry->sectors);
- put_fs_word(xd_info[dev].cylinders,(short *) &geometry->cylinders);
- put_fs_long(xd[MINOR(inode->i_rdev)].start_sect,(long *) &geometry->start);
+ put_user(xd_info[dev].heads, &geometry->heads);
+ put_user(xd_info[dev].sectors, &geometry->sectors);
+ put_user(xd_info[dev].cylinders, &geometry->cylinders);
+ put_user(xd[MINOR(inode->i_rdev)].start_sect,&geometry->start);
return (0);
}
if (arg) {
if ((err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long))))
return (err);
- put_fs_long(xd[MINOR(inode->i_rdev)].nr_sects,(long *) arg);
+ put_user(xd[MINOR(inode->i_rdev)].nr_sects,(long *) arg);
return (0);
}
static u_char xd_setup_dma (u_char mode,u_char *buffer,u_int count)
{
if (buffer < ((u_char *) 0x1000000 - count)) { /* transfer to address < 16M? */
- if (((u_int) buffer & 0xFFFF0000) != ((u_int) buffer + count) & 0xFFFF0000) {
+ if (((u_int) buffer & 0xFFFF0000) != (((u_int) buffer + count) & 0xFFFF0000)) {
#ifdef DEBUG_OTHER
printk("xd_setup_dma: using PIO, transfer overlaps 64k boundary\n");
#endif /* DEBUG_OTHER */
return -EAGAIN;
ATIXL_MSE_DISABLE_UPDATE();
/* Allowed interrupts to occur during data gathering - shouldn't hurt */
- put_fs_byte((char)(~mouse.latch_buttons&7) | 0x80 , buffer);
+ put_user((char)(~mouse.latch_buttons&7) | 0x80 , buffer);
if (mouse.dx < -127)
mouse.dx = -127;
if (mouse.dx > 127)
mouse.dx = 127;
- put_fs_byte((char)mouse.dx, buffer + 1);
+ put_user((char)mouse.dx, buffer + 1);
if (mouse.dy < -127)
mouse.dy = -127;
if (mouse.dy > 127)
mouse.dy = 127;
- put_fs_byte((char)-mouse.dy, buffer + 2);
+ put_user((char)-mouse.dy, buffer + 2);
for(i = 3; i < count; i++)
- put_fs_byte(0x00, buffer + i);
+ put_user(0x00, buffer + i);
mouse.dx = 0;
mouse.dy = 0;
mouse.latch_buttons = mouse.buttons;
* Obtain the current mouse parameters and limit as appropriate for
* the return data format. Interrupts are only disabled while
* obtaining the parameters, NOT during the puts_fs_byte() calls,
- * so paging in put_fs_byte() does not effect mouse tracking.
+ * so paging in put_user() does not effect mouse tracking.
*/
MSE_INT_OFF();
mouse.ready = 0;
MSE_INT_ON();
- put_fs_byte(buttons | 0x80, buffer);
- put_fs_byte((char)dx, buffer + 1);
- put_fs_byte((char)dy, buffer + 2);
+ put_user(buttons | 0x80, buffer);
+ put_user((char)dx, buffer + 1);
+ put_user((char)dy, buffer + 2);
for (r = 3; r < count; r++)
- put_fs_byte(0x00, buffer + r);
+ put_user(0x00, buffer + r);
return r;
}
arg += cmapsz;
if (set)
for (i=0; i<cmapsz ; i++)
- *(charmap+i) = get_fs_byte(arg+i);
+ *(charmap+i) = get_user(arg+i);
else
for (i=0; i<cmapsz ; i++)
- put_fs_byte(*(charmap+i), arg+i);
+ put_user(*(charmap+i), arg+i);
};
cli();
return i;
for (i=0; i<E_TABSZ ; i++)
- p[i] = UNI_DIRECT_BASE | get_fs_byte(arg+i);
+ p[i] = UNI_DIRECT_BASE | get_user(arg+i);
set_inverse_transl(USER_MAP);
return 0;
for (i=0; i<E_TABSZ ; i++)
{
ch = conv_uni_to_pc(p[i]);
- put_fs_byte((ch & ~0xff) ? 0 : ch, arg+i);
+ put_user((ch & ~0xff) ? 0 : ch, arg+i);
}
return 0;
}
return i;
for (i=0; i<E_TABSZ ; i++)
- p[i] = get_fs_word(arg+i);
+ p[i] = get_user(arg+i);
set_inverse_transl(USER_MAP);
return 0;
return i;
for (i=0; i<E_TABSZ ; i++)
- put_fs_word(p[i], arg+i);
+ put_user(p[i], arg+i);
return 0;
}
if (!hashtable_contents_valid)
con_clear_unimap(&hashdefaults);
while(ct) {
- u = get_fs_word(&list->unicode);
+ u = get_user(&list->unicode);
i = u % hashsize;
lct = 1;
while ((hu = hashtable[i].unicode) != 0xffff && hu != u) {
if (lct > hashlevel)
hashlevel = lct;
hashtable[i].unicode = u;
- hashtable[i].fontpos = get_fs_word(&list->fontpos);
+ hashtable[i].fontpos = get_user(&list->fontpos);
list++;
ct--;
}
for (i = 0; i<hashsize; i++)
if (hashtable[i].unicode != 0xffff) {
if (ect++ < ct) {
- put_fs_word(hashtable[i].unicode, &list->unicode);
- put_fs_word(hashtable[i].fontpos, &list->fontpos);
+ put_user(hashtable[i].unicode, &list->unicode);
+ put_user(hashtable[i].fontpos, &list->fontpos);
list++;
}
}
- put_fs_word(ect, uct);
+ put_user(ect, uct);
return ((ect <= ct) ? 0 : -ENOMEM);
}
| ((status & CyRI) ? TIOCM_RNG : 0)
| ((status & CyDSR) ? TIOCM_DSR : 0)
| ((status & CyCTS) ? TIOCM_CTS : 0);
- put_fs_long(result,(unsigned long *) value);
+ put_user(result,value);
return 0;
} /* get_modem_info */
int card,chip,channel;
unsigned char *base_addr;
unsigned long flags;
- unsigned int arg = get_fs_long((unsigned long *) value);
+ unsigned int arg = get_user(value);
card = info->card;
channel = (info->line) - (cy_card[card].first_line);
ret_val = error;
break;
}
- put_fs_long(C_CLOCAL(tty) ? 1 : 0,
- (unsigned long *) arg);
+ put_user(C_CLOCAL(tty) ? 1 : 0,
+ (unsigned int *) arg);
break;
case TIOCSSOFTCAR:
arg = get_fs_long((unsigned long *) arg);
temp = buf;
while (count > 0) {
- c = get_fs_byte(temp);
+ c = get_user(temp);
retval = lp_char_polled(c, minor);
/* only update counting vars if character was printed */
if (retval) { count--; temp++;
lp_release
};
-#ifndef MODULE
+#ifdef MODULE
+char kernel_version[]=UTS_RELEASE;
+int init_module(void)
+#else
long lp_init(long kmem_start)
+#endif
{
int offset = 0;
unsigned int testvalue = 0;
int count = 0;
if (register_chrdev(LP_MAJOR,"lp",&lp_fops)) {
- printk("unable to get major %d for line printer\n", LP_MAJOR);
+ printk("lp: unable to get major %d\n", LP_MAJOR);
+#ifdef MODULE
+ return -EIO;
+#else
return kmem_start;
+#endif
}
/* take on all known port values */
for (offset = 0; offset < LP_NO; offset++) {
printk("lp%d at 0x%04x, ", offset,LP_B(offset));
request_region(LP_B(offset), 3, "lp");
if (LP_IRQ(offset))
- printk("using IRQ%d\n", LP_IRQ(offset));
+ printk("(irq = %d)\n", LP_IRQ(offset));
else
- printk("using polling driver\n");
+ printk("(polling)\n");
count++;
}
}
if (count == 0)
- printk("lp_init: no lp devices found\n");
- return kmem_start;
-}
-
-#else
+ printk("lp: Driver configured but no interfaces found.\n");
-char kernel_version[]= UTS_RELEASE;
-int init_module(void)
-{
- int offset = 0;
- unsigned int testvalue = 0;
- int count = 0;
-
- if (register_chrdev(LP_MAJOR,"lp",&lp_fops)) {
- printk("unable to get major %d for line printer\n", LP_MAJOR);
- return -EIO;
- }
- /* take on all known port values */
- for (offset = 0; offset < LP_NO; offset++) {
- /* write to port & read back to check */
- outb_p( LP_DUMMY, LP_B(offset));
- for (testvalue = 0 ; testvalue < LP_DELAY ; testvalue++)
- ;
- testvalue = inb_p(LP_B(offset));
- if (testvalue == LP_DUMMY) {
- LP_F(offset) |= LP_EXIST;
- lp_reset(offset);
- printk("lp%d at 0x%04x, ", offset,LP_B(offset));
- request_region(LP_B(offset),3,"lp");
- if (LP_IRQ(offset))
- printk("using IRQ%d\n", LP_IRQ(offset));
- else
- printk("using polling driver\n");
- count++;
- }
- }
- if (count == 0)
- printk("lp_init: no lp devices found\n");
+#ifdef MODULE
return 0;
+#else
+ return kmem_start;
+#endif
}
+#ifdef MODULE
void cleanup_module(void)
{
int offset;
if(MOD_IN_USE)
printk("lp: busy - remove delayed\n");
- else
+ else {
unregister_chrdev(LP_MAJOR,"lp");
for (offset = 0; offset < LP_NO; offset++)
if(LP_F(offset) && LP_EXIST)
release_region(LP_B(offset),3);
+ }
}
#endif
count = high_memory - p;
read = 0;
while (p < PAGE_SIZE && count > 0) {
- put_fs_byte(0,buf);
+ put_user(0,buf);
buf++;
p++;
count--;
char * tmp = buf;
while (count-- > 0 && i < 65536) {
- put_fs_byte(inb(i),tmp);
+ put_user(inb(i),tmp);
i++;
tmp++;
}
char * tmp = buf;
while (count-- > 0 && i < 65536) {
- outb(get_fs_byte(tmp),i);
+ outb(get_user(tmp),i);
i++;
tmp++;
}
int left;
for (left = count; left > 0; left--) {
- put_fs_byte(0,buf);
+ put_user(0,buf);
buf++;
}
return count;
return -EINVAL;
if (!mouse.ready)
return -EAGAIN;
- put_fs_byte(mouse.buttons | 0x80, buffer);
+ put_user(mouse.buttons | 0x80, buffer);
dx = mouse.dx < -127 ? -127 : mouse.dx > 127 ? 127 : mouse.dx;
dy = mouse.dy < -127 ? 127 : mouse.dy > 127 ? -127 : -mouse.dy;
- put_fs_byte((char)dx, buffer + 1);
- put_fs_byte((char)dy, buffer + 2);
+ put_user((char)dx, buffer + 1);
+ put_user((char)dy, buffer + 2);
for (i = 3; i < count; i++)
- put_fs_byte(0x00, buffer + i);
+ put_user(0x00, buffer + i);
mouse.dx -= dx;
mouse.dy += dy;
mouse.ready = 0;
if (tty->packet && tty->link->ctrl_status) {
if (b != buf)
break;
- put_fs_byte(tty->link->ctrl_status, b++);
+ put_user(tty->link->ctrl_status, b++);
tty->link->ctrl_status = 0;
break;
}
/* Deal with packet mode. */
if (tty->packet && b == buf) {
- put_fs_byte(TIOCPKT_DATA, b++);
+ put_user(TIOCPKT_DATA, b++);
nr--;
}
tty->read_cnt--;
enable_bh(TQUEUE_BH);
if (!eol) {
- put_fs_byte(c, b++);
+ put_user(c, b++);
if (--nr)
continue;
break;
tty->canon_data = 0;
}
if (c != __DISABLED_CHAR) {
- put_fs_byte(c, b++);
+ put_user(c, b++);
nr--;
}
break;
}
if (O_OPOST(tty)) {
while (nr > 0) {
- c = get_fs_byte(b);
+ c = get_user(b);
if (opost(c, tty) < 0)
break;
b++; nr--;
while ((inb(AUX_STATUS) & AUX_OBUF_FULL) != AUX_OBUF_FULL
&& retries < MAX_RETRIES) { /* wait for ack */
current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + 5;
+ current->timeout = jiffies + (5*HZ + 99) / 100;
schedule();
retries++;
}
static void release_aux(struct inode * inode, struct file * file)
{
+#ifndef __alpha__
aux_write_dev(AUX_DISABLE_DEV); /* disable aux device */
+#endif
aux_write_cmd(AUX_INTS_OFF); /* disable controller ints */
poll_aux_status();
outb_p(AUX_DISABLE,AUX_COMMAND); /* Disable Aux device */
outb_p(AUX_MAGIC_WRITE,AUX_COMMAND);
if (!poll_aux_status())
return -EIO;
- outb_p(get_fs_byte(buffer++),AUX_OUTPUT_PORT);
+ outb_p(get_user(buffer++),AUX_OUTPUT_PORT);
}
inode->i_mtime = CURRENT_TIME;
return count;
while (i--) {
if (!poll_qp_status())
return -EIO;
- outb_p(get_fs_byte(buffer++), qp_data);
+ outb_p(get_user(buffer++), qp_data);
}
inode->i_mtime = CURRENT_TIME;
return count;
}
while (i > 0 && !queue_empty()) {
c = get_from_queue();
- put_fs_byte(c, buffer++);
+ put_user(c, buffer++);
i--;
}
aux_ready = !queue_empty();
if (inb_p(AUX_STATUS) & AUX_OBUF_FULL == AUX_OBUF_FULL)
inb_p(AUX_INPUT_PORT);
current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + 5;
+ current->timeout = jiffies + (5*HZ + 99) / 100;
schedule();
retries++;
}
if (inb_p(qp_status)&(QP_RX_FULL))
inb_p(qp_data);
current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + 5;
+ current->timeout = jiffies + (5*HZ + 99) / 100;
schedule();
retries++;
}
if (scc2 == scc || !(scc2->tty && grp2))
return 0;
- if (grp1 & 0x3f == grp2 & 0x3f)
+ if ((grp1 & 0x3f) == (grp2 & 0x3f))
{
if ( (grp1 & TXGROUP) && (scc2->wreg[R5] & RTS) )
return 1;
restore_flags(flags);
- put_fs_long(result,(unsigned long *) arg);
+ put_user(result,(unsigned int *) arg);
return 0;
case TIOCMBIS:
case TIOCMBIC:
scc->wreg[R5] &= ~RTS;
break;
case TIOCMSET:
- value = get_fs_long((unsigned long *) arg);
+ value = get_user((unsigned int *) arg);
if(value & TIOCM_DTR)
scc->wreg[R5] |= DTR;
{ unsigned short *args, xs, ys, xe, ye;
args = (unsigned short *)(arg + 1);
- xs = get_fs_word(args++) - 1;
- ys = get_fs_word(args++) - 1;
- xe = get_fs_word(args++) - 1;
- ye = get_fs_word(args++) - 1;
- sel_mode = get_fs_word(args);
+ xs = get_user(args++) - 1;
+ ys = get_user(args++) - 1;
+ xe = get_user(args++) - 1;
+ ye = get_user(args++) - 1;
+ sel_mode = get_user(args);
xs = limit(xs, video_num_columns - 1);
ys = limit(ys, video_num_lines - 1);
status = serial_in(info, UART_LSR);
sti();
result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
- put_fs_long(result,(unsigned long *) value);
+ put_user(result,value);
return 0;
}
| ((status & UART_MSR_RI) ? TIOCM_RNG : 0)
| ((status & UART_MSR_DSR) ? TIOCM_DSR : 0)
| ((status & UART_MSR_CTS) ? TIOCM_CTS : 0);
- put_fs_long(result,(unsigned long *) value);
+ put_user(result,value);
return 0;
}
error = verify_area(VERIFY_READ, value, sizeof(int));
if (error)
return error;
- arg = get_fs_long((unsigned long *) value);
+ arg = get_user(value);
switch (cmd) {
case TIOCMBIS:
if (arg & TIOCM_RTS) {
c = (char *)(*p);
d = (char *)(ts->tasks+n);
for (i=0 ; i<sizeof(struct task_struct) ; i++)
- put_fs_byte(*c++, d++);
- put_fs_long(1, (unsigned long *)(ts->present+n));
+ put_user(*c++, d++);
+ put_user(1, ts->present+n);
}
else
- put_fs_long(0, (unsigned long *)(ts->present+n));
+ put_user(0, ts->present+n);
return(0);
}
#endif
retval = verify_area(VERIFY_READ, (void *) arg, 1);
if (retval)
return retval;
- ch = get_fs_byte((char *) arg);
+ ch = get_user((char *) arg);
tty->ldisc.receive_buf(tty, &ch, &mbz, 1);
return 0;
case TIOCGWINSZ:
redirect = real_tty;
return 0;
case FIONBIO:
- retval = verify_area(VERIFY_READ, (void *) arg, sizeof(long));
+ retval = verify_area(VERIFY_READ, (void *) arg, sizeof(int));
if (retval)
return retval;
- arg = get_fs_long((unsigned long *) arg);
+ arg = get_user((unsigned int *) arg);
if (arg)
file->f_flags |= O_NONBLOCK;
else
sizeof (pid_t));
if (retval)
return retval;
- put_fs_long(real_tty->pgrp, (pid_t *) arg);
+ put_user(real_tty->pgrp, (pid_t *) arg);
return 0;
case TIOCSPGRP:
retval = tty_check_change(real_tty);
(current->tty != real_tty) ||
(real_tty->session != current->session))
return -ENOTTY;
- pgrp = get_fs_long((pid_t *) arg);
+ pgrp = get_user((pid_t *) arg);
if (pgrp < 0)
return -EINVAL;
if (session_of_pgrp(pgrp) != current->session)
return 0;
case TIOCGETD:
retval = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof (unsigned long));
+ sizeof (int));
if (retval)
return retval;
- put_fs_long(tty->ldisc.num, (unsigned long *) arg);
+ put_user(tty->ldisc.num, (int *) arg);
return 0;
case TIOCSETD:
retval = tty_check_change(tty);
if (retval)
return retval;
- arg = get_fs_long((unsigned long *) arg);
+ arg = get_user((int *) arg);
return tty_set_ldisc(tty, arg);
case TIOCLINUX:
if (tty->driver.type != TTY_DRIVER_TYPE_CONSOLE)
retval = verify_area(VERIFY_READ, (void *) arg, 1);
if (retval)
return retval;
- switch (retval = get_fs_byte((char *)arg))
+ switch (retval = get_user((char *)arg))
{
case 0:
case 8:
* kernel-internal variable; programs not closely
* related to the kernel should not use this.
*/
- put_fs_byte(shift_state,arg);
+ put_user(shift_state,(char *) arg);
return 0;
case 7:
- put_fs_byte(mouse_reporting(),arg);
+ put_user(mouse_reporting(),(char *) arg);
return 0;
case 10:
set_vesa_blanking(arg);
return 0;
case TIOCOUTQ:
retval = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof (unsigned long));
+ sizeof (int));
if (retval)
return retval;
if (tty->driver.chars_in_buffer)
- put_fs_long(tty->driver.chars_in_buffer(tty),
- (unsigned long *) arg);
+ put_user(tty->driver.chars_in_buffer(tty),
+ (int *) arg);
else
- put_fs_long(0, (unsigned long *) arg);
+ put_user(0, (int *) arg);
return 0;
case TIOCINQ:
retval = verify_area(VERIFY_WRITE, (void *) arg,
tty->driver.subtype != PTY_TYPE_MASTER)
return -ENOTTY;
retval = verify_area(VERIFY_READ, (void *) arg,
- sizeof (unsigned long));
+ sizeof (int));
if (retval)
return retval;
- if (get_fs_long(arg)) {
+ if (get_user((int*)arg)) {
if (!tty->packet) {
tty->packet = 1;
tty->link->ctrl_status = 0;
if (!attr) {
org = screen_pos(cons, p, viewed);
while (count-- > 0)
- put_fs_byte(scr_readw(org++) & 0xff, buf++);
+ put_user(scr_readw(org++) & 0xff, buf++);
} else {
if (p < HEADER_SIZE) {
char header[HEADER_SIZE];
header[1] = (char) video_num_columns;
getconsxy(cons, header+2);
while (p < HEADER_SIZE && count-- > 0)
- put_fs_byte(header[p++], buf++);
+ put_user(header[p++], buf++);
}
p -= HEADER_SIZE;
org = screen_pos(cons, p/2, viewed);
if ((p & 1) && count-- > 0)
- put_fs_byte(scr_readw(org++) >> 8, buf++);
+ put_user(scr_readw(org++) >> 8, buf++);
while (count > 1) {
- put_fs_word(scr_readw(org++), buf);
+ put_user(scr_readw(org++), (unsigned short *) buf);
buf += 2;
count -= 2;
}
if (count > 0)
- put_fs_byte(scr_readw(org) & 0xff, buf++);
+ put_user(scr_readw(org) & 0xff, buf++);
}
read = buf - buf0;
file->f_pos += read;
org = screen_pos(cons, p, viewed);
while (count-- > 0) {
scr_writew((scr_readw(org) & 0xff00) |
- get_fs_byte(buf++), org);
+ get_user(buf++), org);
org++;
}
} else {
char header[HEADER_SIZE];
getconsxy(cons, header+2);
while (p < HEADER_SIZE && count-- > 0)
- header[p++] = get_fs_byte(buf++);
+ header[p++] = get_user(buf++);
if (!viewed)
putconsxy(cons, header+2);
}
p -= HEADER_SIZE;
org = screen_pos(cons, p/2, viewed);
if ((p & 1) && count-- > 0) {
- scr_writew((get_fs_byte(buf++) << 8) |
+ scr_writew((get_user(buf++) << 8) |
(scr_readw(org) & 0xff), org);
org++;
}
while (count > 1) {
- scr_writew(get_fs_word(buf), org++);
+ scr_writew(get_user((unsigned short *) buf), org++);
buf += 2;
count -= 2;
}
if (count > 0)
scr_writew((scr_readw(org) & 0xff00) |
- get_fs_byte(buf++), org);
+ get_user(buf++), org);
}
written = buf - buf0;
file->f_pos += written;
void set_vesa_blanking(const unsigned long arg)
{
- char *argp = (char *)(arg + 1);
- unsigned int mode = get_fs_byte(argp);
+ unsigned char *argp = (unsigned char *)(arg + 1);
+ unsigned int mode = get_user(argp);
vesa_blanking_mode = ((mode < 3) ? mode : 0);
}
*/
i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned char));
if (!i)
- put_fs_byte(KB_101, (char *) arg);
+ put_user(KB_101, (char *) arg);
return i;
case KDADDIO:
return 0;
case KDGETMODE:
- i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned long));
+ i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
if (!i)
- put_fs_long(vt_cons[console]->vc_mode, (unsigned long *) arg);
+ put_user(vt_cons[console]->vc_mode, (int *) arg);
return i;
case KDMAPDISP:
return 0;
case KDGKBMODE:
- i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned long));
+ i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
if (!i) {
ucval = ((kbd->kbdmode == VC_RAW) ? K_RAW :
(kbd->kbdmode == VC_MEDIUMRAW) ? K_MEDIUMRAW :
(kbd->kbdmode == VC_UNICODE) ? K_UNICODE :
K_XLATE);
- put_fs_long(ucval, (unsigned long *) arg);
+ put_user(ucval, (int *) arg);
}
return i;
return 0;
case KDGKBMETA:
- i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned long));
+ i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
if (!i) {
ucval = (vc_kbd_mode(kbd, VC_META) ? K_ESCPREFIX :
K_METABIT);
- put_fs_long(ucval, (unsigned long *) arg);
+ put_user(ucval, (int *) arg);
}
return i;
i = verify_area(VERIFY_WRITE, (void *)a, sizeof(struct kbkeycode));
if (i)
return i;
- sc = get_fs_long((int *) &a->scancode);
+ sc = get_user(&a->scancode);
kc = getkeycode(sc);
if (kc < 0)
return kc;
- put_fs_long(kc, (int *) &a->keycode);
+ put_user(kc, &a->keycode);
return 0;
}
i = verify_area(VERIFY_READ, (void *)a, sizeof(struct kbkeycode));
if (i)
return i;
- sc = get_fs_long((int *) &a->scancode);
- kc = get_fs_long((int *) &a->keycode);
+ sc = get_user(&a->scancode);
+ kc = get_user(&a->keycode);
return setkeycode(sc, kc);
}
i = verify_area(VERIFY_WRITE, (void *)a, sizeof(struct kbentry));
if (i)
return i;
- if ((i = get_fs_byte((char *) &a->kb_index)) >= NR_KEYS)
+ if ((i = get_user(&a->kb_index)) >= NR_KEYS)
return -EINVAL;
- if ((s = get_fs_byte((char *) &a->kb_table)) >= MAX_NR_KEYMAPS)
+ if ((s = get_user(&a->kb_table)) >= MAX_NR_KEYMAPS)
return -EINVAL;
key_map = key_maps[s];
if (key_map) {
val = K_HOLE;
} else
val = (i ? K_HOLE : K_NOSUCHMAP);
- put_fs_word(val, (short *) &a->kb_value);
+ put_user(val, &a->kb_value);
return 0;
}
i = verify_area(VERIFY_READ, (void *)a, sizeof(struct kbentry));
if (i)
return i;
- if ((i = get_fs_byte((char *) &a->kb_index)) >= NR_KEYS)
+ if ((i = get_user(&a->kb_index)) >= NR_KEYS)
return -EINVAL;
- if ((s = get_fs_byte((char *) &a->kb_table)) >= MAX_NR_KEYMAPS)
+ if ((s = get_user(&a->kb_table)) >= MAX_NR_KEYMAPS)
return -EINVAL;
- v = get_fs_word(&a->kb_value);
+ v = get_user(&a->kb_value);
if (!i && v == K_NOSUCHMAP) {
/* disallocate map */
key_map = key_maps[s];
i = verify_area(VERIFY_WRITE, (void *)a, sizeof(struct kbsentry));
if (i)
return i;
- if ((i = get_fs_byte(&a->kb_func)) >= MAX_NR_FUNC || i < 0)
+ if ((i = get_user(&a->kb_func)) >= MAX_NR_FUNC || i < 0)
return -EINVAL;
sz = sizeof(a->kb_string) - 1; /* sz should have been
a struct member */
p = func_table[i];
if(p)
for ( ; *p && sz; p++, sz--)
- put_fs_byte(*p, q++);
- put_fs_byte(0, q);
+ put_user(*p, q++);
+ put_user('\0', q);
return ((p && *p) ? -EOVERFLOW : 0);
}
i = verify_area(VERIFY_READ, (void *)a, sizeof(struct kbsentry));
if (i)
return i;
- if ((i = get_fs_byte(&a->kb_func)) >= MAX_NR_FUNC)
+ if ((i = get_user(&a->kb_func)) >= MAX_NR_FUNC)
return -EINVAL;
q = func_table[i];
delta = (q ? -strlen(q) : 1);
sz = sizeof(a->kb_string); /* sz should have been
a struct member */
- for (p = a->kb_string; get_fs_byte(p) && sz; p++,sz--)
+ for (p = a->kb_string; get_user(p) && sz; p++,sz--)
delta++;
if (!sz)
return -EOVERFLOW;
funcbufsize = sz;
}
for (p = a->kb_string, q = func_table[i]; ; p++, q++)
- if (!(*q = get_fs_byte(p)))
+ if (!(*q = get_user(p)))
break;
return 0;
}
i = verify_area(VERIFY_WRITE, (void *) a, sizeof(struct kbdiacrs));
if (i)
return i;
- put_fs_long(accent_table_size, &a->kb_cnt);
+ put_user(accent_table_size, &a->kb_cnt);
memcpy_tofs(a->kbdiacr, accent_table,
accent_table_size*sizeof(struct kbdiacr));
return 0;
i = verify_area(VERIFY_READ, (void *) a, sizeof(struct kbdiacrs));
if (i)
return i;
- ct = get_fs_long(&a->kb_cnt);
+ ct = get_user(&a->kb_cnt);
if (ct >= MAX_DIACR)
return -EINVAL;
accent_table_size = ct;
i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned char));
if (i)
return i;
- put_fs_byte(kbd->ledflagstate |
- (kbd->default_ledflagstate << 4), (char *) arg);
+ put_user(kbd->ledflagstate |
+ (kbd->default_ledflagstate << 4), (char *) arg);
return 0;
case KDSKBLED:
i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned char));
if (i)
return i;
- put_fs_byte(getledstate(), (char *) arg);
+ put_user(getledstate(), (char *) arg);
return 0;
case KDSETLED:
i = verify_area(VERIFY_WRITE, (void *)vtmode, sizeof(struct vt_mode));
if (i)
return i;
- mode = get_fs_byte(&vtmode->mode);
+ mode = get_user(&vtmode->mode);
if (mode != VT_AUTO && mode != VT_PROCESS)
return -EINVAL;
vt_cons[console]->vt_mode.mode = mode;
- vt_cons[console]->vt_mode.waitv = get_fs_byte(&vtmode->waitv);
- vt_cons[console]->vt_mode.relsig = get_fs_word(&vtmode->relsig);
- vt_cons[console]->vt_mode.acqsig = get_fs_word(&vtmode->acqsig);
+ vt_cons[console]->vt_mode.waitv = get_user(&vtmode->waitv);
+ vt_cons[console]->vt_mode.relsig = get_user(&vtmode->relsig);
+ vt_cons[console]->vt_mode.acqsig = get_user(&vtmode->acqsig);
/* the frsig is ignored, so we set it to 0 */
vt_cons[console]->vt_mode.frsig = 0;
vt_cons[console]->vt_pid = current->pid;
i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct vt_mode));
if (i)
return i;
- put_fs_byte(vt_cons[console]->vt_mode.mode, &vtmode->mode);
- put_fs_byte(vt_cons[console]->vt_mode.waitv, &vtmode->waitv);
- put_fs_word(vt_cons[console]->vt_mode.relsig, &vtmode->relsig);
- put_fs_word(vt_cons[console]->vt_mode.acqsig, &vtmode->acqsig);
- put_fs_word(vt_cons[console]->vt_mode.frsig, &vtmode->frsig);
+ put_user(vt_cons[console]->vt_mode.mode, &vtmode->mode);
+ put_user(vt_cons[console]->vt_mode.waitv, &vtmode->waitv);
+ put_user(vt_cons[console]->vt_mode.relsig, &vtmode->relsig);
+ put_user(vt_cons[console]->vt_mode.acqsig, &vtmode->acqsig);
+ put_user(vt_cons[console]->vt_mode.frsig, &vtmode->frsig);
return 0;
}
i = verify_area(VERIFY_WRITE,(void *)vtstat, sizeof(struct vt_stat));
if (i)
return i;
- put_fs_word(fg_console + 1, &vtstat->v_active);
+ put_user(fg_console + 1, &vtstat->v_active);
state = 1; /* /dev/tty0 is always open */
for (i = 0, mask = 2; i < MAX_NR_CONSOLES && mask; ++i, mask <<= 1)
if (VT_IS_IN_USE(i))
state |= mask;
- put_fs_word(state, &vtstat->v_state);
+ put_user(state, &vtstat->v_state);
return 0;
}
* Returns the first available (non-opened) console.
*/
case VT_OPENQRY:
- i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long));
+ i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
if (i)
return i;
for (i = 0; i < MAX_NR_CONSOLES; ++i)
if (! VT_IS_IN_USE(i))
break;
- put_fs_long(i < MAX_NR_CONSOLES ? (i+1) : -1,
- (unsigned long *)arg);
+ put_user(i < MAX_NR_CONSOLES ? (i+1) : -1, (int *) arg);
return 0;
/*
i = verify_area(VERIFY_READ, (void *)vtsizes, sizeof(struct vt_sizes));
if (i)
return i;
- ll = get_fs_word(&vtsizes->v_rows);
- cc = get_fs_word(&vtsizes->v_cols);
+ ll = get_user(&vtsizes->v_rows);
+ cc = get_user(&vtsizes->v_cols);
i = vc_resize(ll, cc);
return i ? i : kd_size_changed(ll, cc);
}
i = verify_area(VERIFY_READ, (void *)vtconsize, sizeof(struct vt_consize));
if (i)
return i;
- ll = get_fs_word(&vtconsize->v_rows);
- cc = get_fs_word(&vtconsize->v_cols);
- vlin = get_fs_word(&vtconsize->v_vlin);
- clin = get_fs_word(&vtconsize->v_clin);
- vcol = get_fs_word(&vtconsize->v_vcol);
- ccol = get_fs_word(&vtconsize->v_ccol);
+ ll = get_user(&vtconsize->v_rows);
+ cc = get_user(&vtconsize->v_cols);
+ vlin = get_user(&vtconsize->v_vlin);
+ clin = get_user(&vtconsize->v_clin);
+ vcol = get_user(&vtconsize->v_vcol);
+ ccol = get_user(&vtconsize->v_ccol);
vlin = vlin ? vlin : video_scan_lines;
if ( clin )
{
kd_size_changed(ll, cc);
return 0;
- }
+ }
case PIO_FONT:
if (!perm)
i = verify_area(VERIFY_READ, (void *)arg, sizeof(struct unimapdesc));
if (i == 0) {
ud = (struct unimapdesc *) arg;
- ct = get_fs_word(&ud->entry_ct);
- list = (struct unipair *) get_fs_long(&ud->entries);
+ ct = get_user(&ud->entry_ct);
+ list = get_user(&ud->entries);
i = verify_area(VERIFY_READ, (void *) list,
ct*sizeof(struct unipair));
}
i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct unimapdesc));
if (i == 0) {
ud = (struct unimapdesc *) arg;
- ct = get_fs_word(&ud->entry_ct);
- list = (struct unipair *) get_fs_long(&ud->entries);
+ ct = get_user(&ud->entry_ct);
+ list = get_user(&ud->entries);
if (ct)
i = verify_area(VERIFY_WRITE, (void *) list,
ct*sizeof(struct unipair));
MODULES := $(MODULES) eexpress.o
endif
+ifdef CONFIG_EEXPRESS_PRO
+NETDRV_OBJS := $(NETDRV_OBJS) eepro.o
+else
+MODULES := $(MODULES) eepro.o
+endif
+
ifdef CONFIG_WAVELAN
NETDRV_OBJS := $(NETDRV_OBJS) wavelan.o
else
extern int hp_plus_probe(struct device *dev);
extern int znet_probe(struct device *);
extern int express_probe(struct device *);
+extern int eepro_probe(struct device *);
extern int el3_probe(struct device *);
extern int at1500_probe(struct device *);
extern int at1700_probe(struct device *);
#ifdef CONFIG_EEXPRESS /* Intel EtherExpress */
&& express_probe(dev)
#endif
+#ifdef CONFIG_EEXPRESS_PRO /* Intel EtherExpress Pro/10 */
+ && eepro_probe(dev)
+#endif
#ifdef CONFIG_DEPCA /* DEC DEPCA */
&& depca_probe(dev)
#endif
Written 1994-95 by Avery Pennarun, derived from skeleton.c by
Donald Becker.
- Contact Avery at: apenwarr@tourism.807-city.on.ca or
+ Contact Avery at: apenwarr@foxnet.net or
RR #5 Pole Line Road, Thunder Bay, ON, Canada P7C 5M9
**********************
**********************
+ v1.02 (95/06/21)
+ - A fix to make "exception" packets sent from Linux receivable
+ on other systems. (The protocol_id byte was sometimes being set
+ incorrectly, and Linux wasn't checking it on receive so it
+ didn't show up)
+ - Updated my email address. Please use apenwarr@foxnet.net
+ from now on.
v1.01 (95/03/24)
- Fixed some IPX-related bugs. (Thanks to Tomasz Motylewski
<motyl@tichy.ch.uj.edu.pl> for the patches to make arcnet work
* 8 times every second.
*
* This should no longer be necessary. if you experience "stuck" ARCnet
- * drivers, please email apenwarr@tourism.807-city.on.ca or I will remove
+ * drivers, please email apenwarr@foxnet.net or I will remove
* this feature in a future release.
*/
#undef USE_TIMER_HANDLER
/**************************************************************************/
static char *version =
- "arcnet.c:v1.01 95/03/24 Avery Pennarun <apenwarr@tourism.807-city.on.ca>\n";
+ "arcnet.c:v1.02 95/06/21 Avery Pennarun <apenwarr@foxnet.net>\n";
/*
Sources:
* make the packet long enough to fit in a 512-byte
* frame.
*/
- arcpacket->raw[offset+0]=arcsoft->protocol_id;
+ arcpacket->raw[offset+0]=hdr->protocol_id;
arcpacket->raw[offset+1]=0xFF; /* FF flag */
arcpacket->raw[offset+2]=0xFF; /* FF padding */
arcpacket->raw[offset+3]=0xFF; /* FF padding */
--- /dev/null
+/* eepro.c: Intel EtherExpress Pro/10 device driver for Linux. */
+/*
+ Written 1994, 1995 by Bao C. Ha.
+
+ Copyright (C) 1994, 1995 by Bao C. Ha.
+
+ This software may be used and distributed
+ according to the terms of the GNU Public License,
+ incorporated herein by reference.
+
+ The author may be reached at bao@saigon.async.com
+ or 418 Hastings Place, Martinez, GA 30907.
+
+ Things remaining to do:
+ Better record keeping of errors.
+ Eliminate transmit interrupt to reduce overhead.
+ Implement "concurrent processing". I won't be doing it!
+ Allow changes to the partition of the transmit and receive
+ buffers, currently the ratio is 3:1 of receive to transmit
+ buffer ratio.
+
+ Bugs:
+
+ If you have a problem of not detecting the 82595 during a
+ reboot (warm reset), disable the FLASH memory should fix it.
+ This is a compatibility hardware problem.
+
+ Versions:
+
+ 0.07a Fix a stat report which counts every packet as a
+ heart-beat failure. (BCH, 6/3/95)
+
+ 0.07 Modified to support all other 82595-based lan cards.
+ The IRQ vector of the EtherExpress Pro will be set
+ according to the value saved in the EEPROM. For other
+ cards, I will do autoirq_request() to grab the next
+ available interrupt vector. (BCH, 3/17/95)
+
+ 0.06a,b Interim released. Minor changes in the comments and
+ print out format. (BCH, 3/9/95 and 3/14/95)
+
+ 0.06 First stable release that I am comfortable with. (BCH,
+ 3/2/95)
+
+ 0.05 Complete testing of multicast. (BCH, 2/23/95)
+
+ 0.04 Adding multicast support. (BCH, 2/14/95)
+
+ 0.03 First widely alpha release for public testing.
+ (BCH, 2/14/95)
+
+*/
+
+static char *version =
+ "eepro.c: v0.07a 6/5/95 Bao C. Ha (bao@saigon.async.com)\n";
+
+/* Always include 'config.h' first in case the user wants to turn on
+ or override something. */
+#include <linux/config.h>
+
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
+/*
+ Sources:
+
+ This driver wouldn't have been written without the availability
+ of the Crynwr's Lan595 driver source code. It helps me to
+ familiarize with the 82595 chipset while waiting for the Intel
+ documentation. I also learned how to detect the 82595 using
+ the packet driver's technique.
+
+ This driver is written by cutting and pasting the skeleton.c driver
+ provided by Donald Becker. I also borrowed the EEPROM routine from
+ Donald Becker's 82586 driver.
+
+ Datasheet for the Intel 82595. It provides just enough info that
+ the casual reader might think that it documents the i82595.
+
+ The User Manual for the 82595. It provides a lot of the missing
+ information.
+
+*/
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/errno.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+extern struct device *init_etherdev(struct device *dev, int sizeof_private,
+ unsigned long *mem_startp);
+
+/* First, a few definitions that the brave might change. */
+/* A zero-terminated list of I/O addresses to be probed. */
+static unsigned int eepro_portlist[] =
+ { 0x200, 0x240, 0x280, 0x2C0, 0x300, 0x320, 0x340, 0x360, 0};
+
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifndef NET_DEBUG
+#define NET_DEBUG 2
+#endif
+static unsigned int net_debug = NET_DEBUG;
+
+/* The number of low I/O ports used by the ethercard. */
+#define EEPRO_IO_EXTENT 16
+
+/* Information that need to be kept for each board. */
+struct eepro_local {
+ struct enet_statistics stats;
+ unsigned rx_start;
+ unsigned tx_start; /* start of the transmit chain */
+ int tx_last; /* pointer to last packet in the transmit chain */
+ unsigned tx_end; /* end of the transmit chain (plus 1) */
+ int eepro; /* a flag, TRUE=1 for the EtherExpress Pro/10,
+ FALSE = 0 for other 82595-based lan cards. */
+};
+
+/* The station (ethernet) address prefix, used for IDing the board. */
+#define SA_ADDR0 0x00
+#define SA_ADDR1 0xaa
+#define SA_ADDR2 0x00
+
+/* Index to functions, as function prototypes. */
+
+extern int eepro_probe(struct device *dev);
+
+static int eepro_probe1(struct device *dev, short ioaddr);
+static int eepro_open(struct device *dev);
+static int eepro_send_packet(struct sk_buff *skb, struct device *dev);
+static void eepro_interrupt(int irq, struct pt_regs *regs);
+static void eepro_rx(struct device *dev);
+static void eepro_transmit_interrupt(struct device *dev);
+static int eepro_close(struct device *dev);
+static struct enet_statistics *eepro_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
+
+static int read_eeprom(int ioaddr, int location);
+static void hardware_send_packet(struct device *dev, void *buf, short length);
+static int eepro_grab_irq(struct device *dev);
+
+/*
+ Details of the i82595.
+
+You will need either the datasheet or the user manual to understand what
+is going on here. The 82595 is very different from the 82586, 82593.
+
+The receive algorithm in eepro_rx() is just an implementation of the
+RCV ring structure that the Intel 82595 imposes at the hardware level.
+The receive buffer is set at 24K, and the transmit buffer is 8K. I
+am assuming that the total buffer memory is 32K, which is true for the
+Intel EtherExpress Pro/10. If it is less than that on a generic card,
+the driver will be broken.
+
+The transmit algorithm in the hardware_send_packet() is similar to the
+one in the eepro_rx(). The transmit buffer is a ring linked list.
+I just queue the next available packet to the end of the list. In my
+system, the 82595 is so fast that the list seems to always contain a
+single packet. In other systems with faster computers and more congested
+network traffics, the ring linked list should improve performance by
+allowing up to 8K worth of packets to be queued.
+
+*/
+#define RAM_SIZE 0x8000
+#define RCV_HEADER 8
+#define RCV_RAM 0x6000 /* 24KB for RCV buffer */
+#define RCV_LOWER_LIMIT 0x00 /* 0x0000 */
+#define RCV_UPPER_LIMIT ((RCV_RAM - 2) >> 8) /* 0x5ffe */
+#define XMT_RAM (RAM_SIZE - RCV_RAM) /* 8KB for XMT buffer */
+#define XMT_LOWER_LIMIT (RCV_RAM >> 8) /* 0x6000 */
+#define XMT_UPPER_LIMIT ((RAM_SIZE - 2) >> 8) /* 0x7ffe */
+#define XMT_HEADER 8
+
+#define RCV_DONE 0x0008
+#define RX_OK 0x2000
+#define RX_ERROR 0x0d81
+
+#define TX_DONE_BIT 0x0080
+#define CHAIN_BIT 0x8000
+#define XMT_STATUS 0x02
+#define XMT_CHAIN 0x04
+#define XMT_COUNT 0x06
+
+#define BANK0_SELECT 0x00
+#define BANK1_SELECT 0x40
+#define BANK2_SELECT 0x80
+
+/* Bank 0 registers */
+#define COMMAND_REG 0x00 /* Register 0 */
+#define MC_SETUP 0x03
+#define XMT_CMD 0x04
+#define DIAGNOSE_CMD 0x07
+#define RCV_ENABLE_CMD 0x08
+#define RCV_DISABLE_CMD 0x0a
+#define STOP_RCV_CMD 0x0b
+#define RESET_CMD 0x0e
+#define POWER_DOWN_CMD 0x18
+#define RESUME_XMT_CMD 0x1c
+#define SEL_RESET_CMD 0x1e
+#define STATUS_REG 0x01 /* Register 1 */
+#define RX_INT 0x02
+#define TX_INT 0x04
+#define EXEC_STATUS 0x30
+#define ID_REG 0x02 /* Register 2 */
+#define R_ROBIN_BITS 0xc0 /* round robin counter */
+#define ID_REG_MASK 0x2c
+#define ID_REG_SIG 0x24
+#define AUTO_ENABLE 0x10
+#define INT_MASK_REG 0x03 /* Register 3 */
+#define RX_STOP_MASK 0x01
+#define RX_MASK 0x02
+#define TX_MASK 0x04
+#define EXEC_MASK 0x08
+#define ALL_MASK 0x0f
+#define RCV_BAR 0x04 /* The following are word (16-bit) registers */
+#define RCV_STOP 0x06
+#define XMT_BAR 0x0a
+#define HOST_ADDRESS_REG 0x0c
+#define IO_PORT 0x0e
+
+/* Bank 1 registers */
+#define REG1 0x01
+#define WORD_WIDTH 0x02
+#define INT_ENABLE 0x80
+#define INT_NO_REG 0x02
+#define RCV_LOWER_LIMIT_REG 0x08
+#define RCV_UPPER_LIMIT_REG 0x09
+#define XMT_LOWER_LIMIT_REG 0x0a
+#define XMT_UPPER_LIMIT_REG 0x0b
+
+/* Bank 2 registers */
+#define XMT_Chain_Int 0x20 /* Interrupt at the end of the transmit chain */
+#define XMT_Chain_ErrStop 0x40 /* Interrupt at the end of the chain even if there are errors */
+#define RCV_Discard_BadFrame 0x80 /* Throw bad frames away, and continue to receive others */
+#define REG2 0x02
+#define PRMSC_Mode 0x01
+#define Multi_IA 0x20
+#define REG3 0x03
+#define TPE_BIT 0x04
+#define BNC_BIT 0x20
+
+#define I_ADD_REG0 0x04
+#define I_ADD_REG1 0x05
+#define I_ADD_REG2 0x06
+#define I_ADD_REG3 0x07
+#define I_ADD_REG4 0x08
+#define I_ADD_REG5 0x09
+
+#define EEPROM_REG 0x0a
+#define EESK 0x01
+#define EECS 0x02
+#define EEDI 0x04
+#define EEDO 0x08
+
+
+/* Check for a network adaptor of this type, and return '0' iff one exists.
+ If dev->base_addr == 0, probe all likely locations.
+ If dev->base_addr == 1, always return failure.
+ If dev->base_addr == 2, allocate space for the device and return success
+ (detachable devices only).
+ */
+#ifdef HAVE_DEVLIST
+/* Support for a alternate probe manager, which will eliminate the
+ boilerplate below. */
+struct netdev_entry netcard_drv =
+{"eepro", eepro_probe1, EEPRO_IO_EXTENT, eepro_portlist};
+#else
+int
+eepro_probe(struct device *dev)
+{
+ int i;
+ int base_addr = dev ? dev->base_addr : 0;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return eepro_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return ENXIO;
+
+ for (i = 0; eepro_portlist[i]; i++) {
+ int ioaddr = eepro_portlist[i];
+ if (check_region(ioaddr, EEPRO_IO_EXTENT))
+ continue;
+ if (eepro_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+#endif
+
+/* This is the real probe routine. Linux has a history of friendly device
+ probes on the ISA bus. A good device probes avoids doing writes, and
+ verifies that the correct device exists and functions. */
+
+int eepro_probe1(struct device *dev, short ioaddr)
+{
+ unsigned short station_addr[6], id, counter;
+ int i;
+ int eepro; /* a flag, TRUE=1 for the EtherExpress Pro/10,
+ FALSE = 0 for other 82595-based lan cards. */
+ char *ifmap[] = {"AUI", "10Base2", "10BaseT"};
+ enum iftype { AUI=0, BNC=1, TPE=2 };
+
+ /* Now, we are going to check for the signature of the
+ ID_REG (register 2 of bank 0) */
+
+ if (((id=inb(ioaddr + ID_REG)) & ID_REG_MASK) == ID_REG_SIG) {
+
+ /* We seem to have the 82595 signature, let's
+ play with its counter (last 2 bits of
+ register 2 of bank 0) to be sure. */
+
+ counter = (id & R_ROBIN_BITS);
+ if (((id=inb(ioaddr+ID_REG)) & R_ROBIN_BITS) ==
+ (counter + 0x40)) {
+
+ /* Yes, the 82595 has been found */
+
+ /* Now, get the ethernet hardware address from
+ the EEPROM */
+
+ station_addr[0] = read_eeprom(ioaddr, 2);
+ station_addr[1] = read_eeprom(ioaddr, 3);
+ station_addr[2] = read_eeprom(ioaddr, 4);
+
+ /* Check the station address for the manufacturer's code */
+
+ if (station_addr[2] != 0x00aa || (station_addr[1] & 0xff00) != 0x0000) {
+ eepro = 0;
+ printk("%s: Intel 82595-based lan card at %#x,",
+ dev->name, ioaddr);
+ }
+ else {
+ eepro = 1;
+ printk("%s: Intel EtherExpress Pro/10 at %#x,",
+ dev->name, ioaddr);
+ }
+
+ /* Fill in the 'dev' fields. */
+ dev->base_addr = ioaddr;
+
+ for (i=0; i < 6; i++) {
+ dev->dev_addr[i] = ((unsigned char *) station_addr)[5-i];
+ printk("%c%02x", i ? ':' : ' ', dev->dev_addr[i]);
+ }
+
+ outb(BANK2_SELECT, ioaddr); /* be CAREFULL, BANK 2 now */
+ id = inb(ioaddr + REG3);
+ if (id & TPE_BIT)
+ dev->if_port = TPE;
+ else dev->if_port = BNC;
+
+ if (dev->irq < 2 && eepro) {
+ i = read_eeprom(ioaddr, 1);
+ switch (i & 0x07) {
+ case 0: dev->irq = 9; break;
+ case 1: dev->irq = 3; break;
+ case 2: dev->irq = 5; break;
+ case 3: dev->irq = 10; break;
+ case 4: dev->irq = 11; break;
+ default: /* should never get here !!!!! */
+ printk(" illegal interrupt vector stored in EEPROM.\n");
+ return ENODEV;
+ }
+ }
+ else if (dev->irq == 2)
+ dev->irq = 9;
+
+ if (dev->irq > 2) {
+ printk(", IRQ %d, %s.\n", dev->irq,
+ ifmap[dev->if_port]);
+ if (request_irq(dev->irq, &eepro_interrupt, 0, "eepro")) {
+ printk("%s: unable to get IRQ %d.\n", dev->name, dev->irq);
+ return -EAGAIN;
+ }
+ }
+ else printk(", %s.\n", ifmap[dev->if_port]);
+
+ if ((dev->mem_start & 0xf) > 0)
+ net_debug = dev->mem_start & 7;
+
+ if (net_debug > 3) {
+ i = read_eeprom(ioaddr, 5);
+ if (i & 0x2000) /* bit 13 of EEPROM word 5 */
+ printk("%s: Concurrent Processing is enabled but not used!\n",
+ dev->name);
+ }
+
+ if (net_debug)
+ printk(version);
+
+ /* Grab the region so we can find another board if autoIRQ fails. */
+ request_region(ioaddr, EEPRO_IO_EXTENT,"eepro");
+
+ /* Initialize the device structure */
+ if (dev->priv == NULL)
+ dev->priv = kmalloc(sizeof(struct eepro_local), GFP_KERNEL);
+ memset(dev->priv, 0, sizeof(struct eepro_local));
+
+ dev->open = eepro_open;
+ dev->stop = eepro_close;
+ dev->hard_start_xmit = eepro_send_packet;
+ dev->get_stats = eepro_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+
+ /* Fill in the fields of the device structure with
+ ethernet generic values */
+
+ ether_setup(dev);
+
+ outb(RESET_CMD, ioaddr); /* RESET the 82595 */
+
+ return 0;
+ }
+ else return ENODEV;
+ }
+ else if (net_debug > 3)
+ printk ("EtherExpress Pro probed failed!\n");
+ return ENODEV;
+}
+
+/* Open/initialize the board. This is called (in the current kernel)
+ sometime after booting when the 'ifconfig' program is run.
+
+ This routine should set everything up anew at each open, even
+ registers that "should" only need to be set once at boot, so that
+ there is non-reboot way to recover if something goes wrong.
+ */
+
+static char irqrmap[] = {-1,-1,0,1,-1,2,-1,-1,-1,0,3,4,-1,-1,-1,-1};
+static int eepro_grab_irq(struct device *dev)
+{
+ int irqlist[] = { 5, 9, 10, 11, 4, 3, 0};
+ int *irqp = irqlist, temp_reg, ioaddr = dev->base_addr;
+
+ outb(BANK1_SELECT, ioaddr); /* be CAREFULL, BANK 1 now */
+
+ /* Enable the interrupt line. */
+ temp_reg = inb(ioaddr + REG1);
+ outb(temp_reg | INT_ENABLE, ioaddr + REG1);
+
+ outb(BANK0_SELECT, ioaddr); /* be CAREFULL, BANK 0 now */
+
+ /* clear all interrupts */
+ outb(ALL_MASK, ioaddr + STATUS_REG);
+ /* Let EXEC event to interrupt */
+ outb(ALL_MASK & ~(EXEC_MASK), ioaddr + INT_MASK_REG);
+
+ do {
+ outb(BANK1_SELECT, ioaddr); /* be CAREFULL, BANK 1 now */
+
+ temp_reg = inb(ioaddr + INT_NO_REG);
+ outb((temp_reg & 0xf8) | irqrmap[*irqp], ioaddr + INT_NO_REG);
+
+ outb(BANK0_SELECT, ioaddr); /* Switch back to Bank 0 */
+
+ if (request_irq (*irqp, NULL, 0, "bogus") != EBUSY) {
+ /* Twinkle the interrupt, and check if it's seen */
+ autoirq_setup(0);
+
+ outb(DIAGNOSE_CMD, ioaddr); /* RESET the 82595 */
+
+ if (*irqp == autoirq_report(2) && /* It's a good IRQ line */
+ (request_irq(dev->irq = *irqp, &eepro_interrupt, 0, "eepro") == 0))
+ break;
+
+ /* clear all interrupts */
+ outb(ALL_MASK, ioaddr + STATUS_REG);
+ }
+ } while (*++irqp);
+
+ outb(BANK1_SELECT, ioaddr); /* Switch back to Bank 1 */
+
+ /* Disable the physical interrupt line. */
+ temp_reg = inb(ioaddr + REG1);
+ outb(temp_reg & 0x7f, ioaddr + REG1);
+
+ outb(BANK0_SELECT, ioaddr); /* Switch back to Bank 0 */
+
+ /* Mask all the interrupts. */
+ outb(ALL_MASK, ioaddr + INT_MASK_REG);
+
+ /* clear all interrupts */
+ outb(ALL_MASK, ioaddr + STATUS_REG);
+
+ return dev->irq;
+}
+
+static int
+eepro_open(struct device *dev)
+{
+ unsigned short temp_reg;
+ int i, ioaddr = dev->base_addr;
+ struct eepro_local *lp = (struct eepro_local *)dev->priv;
+
+ if (net_debug > 3)
+ printk("eepro: entering eepro_open routine.\n");
+
+ if (dev->dev_addr[0] == SA_ADDR0 &&
+ dev->dev_addr[1] == SA_ADDR1 &&
+ dev->dev_addr[2] == SA_ADDR2)
+ lp->eepro = 1; /* Yes, an Intel EtherExpress Pro/10 */
+ else lp->eepro = 0; /* No, it is a generic 82585 lan card */
+
+ /* Get the interrupt vector for the 82595 */
+ if (dev->irq < 2 && eepro_grab_irq(dev) == 0) {
+ printk("%s: unable to get IRQ %d.\n", dev->name, dev->irq);
+ return -EAGAIN;
+ }
+
+ if (irq2dev_map[dev->irq] != 0
+ || (irq2dev_map[dev->irq] = dev) == 0)
+ return -EAGAIN;
+
+ /* Initialize the 82595. */
+
+ outb(BANK2_SELECT, ioaddr); /* be CAREFULL, BANK 2 now */
+ temp_reg = inb(ioaddr + EEPROM_REG);
+ if (temp_reg & 0x10) /* Check the TurnOff Enable bit */
+ outb(temp_reg & 0xef, ioaddr + EEPROM_REG);
+ for (i=0; i < 6; i++)
+ outb(dev->dev_addr[i] , ioaddr + I_ADD_REG0 + i);
+
+ temp_reg = inb(ioaddr + REG1); /* Setup Transmit Chaining */
+ outb(temp_reg | XMT_Chain_Int | XMT_Chain_ErrStop /* and discard bad RCV frames */
+ | RCV_Discard_BadFrame, ioaddr + REG1);
+
+ temp_reg = inb(ioaddr + REG2); /* Match broadcast */
+ outb(temp_reg | 0x14, ioaddr + REG2);
+
+ temp_reg = inb(ioaddr + REG3);
+ outb(temp_reg & 0x3f, ioaddr + REG3); /* clear test mode */
+
+ /* Set the receiving mode */
+ outb(BANK1_SELECT, ioaddr); /* be CAREFULL, BANK 1 now */
+
+ temp_reg = inb(ioaddr + INT_NO_REG);
+ outb((temp_reg & 0xf8) | irqrmap[dev->irq], ioaddr + INT_NO_REG);
+
+ /* Initialize the RCV and XMT upper and lower limits */
+ outb(RCV_LOWER_LIMIT, ioaddr + RCV_LOWER_LIMIT_REG);
+ outb(RCV_UPPER_LIMIT, ioaddr + RCV_UPPER_LIMIT_REG);
+ outb(XMT_LOWER_LIMIT, ioaddr + XMT_LOWER_LIMIT_REG);
+ outb(XMT_UPPER_LIMIT, ioaddr + XMT_UPPER_LIMIT_REG);
+
+ /* Enable the interrupt line. */
+ temp_reg = inb(ioaddr + REG1);
+ outb(temp_reg | INT_ENABLE, ioaddr + REG1);
+
+ outb(BANK0_SELECT, ioaddr); /* Switch back to Bank 0 */
+
+ /* Let RX and TX events to interrupt */
+ outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG);
+ /* clear all interrupts */
+ outb(ALL_MASK, ioaddr + STATUS_REG);
+
+ /* Initialize RCV */
+ outw(RCV_LOWER_LIMIT << 8, ioaddr + RCV_BAR);
+ lp->rx_start = (RCV_LOWER_LIMIT << 8) ;
+ outw((RCV_UPPER_LIMIT << 8) | 0xfe, ioaddr + RCV_STOP);
+
+ /* Initialize XMT */
+ outw(XMT_LOWER_LIMIT << 8, ioaddr + XMT_BAR);
+
+ outb(SEL_RESET_CMD, ioaddr);
+ /* We are supposed to wait for 2 us after a SEL_RESET */
+ SLOW_DOWN_IO;
+ SLOW_DOWN_IO;
+
+ lp->tx_start = lp->tx_end = XMT_LOWER_LIMIT << 8; /* or = RCV_RAM */
+ lp->tx_last = 0;
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+ if (net_debug > 3)
+ printk("eepro: exiting eepro_open routine.\n");
+
+ outb(RCV_ENABLE_CMD, ioaddr);
+
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
+ return 0;
+}
+
+static int
+eepro_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ struct eepro_local *lp = (struct eepro_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+ if (net_debug > 5)
+ printk("eepro: entering eepro_send_packet routine.\n");
+
+ if (dev->tbusy) {
+ /* If we get here, some higher level has decided we are broken.
+ There should really be a "kick me" function call instead. */
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 5)
+ return 1;
+ if (net_debug > 1)
+ printk("%s: transmit timed out, %s?\n", dev->name,
+ "network cable problem");
+ lp->stats.tx_errors++;
+ /* Try to restart the adaptor. */
+ outb(SEL_RESET_CMD, ioaddr);
+ /* We are supposed to wait for 2 us after a SEL_RESET */
+ SLOW_DOWN_IO;
+ SLOW_DOWN_IO;
+
+ /* Do I also need to flush the transmit buffers here? YES? */
+ lp->tx_start = lp->tx_end = RCV_RAM;
+ lp->tx_last = 0;
+
+ dev->tbusy=0;
+ dev->trans_start = jiffies;
+
+ outb(RCV_ENABLE_CMD, ioaddr);
+
+ }
+
+ /* If some higher layer thinks we've missed an tx-done interrupt
+ we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+ itself. */
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ /* Block a timer-based transmit from overlapping. */
+ if (set_bit(0, (void*)&dev->tbusy) != 0)
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ else {
+ short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ unsigned char *buf = skb->data;
+
+ hardware_send_packet(dev, buf, length);
+ dev->trans_start = jiffies;
+ }
+
+ dev_kfree_skb (skb, FREE_WRITE);
+
+ /* You might need to clean up and record Tx statistics here. */
+ /* lp->stats.tx_aborted_errors++; */
+
+ if (net_debug > 5)
+ printk("eepro: exiting eepro_send_packet routine.\n");
+
+ return 0;
+}
+
+
+/* The typical workload of the driver:
+ Handle the network interface interrupts. */
+static void
+eepro_interrupt(int irq, struct pt_regs * regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ int ioaddr, status, boguscount = 0;
+
+ if (net_debug > 5)
+ printk("eepro: entering eepro_interrupt routine.\n");
+
+ if (dev == NULL) {
+ printk ("eepro_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+ dev->interrupt = 1;
+
+ ioaddr = dev->base_addr;
+
+ do {
+ status = inb(ioaddr + STATUS_REG);
+
+ if (status & RX_INT) {
+ if (net_debug > 4)
+ printk("eepro: packet received interrupt.\n");
+
+ /* Acknowledge the RX_INT */
+ outb(RX_INT, ioaddr + STATUS_REG);
+
+ /* Get the received packets */
+ eepro_rx(dev);
+ }
+ else if (status & TX_INT) {
+ if (net_debug > 4)
+ printk("eepro: packet transmit interrupt.\n");
+
+ /* Acknowledge the TX_INT */
+ outb(TX_INT, ioaddr + STATUS_REG);
+
+ /* Process the status of transmitted packets */
+ eepro_transmit_interrupt(dev);
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+ } while ((++boguscount < 10) && (status & 0x06));
+
+ dev->interrupt = 0;
+ if (net_debug > 5)
+ printk("eepro: exiting eepro_interrupt routine.\n");
+
+ return;
+}
+
+static int
+eepro_close(struct device *dev)
+{
+ struct eepro_local *lp = (struct eepro_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+ short temp_reg;
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ outb(BANK1_SELECT, ioaddr); /* Switch back to Bank 1 */
+
+ /* Disable the physical interrupt line. */
+ temp_reg = inb(ioaddr + REG1);
+ outb(temp_reg & 0x7f, ioaddr + REG1);
+
+ outb(BANK0_SELECT, ioaddr); /* Switch back to Bank 0 */
+
+ /* Flush the Tx and disable Rx. */
+ outb(STOP_RCV_CMD, ioaddr);
+ lp->tx_start = lp->tx_end = RCV_RAM ;
+ lp->tx_last = 0;
+
+ /* Mask all the interrupts. */
+ outb(ALL_MASK, ioaddr + INT_MASK_REG);
+
+ /* clear all interrupts */
+ outb(ALL_MASK, ioaddr + STATUS_REG);
+
+ /* Reset the 82595 */
+ outb(RESET_CMD, ioaddr);
+
+ /* release the interrupt */
+ free_irq(dev->irq);
+
+ irq2dev_map[dev->irq] = 0;
+
+ /* release the ioport-region */
+ release_region(ioaddr, 16);
+
+ /* Update the statistics here. What statistics? */
+
+ /* We are supposed to wait for 200 us after a RESET */
+ SLOW_DOWN_IO;
+ SLOW_DOWN_IO; /* May not be enough? */
+
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
+ return 0;
+}
+
+/* Get the current statistics. This may be called with the card open or
+ closed. */
+static struct enet_statistics *
+eepro_get_stats(struct device *dev)
+{
+ struct eepro_local *lp = (struct eepro_local *)dev->priv;
+
+ return &lp->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+ num_addrs == -1 Promiscuous mode, receive all packets
+ num_addrs == 0 Normal mode, clear multicast list
+ num_addrs > 0 Multicast mode, receive normal and MC packets, and do
+ best-effort filtering.
+ */
+static void
+set_multicast_list(struct device *dev, int num_addrs, void *addrs)
+{
+ struct eepro_local *lp = (struct eepro_local *)dev->priv;
+ short ioaddr = dev->base_addr;
+ unsigned short mode;
+
+ if (num_addrs < -1 || num_addrs > 63) {
+ outb(BANK2_SELECT, ioaddr); /* be CAREFULL, BANK 2 now */
+ mode = inb(ioaddr + REG2);
+ outb(mode | PRMSC_Mode, ioaddr + REG2);
+ mode = inb(ioaddr + REG3);
+ outb(mode, ioaddr + REG3); /* writing reg. 3 to complete the update */
+ outb(BANK0_SELECT, ioaddr); /* Return to BANK 0 now */
+ printk("%s: promiscuous mode enabled.\n", dev->name);
+ }
+ else if (num_addrs == 0) {
+ outb(BANK2_SELECT, ioaddr); /* be CAREFULL, BANK 2 now */
+ mode = inb(ioaddr + REG2);
+ outb(mode & 0xd6, ioaddr + REG2); /* Turn off Multi-IA and PRMSC_Mode bits */
+ mode = inb(ioaddr + REG3);
+ outb(mode, ioaddr + REG3); /* writing reg. 3 to complete the update */
+ outb(BANK0_SELECT, ioaddr); /* Return to BANK 0 now */
+ }
+ else {
+ unsigned short status, *eaddrs = addrs;
+ int i, boguscount = 0;
+
+ /* Disable RX and TX interrupts. Neccessary to avoid
+ corruption of the HOST_ADDRESS_REG by interrupt
+ service routines. */
+ outb(ALL_MASK, ioaddr + INT_MASK_REG);
+
+ outb(BANK2_SELECT, ioaddr); /* be CAREFULL, BANK 2 now */
+ mode = inb(ioaddr + REG2);
+ outb(mode | Multi_IA, ioaddr + REG2);
+ mode = inb(ioaddr + REG3);
+ outb(mode, ioaddr + REG3); /* writing reg. 3 to complete the update */
+ outb(BANK0_SELECT, ioaddr); /* Return to BANK 0 now */
+ outw(lp->tx_end, ioaddr + HOST_ADDRESS_REG);
+ outw(MC_SETUP, ioaddr + IO_PORT);
+ outw(0, ioaddr + IO_PORT);
+ outw(0, ioaddr + IO_PORT);
+ outw(6*(num_addrs + 1), ioaddr + IO_PORT);
+ for (i = 0; i < num_addrs; i++) {
+ outw(*eaddrs++, ioaddr + IO_PORT);
+ outw(*eaddrs++, ioaddr + IO_PORT);
+ outw(*eaddrs++, ioaddr + IO_PORT);
+ }
+ eaddrs = (unsigned short *) dev->dev_addr;
+ outw(eaddrs[0], ioaddr + IO_PORT);
+ outw(eaddrs[1], ioaddr + IO_PORT);
+ outw(eaddrs[2], ioaddr + IO_PORT);
+ outw(lp->tx_end, ioaddr + XMT_BAR);
+ outb(MC_SETUP, ioaddr);
+
+ /* Update the transmit queue */
+ i = lp->tx_end + XMT_HEADER + 6*(num_addrs + 1);
+ if (lp->tx_start != lp->tx_end) {
+ /* update the next address and the chain bit in the
+ last packet */
+ outw(lp->tx_last + XMT_CHAIN, ioaddr + HOST_ADDRESS_REG);
+ outw(i, ioaddr + IO_PORT);
+ outw(lp->tx_last + XMT_COUNT, ioaddr + HOST_ADDRESS_REG);
+ status = inw(ioaddr + IO_PORT);
+ outw(status | CHAIN_BIT, ioaddr + IO_PORT);
+ lp->tx_end = i ;
+ } else lp->tx_start = lp->tx_end = i ;
+
+ /* Acknowledge that the MC setup is done */
+ do { /* We should be doing this in the eepro_interrupt()! */
+ SLOW_DOWN_IO;
+ SLOW_DOWN_IO;
+ if (inb(ioaddr + STATUS_REG) & 0x08) {
+ i = inb(ioaddr);
+ outb(0x08, ioaddr + STATUS_REG);
+ if (i & 0x20) { /* command ABORTed */
+ printk("%s: multicast setup failed.\n",
+ dev->name);
+ break;
+ } else if ((i & 0x0f) == 0x03) { /* MC-Done */
+ printk("%s: set Rx mode to %d addresses.\n",
+ dev->name, num_addrs);
+ break;
+ }
+ }
+ } while (++boguscount < 100);
+
+ /* Re-enable RX and TX interrupts */
+ outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG);
+
+ }
+ outb(RCV_ENABLE_CMD, ioaddr);
+}
+
+/* The horrible routine to read a word from the serial EEPROM. */
+/* IMPORTANT - the 82595 will be set to Bank 0 after the eeprom is read */
+
+/* The delay between EEPROM clock transitions. */
+#define eeprom_delay() { int _i = 40; while (--_i > 0) { __SLOW_DOWN_IO; }}
+#define EE_READ_CMD (6 << 6)
+
+int
+read_eeprom(int ioaddr, int location)
+{
+ int i;
+ unsigned short retval = 0;
+ short ee_addr = ioaddr + EEPROM_REG;
+ int read_cmd = location | EE_READ_CMD;
+ short ctrl_val = EECS ;
+
+ outb(BANK2_SELECT, ioaddr);
+ outb(ctrl_val, ee_addr);
+
+ /* Shift the read command bits out. */
+ for (i = 8; i >= 0; i--) {
+ short outval = (read_cmd & (1 << i)) ? ctrl_val | EEDI
+ : ctrl_val;
+ outb(outval, ee_addr);
+ outb(outval | EESK, ee_addr); /* EEPROM clock tick. */
+ eeprom_delay();
+ outb(outval, ee_addr); /* Finish EEPROM a clock tick. */
+ eeprom_delay();
+ }
+ outb(ctrl_val, ee_addr);
+
+ for (i = 16; i > 0; i--) {
+ outb(ctrl_val | EESK, ee_addr); eeprom_delay();
+ retval = (retval << 1) | ((inb(ee_addr) & EEDO) ? 1 : 0);
+ outb(ctrl_val, ee_addr); eeprom_delay();
+ }
+
+ /* Terminate the EEPROM access. */
+ ctrl_val &= ~EECS;
+ outb(ctrl_val | EESK, ee_addr);
+ eeprom_delay();
+ outb(ctrl_val, ee_addr);
+ eeprom_delay();
+ outb(BANK0_SELECT, ioaddr);
+ return retval;
+}
+
+static void
+hardware_send_packet(struct device *dev, void *buf, short length)
+{
+ struct eepro_local *lp = (struct eepro_local *)dev->priv;
+ short ioaddr = dev->base_addr;
+ unsigned status, tx_available, last, end, boguscount = 10;
+
+ if (net_debug > 5)
+ printk("eepro: entering hardware_send_packet routine.\n");
+
+ while (boguscount-- > 0) {
+
+ /* determine how much of the transmit buffer space is available */
+ if (lp->tx_end > lp->tx_start)
+ tx_available = XMT_RAM - (lp->tx_end - lp->tx_start);
+ else if (lp->tx_end < lp->tx_start)
+ tx_available = lp->tx_start - lp->tx_end;
+ else tx_available = XMT_RAM;
+
+ /* Disable RX and TX interrupts. Neccessary to avoid
+ corruption of the HOST_ADDRESS_REG by interrupt
+ service routines. */
+ outb(ALL_MASK, ioaddr + INT_MASK_REG);
+
+ if (((((length + 1) >> 1) << 1) + 2*XMT_HEADER)
+ >= tx_available) /* No space available ??? */
+ continue;
+
+ last = lp->tx_end;
+ end = last + (((length + 1) >> 1) << 1) + XMT_HEADER;
+
+ if (end >= RAM_SIZE) { /* the transmit buffer is wrapped around */
+ if ((RAM_SIZE - last) <= XMT_HEADER) {
+ /* Arrrr!!!, must keep the xmt header together,
+ several days were lost to chase this one down. */
+ last = RCV_RAM;
+ end = last + (((length + 1) >> 1) << 1) + XMT_HEADER;
+ }
+ else end = RCV_RAM + (end - RAM_SIZE);
+ }
+
+ outw(last, ioaddr + HOST_ADDRESS_REG);
+ outw(XMT_CMD, ioaddr + IO_PORT);
+ outw(0, ioaddr + IO_PORT);
+ outw(end, ioaddr + IO_PORT);
+ outw(length, ioaddr + IO_PORT);
+ outsw(ioaddr + IO_PORT, buf, (length + 1) >> 1);
+
+ if (lp->tx_start != lp->tx_end) {
+ /* update the next address and the chain bit in the
+ last packet */
+ if (lp->tx_end != last) {
+ outw(lp->tx_last + XMT_CHAIN, ioaddr + HOST_ADDRESS_REG);
+ outw(last, ioaddr + IO_PORT);
+ }
+ outw(lp->tx_last + XMT_COUNT, ioaddr + HOST_ADDRESS_REG);
+ status = inw(ioaddr + IO_PORT);
+ outw(status | CHAIN_BIT, ioaddr + IO_PORT);
+ }
+
+ /* A dummy read to flush the DRAM write pipeline */
+ status = inw(ioaddr + IO_PORT);
+
+ /* Enable RX and TX interrupts */
+ outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG);
+
+ if (lp->tx_start == lp->tx_end) {
+ outw(last, ioaddr + XMT_BAR);
+ outb(XMT_CMD, ioaddr);
+ lp->tx_start = last; /* I don't like to change tx_start here */
+ }
+ else outb(RESUME_XMT_CMD, ioaddr);
+
+ lp->tx_last = last;
+ lp->tx_end = end;
+
+ if (dev->tbusy) {
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+
+ if (net_debug > 5)
+ printk("eepro: exiting hardware_send_packet routine.\n");
+ return;
+ }
+ dev->tbusy = 1;
+ if (net_debug > 5)
+ printk("eepro: exiting hardware_send_packet routine.\n");
+}
+
+static void
+eepro_rx(struct device *dev)
+{
+ struct eepro_local *lp = (struct eepro_local *)dev->priv;
+ short ioaddr = dev->base_addr;
+ short boguscount = 20;
+ short rcv_car = lp->rx_start;
+ unsigned rcv_event, rcv_status, rcv_next_frame, rcv_size;
+
+ if (net_debug > 5)
+ printk("eepro: entering eepro_rx routine.\n");
+
+ /* Set the read pointer to the start of the RCV */
+ outw(rcv_car, ioaddr + HOST_ADDRESS_REG);
+ rcv_event = inw(ioaddr + IO_PORT);
+
+ while (rcv_event == RCV_DONE) {
+ rcv_status = inw(ioaddr + IO_PORT);
+ rcv_next_frame = inw(ioaddr + IO_PORT);
+ rcv_size = inw(ioaddr + IO_PORT);
+
+ if ((rcv_status & (RX_OK | RX_ERROR)) == RX_OK) {
+ /* Malloc up new buffer. */
+ struct sk_buff *skb;
+
+ rcv_size &= 0x3fff;
+ skb = alloc_skb(rcv_size, GFP_ATOMIC);
+ if (skb == NULL) {
+ printk("%s: Memory squeeze, dropping packet.\n", dev->name);
+ lp->stats.rx_dropped++;
+ break;
+ }
+ skb->len = rcv_size;
+ skb->dev = dev;
+
+ insw(ioaddr+IO_PORT, skb->data, (rcv_size + 1) >> 1);
+
+ skb->protocol = eth_type_trans(skb,dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ }
+ else { /* Not sure will ever reach here,
+ I set the 595 to discard bad received frames */
+ lp->stats.rx_errors++;
+ if (rcv_status & 0x0100)
+ lp->stats.rx_over_errors++;
+ else if (rcv_status & 0x0400)
+ lp->stats.rx_frame_errors++;
+ else if (rcv_status & 0x0800)
+ lp->stats.rx_crc_errors++;
+ printk("%s: event = %#x, status = %#x, next = %#x, size = %#x\n",
+ dev->name, rcv_event, rcv_status, rcv_next_frame, rcv_size);
+ }
+ if (rcv_status & 0x1000)
+ lp->stats.rx_length_errors++;
+ if (--boguscount == 0)
+ break;
+
+ rcv_car = lp->rx_start + RCV_HEADER + rcv_size;
+ lp->rx_start = rcv_next_frame;
+ outw(rcv_next_frame, ioaddr + HOST_ADDRESS_REG);
+ rcv_event = inw(ioaddr + IO_PORT);
+
+ }
+ if (rcv_car == 0)
+ rcv_car = (RCV_UPPER_LIMIT << 8) | 0xff;
+ outw(rcv_car - 1, ioaddr + RCV_STOP);
+
+ if (net_debug > 5)
+ printk("eepro: exiting eepro_rx routine.\n");
+}
+
+static void
+eepro_transmit_interrupt(struct device *dev)
+{
+ struct eepro_local *lp = (struct eepro_local *)dev->priv;
+ short ioaddr = dev->base_addr;
+ short boguscount = 10;
+ short xmt_status;
+
+ while (lp->tx_start != lp->tx_end) {
+
+ outw(lp->tx_start, ioaddr + HOST_ADDRESS_REG);
+ xmt_status = inw(ioaddr+IO_PORT);
+ if ((xmt_status & TX_DONE_BIT) == 0) break;
+ xmt_status = inw(ioaddr+IO_PORT);
+ lp->tx_start = inw(ioaddr+IO_PORT);
+
+ if (dev->tbusy) {
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+
+ if (xmt_status & 0x2000)
+ lp->stats.tx_packets++;
+ else {
+ lp->stats.tx_errors++;
+ if (xmt_status & 0x0400)
+ lp->stats.tx_carrier_errors++;
+ printk("%s: XMT status = %#x\n",
+ dev->name, xmt_status);
+ }
+ if (xmt_status & 0x000f)
+ lp->stats.collisions += (xmt_status & 0x000f);
+ if ((xmt_status & 0x0040) == 0x0)
+ lp->stats.tx_heartbeat_errors++;
+
+ if (--boguscount == 0)
+ break;
+ }
+}
+
+#ifdef MODULE
+char kernel_version[] = UTS_RELEASE;
+static struct device dev_eepro = {
+ " " /*"eepro"*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, eepro_probe };
+
+int
+init_module(void)
+{
+ if (register_netdev(&dev_eepro) != 0)
+ return -EIO;
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ if (MOD_IN_USE)
+ printk("eepro: device busy, remove delayed\n");
+ else
+ {
+ unregister_netdev(&dev_eepro);
+ kfree_s(dev_eepro.priv,sizeof(struct eepro_local));
+ dev_eepro.priv=NULL;
+ }
+}
+#endif /* MODULE */
if ( slave->dev != 0)
{
- if ( slave->dev->flags & IFF_UP == IFF_UP )
+ if ((slave->dev->flags & IFF_UP) == IFF_UP )
{
slave_load = (ULONG_MAX - (ULONG_MAX / 2)) -
(priority_Bps) + bytes_queued * 8;
{
if (slave != 0)
{
- if ( slave->dev->flags & IFF_UP == IFF_UP )
+ if ((slave->dev->flags & IFF_UP) == IFF_UP )
{
slave->bytes_queued -= slave->priority_Bps;
printk("%s: ne_reset_8390() did not complete.\n", dev->name);
break;
}
- outb_p(ENISR_RESET, NE_BASE + NE_RESET); /* Ack intr. */
+ outb_p(ENISR_RESET, NE_BASE + EN0_ISR); /* Ack intr. */
}
/* Block input and output, similar to the Crynwr packet driver. If you
return -EOVERFLOW; /* ZZZ; HACK! */
} else {
/* have the space: copy the packet, faking the first two bytes */
- put_fs_byte (PPP_ADDRESS, buf++);
- put_fs_byte (PPP_CONTROL, buf++);
+ put_user (PPP_ADDRESS, buf++);
+ put_user (PPP_CONTROL, buf++);
i = len;
while (i-- > 0) {
GETC (c);
- put_fs_byte (c, buf++);
+ put_user (c, buf++);
}
}
ppp->fcs = PPP_FCS_INIT;
i = nr;
while (i-- > 0)
- ppp_stuff_char(ppp,get_fs_byte(buf++));
+ ppp_stuff_char(ppp,get_user(buf++));
ppp_add_fcs(ppp); /* concatenate FCS at end */
case PPPIOCSMRU:
error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
if (error == 0) {
- temp_i = (int) get_fs_long (l);
+ temp_i = get_user ((int *) l);
PRINTKN (3,(KERN_INFO "ppp_ioctl: set mru to %d\n", temp_i));
if (ppp->mru != temp_i)
ppp_changedmtu (ppp, ppp->dev->mtu, temp_i);
#ifndef CHECK_CHARACTERS /* Don't generate errors if we don't check chars. */
temp_i |= SC_RCV_B7_1 | SC_RCV_B7_0 | SC_RCV_ODDP | SC_RCV_EVNP;
#endif
- put_fs_long ((long) temp_i, l);
+ put_user (temp_i, (int *) l);
PRINTKN (3,(KERN_DEBUG "ppp_ioctl: get flags: addr %lx flags %x\n",
l,
temp_i));
case PPPIOCSFLAGS:
error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
if (error == 0) {
- temp_i = (int) get_fs_long (l);
+ temp_i = get_user ((int *) l);
ppp->flags ^= ((ppp->flags ^ temp_i) & SC_MASK);
PRINTKN (3,(KERN_INFO "ppp_ioctl: set flags to %x\n", temp_i));
}
case PPPIOCGASYNCMAP:
error = verify_area (VERIFY_WRITE, (void *) l, sizeof (temp_i));
if (error == 0) {
- put_fs_long (ppp->xmit_async_map[0], l);
+ put_user (ppp->xmit_async_map[0], (int *) l);
PRINTKN (3,(KERN_INFO "ppp_ioctl: get asyncmap: addr %lx asyncmap %lx\n",
l, ppp->xmit_async_map[0]));
}
case PPPIOCSASYNCMAP:
error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
if (error == 0) {
- ppp->xmit_async_map[0] = get_fs_long (l);
+ ppp->xmit_async_map[0] = get_user ((int *) l);
bset (ppp->xmit_async_map, PPP_FLAG);
bset (ppp->xmit_async_map, PPP_ESC);
PRINTKN (3,(KERN_INFO "ppp_ioctl: set xmit asyncmap %lx\n",
case PPPIOCRASYNCMAP:
error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
if (error == 0) {
- ppp->recv_async_map = get_fs_long (l);
+ ppp->recv_async_map = get_user ((int *) l);
PRINTKN (3,(KERN_INFO "ppp_ioctl: set recv asyncmap %lx\n",
ppp->recv_async_map));
}
case PPPIOCGUNIT:
error = verify_area (VERIFY_WRITE, (void *) l, sizeof (temp_i));
if (error == 0) {
- put_fs_long (ppp->dev->base_addr, l);
+ put_user (ppp->dev->base_addr, (int *) l);
PRINTKN (3,(KERN_INFO "ppp_ioctl: get unit: %ld", ppp->dev->base_addr));
}
break;
case PPPIOCSINPSIG:
error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
if (error == 0) {
- ppp->inp_sig = (int) get_fs_long (l);
+ ppp->inp_sig = get_user ((int *) l);
ppp->inp_sig_pid = current->pid;
PRINTKN (3,(KERN_INFO "ppp_ioctl: set input signal %d\n", ppp->inp_sig));
}
case PPPIOCSDEBUG:
error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
if (error == 0) {
- ppp_debug = (int) get_fs_long (l);
+ ppp_debug = get_int ((int *) l);
ppp_debug_netpackets = (ppp_debug & 0xff00) >> 8;
ppp_debug &= 0xff;
PRINTKN (1, (KERN_INFO "ppp_ioctl: set debug level %d, netpacket %d\n",
case PPPIOCGDEBUG:
error = verify_area (VERIFY_WRITE, (void *) l, sizeof (temp_i));
if (error == 0) {
- put_fs_long ((long) (ppp_debug | (ppp_debug_netpackets << 8)), l);
+ put_user ((long) (ppp_debug | (ppp_debug_netpackets << 8)), (int *) l);
PRINTKN (3,(KERN_INFO "ppp_ioctl: get debug level %d\n",
ppp_debug | (ppp_debug_netpackets << 8)));
}
case PPPIOCSMAXCID:
error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
if (error == 0) {
- temp_i = (int) get_fs_long (l) + 1;
+ temp_i = get_user ((int *) l) + 1;
PRINTKN (3,(KERN_INFO "ppp_ioctl: set maxcid to %d\n", temp_i));
if (ppp->slcomp != NULL)
slhc_free (ppp->slcomp);
register unsigned char next_ch;
while (count-- > 0) {
- next_ch = (unsigned char) get_fs_byte (in);
+ next_ch = (unsigned char) get_user (in);
*out++ = hex[(next_ch >> 4) & 0x0F];
*out++ = hex[next_ch & 0x0F];
register unsigned char next_ch;
while (count-- > 0) {
- next_ch = (unsigned char) get_fs_byte (in);
+ next_ch = (unsigned char) get_user (in);
if (next_ch < 0x20 || next_ch > 0x7e)
*out++ = '.';
* not to be concerned (see Data sheet)
*/
- if (rmdstat & (RX_STP | RX_ENP) != (RX_STP | RX_ENP))
+ if ((rmdstat & (RX_STP | RX_ENP)) != (RX_STP | RX_ENP))
{
/* Start of a frame > 1518 Bytes ? */
return 0;
case SIOCGIFENCAP:
- err = verify_area(VERIFY_WRITE, arg, sizeof(long));
+ err = verify_area(VERIFY_WRITE, arg, sizeof(int));
if (err) {
return -err;
}
- put_fs_long(sl->mode, (long *)arg);
+ put_user(sl->mode, (int *)arg);
return 0;
case SIOCSIFENCAP:
- err = verify_area(VERIFY_READ, arg, sizeof(long));
+ err = verify_area(VERIFY_READ, arg, sizeof(int));
if (err) {
return -err;
}
- tmp = get_fs_long((long *)arg);
+ tmp = get_user((int *)arg);
#ifndef SL_INCLUDE_CSLIP
if (tmp & (SL_MODE_CSLIP|SL_MODE_ADAPTIVE)) {
return -EINVAL;
#define PERM_OPTIONS (OPTION_IO_MAPPED|OPTION_DEBUG_TEST1)
+
/*
* Define SCSI_MALLOC to use scsi_malloc instead of kmalloc. Other than
* preventing deadlock, I'm not sure why we'd want to do this.
static int NCR53c8xx_script_len;
static int NCR53c8xx_dsa_len;
static void NCR53c7x0_intr(int irq, struct pt_regs * regs);
-static int halt (struct Scsi_Host *host);
+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, unsigned long *dsa);
-static int print_insn (struct Scsi_Host *host, unsigned long *insn,
+static void print_dsa (struct Scsi_Host *host, u32 *dsa);
+static int print_insn (struct Scsi_Host *host, u32 *insn,
char *prefix, int kernel);
static void NCR53c8xx_dsa_fixup (struct NCR53c7x0_cmd *cmd);
struct pci_chip {
unsigned short pci_device_id;
int chip;
- int max_revision;
int min_revision;
+ int max_revision;
};
static struct pci_chip pci_chip_ids[] = {
} else {
overrides[commandline_current].data.pci.bus = ints[1];
overrides[commandline_current].data.pci.device = ints[2];
- overrides[commandline_current].data.pci.device = ints[3];
+ overrides[commandline_current].data.pci.function = ints[3];
overrides[commandline_current].options = (ints[0] >= 4) ?
ints[4] : 0;
}
/* Only the ISTAT register is readable when the NCR is running, so make
sure it's halted. */
- halt(host);
+ ncr_halt(host);
/*
* XXX - the NCR53c700 uses bitfielded registers for SCID, SDID, etc,
case DMODE_BL_4: i = 4; break;
case DMODE_BL_8: i = 8; break;
case DMODE_BL_16: i = 16; break;
- default: i = 0;
+ default: i = 0;
}
printk ("scsi%d : burst length %d\n", host->host_no, i);
}
hostdata->issue_queue = hostdata->running_list =
hostdata->finished_queue = NULL;
- hostdata->issue_dsa_head = NULL;
+ hostdata->issue_dsa_head = 0;
hostdata->issue_dsa_tail = NULL;
if (hostdata->init_save_regs)
*/
static int normal_init (Scsi_Host_Template *tpnt, int board, int chip,
- int base, int io_port, int irq, int dma, int pci_valid,
+ u32 base, int io_port, int irq, int dma, int pci_valid,
unsigned char pci_bus, unsigned char pci_device_fn, int options) {
struct Scsi_Host *instance;
struct NCR53c7x0_hostdata *hostdata;
printk ("scsi-ncr53c7,8xx : for better reliability and performance, please use the\n"
" PCI override instead.\n"
" Syntax : ncr53c8{10,15,20,25}=pci,<bus>,<device>,<function>\n"
- " <bus> and <device> are usually 0.\n");
+ " <bus> and <device> are usually 0.\n");
if (options & OPTION_DEBUG_PROBE_ONLY) {
printk ("scsi-ncr53c7,8xx : probe only enabled, aborting initialization\n");
hostdata = (struct NCR53c7x0_hostdata *)
instance->hostdata;
hostdata->size = size;
- hostdata->script_count = script_len / sizeof(long);
+ hostdata->script_count = script_len / sizeof(u32);
hostdata = (struct NCR53c7x0_hostdata *) instance->hostdata;
hostdata->board = board;
hostdata->chip = chip;
*/
if (base) {
- instance->base = (unsigned char *) base;
+ instance->base = (unsigned char*) (unsigned long) base;
/* Check for forced I/O mapping */
if (!(options & OPTION_IO_MAPPED)) {
options |= OPTION_MEMORY_MAPPED;
/*
- * Function : static int ncr_init(Scsi_Host_Template *tpnt, int board,
+ * Function : static int pci_init(Scsi_Host_Template *tpnt, int board,
* int chip, int bus, int device_fn, int options)
*
* Purpose : initializes a NCR53c800 family based on the PCI
*
*/
-static int ncr_init (Scsi_Host_Template *tpnt, int board, int chip,
+static int ncr_pci_init (Scsi_Host_Template *tpnt, int board, int chip,
unsigned char bus, unsigned char device_fn, int options) {
unsigned short vendor_id, device_id, command;
- unsigned long base, io_port;
+ u32 base;
+ int io_port;
unsigned char irq, revision;
int error, expected_chip;
int expected_id = -1, max_revision = -1, min_revision = -1;
(error = pcibios_read_config_byte (bus, device_fn, PCI_INTERRUPT_LINE,
&irq))) {
printk ("scsi-ncr53c7,8xx : error %s not initializing due to error reading configuration space\n"
- " perhaps you specified an incorrect PCI bus, device, or function.\n"
- , pcibios_strerror(error));
+ " perhaps you specified an incorrect PCI bus, device, or function.\n"
+ , pci_strbioserr(error));
return -1;
}
if (command & PCI_COMMAND_IO) {
if ((io_port & 3) != 1) {
- printk ("scsi-ncr53c7,8xx : disabling I/O mapping since base address 0 (0x%lx)\n"
+ printk ("scsi-ncr53c7,8xx : disabling I/O mapping since base address 0 (0x%x)\n"
" bits 0..1 indicate a non-IO mapping\n", io_port);
io_port = 0;
} else
io_port &= PCI_BASE_ADDRESS_IO_MASK;
} else {
- io_port = 0;
+ io_port = 0;
}
if (command & PCI_COMMAND_MEMORY) {
} else
base &= PCI_BASE_ADDRESS_MEM_MASK;
} else {
- base = 0;
+ base = 0;
}
if (!io_port && !base) {
printk ("scsi-ncr53c7,8xx : warning : revision of %d is less than %d.\n",
(int) revision, min_revision);
- return normal_init (tpnt, board, chip, (int) base, (int) io_port,
+ return normal_init (tpnt, board, chip, (int) base, io_port,
(int) irq, DMA_NONE, 1, bus, device_fn, options);
}
for (current_override = count = 0; current_override < OVERRIDE_LIMIT;
++current_override) {
if (overrides[current_override].pci ?
- !ncr_init (tpnt, overrides[current_override].board,
+ !ncr_pci_init (tpnt, overrides[current_override].board,
overrides[current_override].chip,
(unsigned char) overrides[current_override].data.pci.bus,
(((overrides[current_override].data.pci.device
!pcibios_find_device (PCI_VENDOR_ID_NCR,
pci_chip_ids[i].pci_device_id, pci_index, &pci_bus,
&pci_device_fn) &&
- !ncr_init (tpnt, BOARD_GENERIC, pci_chip_ids[i].chip,
+ !ncr_pci_init (tpnt, BOARD_GENERIC, pci_chip_ids[i].chip,
pci_bus, pci_device_fn, /* no options */ 0);
++count, ++pci_index);
}
host->hostdata;
unsigned char tmp;
int i, ncr_to_memory, memory_to_ncr, ncr_to_ncr;
- unsigned long base;
+ u32 base;
NCR53c7x0_local_setup(host);
sizeof(SCRIPT));
/* Fixup labels */
for (i = 0; i < PATCHES; ++i)
- hostdata->script[LABELPATCHES[i]] +=
- (unsigned long) hostdata->script;
+ 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,
- (long) &(hostdata->NCR53c7xx_msg_abort));
+ virt_to_bus(&hostdata->NCR53c7xx_msg_abort));
patch_abs_32 (hostdata->script, 0, NCR53c7xx_msg_reject,
- (long) &(hostdata->NCR53c7xx_msg_reject));
+ virt_to_bus(&hostdata->NCR53c7xx_msg_reject));
patch_abs_32 (hostdata->script, 0, NCR53c7xx_zero,
- (long) &(hostdata->NCR53c7xx_zero));
+ virt_to_bus(&hostdata->NCR53c7xx_zero));
patch_abs_32 (hostdata->script, 0, NCR53c7xx_sink,
- (long) &(hostdata->NCR53c7xx_sink));
+ virt_to_bus(&hostdata->NCR53c7xx_sink));
+
+ /* 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.
tmp &= (DMODE_800_ERL | DMODE_BL_MASK);
if (!(hostdata->options & OPTION_MEMORY_MAPPED)) {
- base = (long) host->io_port;
+ base = (u32) host->io_port;
memory_to_ncr = tmp|DMODE_800_DIOM;
ncr_to_memory = tmp|DMODE_800_SIOM;
ncr_to_ncr = tmp|DMODE_800_DIOM|DMODE_800_SIOM;
} else {
- base = (long) host->base;
+ base = virt_to_phys(host->base);
ncr_to_ncr = memory_to_ncr = ncr_to_memory = tmp;
}
patch_abs_rwri_data (hostdata->script, 0, dmode_ncr_to_memory, ncr_to_memory);
patch_abs_rwri_data (hostdata->script, 0, dmode_ncr_to_ncr, ncr_to_ncr);
- patch_abs_32 (hostdata->script, 0, issue_dsa_head,
- (long) &(hostdata->issue_dsa_head));
- patch_abs_32 (hostdata->script, 0, msg_buf, (long) &(hostdata->msg_buf));
- patch_abs_32 (hostdata->script, 0, reconnect_dsa_head,
- (long) &(hostdata->reconnect_dsa_head));
- patch_abs_32 (hostdata->script, 0, reselected_identify,
- (long) &(hostdata->reselected_identify));
- patch_abs_32 (hostdata->script, 0, reselected_tag,
- (long) &(hostdata->reselected_tag));
+ patch_abs_32 (hostdata->script, 0, issue_dsa_head,
+ virt_to_bus((void*)&hostdata->issue_dsa_head));
+ 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, reselected_identify,
+ virt_to_bus((void*)&hostdata->reselected_identify));
+ patch_abs_32 (hostdata->script, 0, reselected_tag,
+ virt_to_bus((void*)&hostdata->reselected_tag));
- patch_abs_32 (hostdata->script, 0, test_dest, (long) &(hostdata->test_dest));
- patch_abs_32 (hostdata->script, 0, test_src, (long) &(hostdata->test_source));
+ 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));
/*
*/
/*
- * XXX - for cleanness, E_* fields should be type unsigned long *
+ * XXX - for cleanness, E_* fields should be type u32 *
* and should reflect the _relocated_ addresses. Change this.
*/
hostdata->E_accept_message = Ent_accept_message;
host->host_no, A_dsa_fields_start, Ent_dsa_code_template_end -
Ent_dsa_zero);
- printk("scsi%d : NCR code relocated to 0x%lx\n", host->host_no,
- (unsigned long) hostdata->script);
+ printk("scsi%d : NCR code relocated to 0x%p\n", host->host_no,
+ hostdata->script);
}
/*
NCR53c7x0_local_declare();
struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
host->hostdata;
- unsigned long timeout, start;
+ 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 */
if (hostdata->issue_dsa_head) {
printk ("scsi%d : hostdata->issue_dsa_head corrupt before test 1\n",
host->host_no);
- hostdata->issue_dsa_head = NULL;
+ hostdata->issue_dsa_head = 0;
}
if (hostdata->options & OPTION_DEBUG_TEST1) {
hostdata->test_completed = -1;
hostdata->test_dest = 0;
hostdata->test_source = 0xdeadbeef;
- start = ((unsigned long) hostdata->script) + hostdata->E_test_1;
+ start = virt_to_bus(hostdata->script) + hostdata->E_test_1;
hostdata->state = STATE_RUNNING;
printk ("scsi%d : test 1", host->host_no);
NCR53c7x0_write32 (DSP_REG, start);
+ mb();
printk (" started\n");
sti();
- timeout = jiffies + 50; /* arbitrary */
+ timeout = jiffies + 5 * HZ / 10; /* arbitrary */
while ((hostdata->test_completed == -1) && jiffies < timeout)
barrier();
(hostdata->test_dest == 0xdeadbeef) ?
" due to lost interrupt.\n"
" Please verify that the correct IRQ is being used for your board,\n"
- " and that the motherboard IRQ jumpering matches the PCI setup on\n"
+ " and that the motherboard IRQ jumpering matches the PCI setup on\n"
" PCI systems.\n"
" If you are using a NCR53c810 board in a PCI system, you should\n"
" also verify that the board is jumpered to use PCI INTA, since\n"
" most PCI motherboards lack support for INTB, INTC, and INTD.\n"
: "");
else if (hostdata->test_completed != 1)
- printk ("scsi%d : test 1 bad interrupt value (%ld)\n", host->host_no,
+ 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",
+ " 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%lx (script at 0x%lx, start at 0x%lx)\n",
- host->host_no, (unsigned long) NCR53c7x0_read32(DSP_REG),
- (unsigned long) hostdata->script, start);
- printk ("scsi%d : DSPS = 0x%lx\n", host->host_no,
- (unsigned long) NCR53c7x0_read32(DSPS_REG));
+ printk ("scsi%d : DSP = 0x%x (script at 0x%p, start at 0x%x)\n",
+ host->host_no, 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;
}
if (hostdata->issue_dsa_head) {
printk ("scsi%d : hostdata->issue_dsa_head corrupt after test 1\n",
host->host_no);
- hostdata->issue_dsa_head = NULL;
+ hostdata->issue_dsa_head = 0;
}
if (hostdata->options & OPTION_DEBUG_TEST2) {
- unsigned long dsa[48];
+ u32 dsa[48];
unsigned char identify = IDENTIFY(0, 0);
unsigned char cmd[6];
unsigned char data[36];
cmd[4] = sizeof(data);
dsa[2] = 1;
- dsa[3] = (unsigned long) &identify;
+ dsa[3] = virt_to_bus(&identify);
dsa[4] = 6;
- dsa[5] = (unsigned long) &cmd;
+ dsa[5] = virt_to_bus(&cmd);
dsa[6] = sizeof(data);
- dsa[7] = (unsigned long) &data;
+ dsa[7] = virt_to_bus(&data);
dsa[8] = 1;
- dsa[9] = (unsigned long) &status;
+ dsa[9] = virt_to_bus(&status);
dsa[10] = 1;
- dsa[11] = (unsigned long) &msg;
+ dsa[11] = virt_to_bus(&msg);
for (i = 0; i < 3; ++i) {
- cli();
+ cli();
if (!hostdata->idle) {
printk ("scsi%d : chip not idle, aborting tests\n", host->host_no);
restore_flags(flags);
hostdata->idle = 0;
hostdata->test_running = 2;
hostdata->test_completed = -1;
- start = ((unsigned long) hostdata->script) + hostdata->E_test_2;
+ start = virt_to_bus(hostdata->script) + hostdata->E_test_2;
hostdata->state = STATE_RUNNING;
- NCR53c7x0_write32 (DSA_REG, (unsigned long) dsa);
+ NCR53c7x0_write32 (DSA_REG, virt_to_bus(dsa));
NCR53c7x0_write32 (DSP_REG, start);
+ mb();
sti();
- timeout = jiffies + 500; /* arbitrary */
+ timeout = jiffies + 5 * HZ; /* arbitrary */
while ((hostdata->test_completed == -1) && jiffies < timeout)
barrier();
NCR53c7x0_write32 (DSA_REG, 0);
+ mb();
if (hostdata->test_completed == 2) {
data[35] = 0;
if (hostdata->issue_dsa_head) {
printk ("scsi%d : hostdata->issue_dsa_head corrupt after test 2 id %d\n",
host->host_no, i);
- hostdata->issue_dsa_head = NULL;
+ hostdata->issue_dsa_head = 0;
}
}
}
memcpy (cmd->dsa, hostdata->script + (hostdata->E_dsa_code_template / 4),
hostdata->E_dsa_code_template_end - hostdata->E_dsa_code_template);
- patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(long),
- dsa_temp_jump_resume, ((unsigned long) cmd->dsa) +
+ patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
+ dsa_temp_jump_resume, virt_to_bus(cmd->dsa) +
Ent_dsa_jump_resume - Ent_dsa_zero);
- patch_abs_rwri_data (cmd->dsa, Ent_dsa_code_template / sizeof(long),
+ patch_abs_rwri_data (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
dsa_temp_lun, c->lun);
- patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(long),
- dsa_temp_dsa_next, ((unsigned long) cmd->dsa) + A_dsa_next);
- patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(long),
+ patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
+ dsa_temp_dsa_next, virt_to_bus(cmd->dsa) + A_dsa_next);
+ patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
dsa_temp_sync, hostdata->sync[c->target].select_indirect);
- patch_abs_rwri_data (cmd->dsa, Ent_dsa_code_template / sizeof(long),
+ patch_abs_rwri_data (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
dsa_temp_target, c->target);
}
struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
host->hostdata;
unsigned long flags;
- char **prev, *search;
+ volatile u32 *prev, search;
int i;
save_flags(flags);
cli();
for (i = 0; i < 2; ++i) {
- for (search = (char *) (i ? hostdata->issue_dsa_head :
- hostdata->reconnect_dsa_head), prev = (char **) (i ?
- &(hostdata->issue_dsa_head) : &(hostdata->reconnect_dsa_head));
- search && (search + hostdata->dsa_start) != (char *) cmd->dsa;
- prev = (char **) (search + hostdata->dsa_next),
+ for (search = (i ? hostdata->issue_dsa_head :
+ hostdata->reconnect_dsa_head), prev = (i ?
+ &hostdata->issue_dsa_head : &hostdata->reconnect_dsa_head);
+ search && ((char*)bus_to_virt(search) + hostdata->dsa_start) != (char *) cmd->dsa;
+ prev = (u32*) ((char*)bus_to_virt(search) + hostdata->dsa_next),
search = *prev);
if (search)
- *prev = *(char **) (search + hostdata->dsa_next);
+ *prev = *(u32*) ((char*)bus_to_virt(search) + hostdata->dsa_next);
}
if (cmd->prev)
#if 0
Scsi_Cmnd *c = cmd ? cmd->cmd : NULL;
#endif
- unsigned long *dsp;
+ u32 *dsp;
struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
host->hostdata;
unsigned long flags;
* dump the appropriate debugging information to standard
* output.
*/
+
save_flags(flags);
cli();
- dsp = (unsigned long *) NCR53c7x0_read32(DSP_REG);
+ dsp = (u32 *) bus_to_virt(NCR53c7x0_read32(DSP_REG));
for (bp = hostdata->breakpoints; bp && bp->address != dsp;
bp = bp->next);
if (!bp)
NCR53c7x0_write8 (hostdata->dmode,
NCR53c7x0_read8(hostdata->dmode)|DMODE_MAN);
+ mb();
/*
* And update the DSP register, using the size of the old
* instruction in bytes.
*/
- restore_flags(flags);
+ restore_flags(flags);
}
/*
}
/* Offset = 0, transfer period = divide SCLK by 4 */
NCR53c7x0_write8 (SXFER_REG, 0);
+ mb();
}
return 0;
}
struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
host->hostdata;
int desire, divisor, i, limit;
- unsigned long *script;
+ u32 *script;
unsigned char scntl3, sxfer;
/* Scale divisor by 10 to accommodate fractions */
hostdata->sync[target].select_indirect = (scntl3 << 24) | (target << 16) |
(sxfer << 8);
- script = (long *) hostdata->sync[target].script;
+ script = (u32*) hostdata->sync[target].script;
/* XXX - add NCR53c7x0 code to reprogram SCF bits if we want to */
if ((hostdata->chip / 100) == 8) {
Scsi_Cmnd *c = cmd ? cmd->cmd : NULL;
struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
host->hostdata;
- unsigned long dsps,*dsp; /* Argument of the INT instruction */
+ u32 dsps,*dsp; /* Argument of the INT instruction */
NCR53c7x0_local_setup(host);
dsps = NCR53c7x0_read32(DSPS_REG);
- dsp = (unsigned long *) NCR53c7x0_read32(DSP_REG);
+ dsp = bus_to_virt(NCR53c7x0_read32(DSP_REG));
if (hostdata->options & OPTION_DEBUG_INTR)
- printk ("scsi%d : DSPS = 0x%lx\n", host->host_no, dsps);
+ printk ("scsi%d : DSPS = 0x%x\n", host->host_no, dsps);
switch (dsps) {
case A_int_msg_1:
*/
case MESSAGE_REJECT:
hostdata->dsp = hostdata->script + hostdata->E_accept_message /
- sizeof(long);
+ sizeof(u32);
hostdata->dsp_changed = 1;
break;
case INITIATE_RECOVERY:
printk ("scsi%d : extended contingent allegiance not supported yet, rejecting\n",
host->host_no);
hostdata->dsp = hostdata->script + hostdata->E_reject_message /
- sizeof(long);
+ sizeof(u32);
hostdata->dsp_changed = 1;
}
return SPECIFIC_INT_NOTHING;
*/
if (cmd->flags & CMD_FLAG_SDTR) {
cmd->flags &= ~CMD_FLAG_SDTR;
- synchronous (host, c->target, (unsigned char *)
+ synchronous (host, c->target, (unsigned char *)
hostdata->msg_buf);
hostdata->dsp = hostdata->script + hostdata->E_accept_message /
- sizeof(long);
+ 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);
+ hostdata->msg_buf);
} else {
hostdata->msg_buf[4] = 0; /* 0 offset = async */
}
patch_dsa_32 (cmd->dsa, dsa_msgout_other, 0, 5);
patch_dsa_32 (cmd->dsa, dsa_msgout_other, 1,
- hostdata->msg_buf);
+ virt_to_bus((void*)hostdata->msg_buf));
hostdata->dsp = hostdata->script +
- hostdata->E_respond_message / sizeof(long);
+ hostdata->E_respond_message / sizeof(u32);
hostdata->dsp_changed = 1;
}
/* Fall through to abort */
case A_int_msg_wdtr:
hostdata->dsp = hostdata->script + hostdata->E_reject_message /
- sizeof(long);
+ sizeof(u32);
hostdata->dsp_changed = 1;
return SPECIFIC_INT_NOTHING;
case A_int_err_unexpected_phase:
printk ("scsi%d : selected by target %d\n", host->host_no,
(int) NCR53c7x0_read8(SSID_REG_800) &7);
hostdata->dsp = hostdata->script + hostdata->E_target_abort /
- sizeof(long);
+ sizeof(u32);
hostdata->dsp_changed = 1;
return SPECIFIC_INT_NOTHING;
case A_int_err_unexpected_reselect:
printk ("scsi%d : unexpected reselect by target %d\n", host->host_no,
(int) NCR53c7x0_read8(SSID_REG_800));
hostdata->dsp = hostdata->script + hostdata->E_initiator_abort /
- sizeof(long);
+ sizeof(u32);
hostdata->dsp_changed = 1;
return SPECIFIC_INT_NOTHING;
/*
*/
patch_dsa_32 (cmd->dsa, dsa_dataout, 0, hostdata->E_other_transfer);
- patch_dsa_32 (cmd->dsa, dsa_datain, 0, cmd->data_transfer_start);
+ 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] = (unsigned long) c->sense_buffer;
+ cmd->data_transfer_start[1] = virt_to_bus(c->sense_buffer);
cmd->data_transfer_start[2] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP)
<< 24) | DBC_TCI_TRUE;
* Restart command as a REQUEST SENSE.
*/
hostdata->dsp = hostdata->script + hostdata->E_select /
- sizeof(long);
+ 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 = hostdata->script + hostdata->E_schedule /
- sizeof(long);
+ sizeof(u32);
hostdata->dsp_changed = 1;
if (cmd)
abnormal_finished (cmd, DID_ERROR << 16);
hostdata->idle = 1;
hostdata->test_completed = (dsps - A_int_test_1) / 0x00010000 + 1;
if (hostdata->options & OPTION_DEBUG_INTR)
- printk("scsi%d : test%ld complete\n", host->host_no,
+ printk("scsi%d : test %d complete\n", host->host_no,
hostdata->test_completed);
return SPECIFIC_INT_NOTHING;
#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%lx scheduled\n", host->host_no,
+ printk("scsi%d : new I/O 0x%x scheduled\n", host->host_no,
NCR53c7x0_read32(DSA_REG));
}
return SPECIFIC_INT_RESTART;
#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%lx\n", host->host_no,
+ printk("scsi%d : DSA loaded with 0x%x\n", host->host_no,
NCR53c7x0_read32(DSA_REG));
}
return SPECIFIC_INT_RESTART;
#ifdef A_int_debug_head
case A_int_debug_head:
if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) {
- printk("scsi%d : issue_dsa_head now 0x%lx\n",
- host->host_no, (unsigned long) hostdata->issue_dsa_head);
+ printk("scsi%d : issue_dsa_head now 0x%x\n",
+ host->host_no, hostdata->issue_dsa_head);
}
return SPECIFIC_INT_RESTART;
#endif
default:
if ((dsps & 0xff000000) == 0x03000000) {
- printk ("scsi%d : misc debug interrupt 0x%lx\n",
+ printk ("scsi%d : misc debug interrupt 0x%x\n",
host->host_no, dsps);
return SPECIFIC_INT_RESTART;
}
"ms <addr> <size> <value> - store memory\n"
"rp <num> <size> - print register\n"
"rs <num> <size> <value> - store register\n"
-"s - single step\n"
+"s - single step\n"
"tb - begin trace \n"
"te - end trace\n";
*/
static int debugger_fn_bc (struct Scsi_Host *host, struct debugger_token *token,
- unsigned long args[]) {
+ u32 args[]) {
struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
instance->hostdata;
struct NCR53c7x0_break *bp, **prev;
static int debugger_fn_bl (struct Scsi_Host *host, struct debugger_token *token,
- unsigned long args[]) {
+ u32 args[]) {
struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
host->hostdata;
struct NCR53c7x0_break *bp;
len = strlen(buf);
if ((bp->old[0] & (DCMD_TYPE_MASK << 24)) ==
(DCMD_TYPE_MMI << 24)) {
- sprintf(buf + len, "%08x\n", * (long *) bp->addr);
+ sprintf(buf + len, "%08x\n", * (u32 *) bp->addr);
} else {
sprintf(buf + len, "\n");
}
}
static int debugger_fn_bs (struct Scsi_Host *host, struct debugger_token *token,
- unsigned long args[]) {
+ u32 args[]) {
struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
host->hostdata;
struct NCR53c7x0_break *bp;
char buf[80];
size_t len;
unsigned long flags;
+
save_flags(flags);
cli();
return -1;
}
- bp->address = (unsigned long *) args[0];
+ bp->address = (u32 *) args[0];
memcpy ((void *) bp->old_instruction, (void *) bp->address, 8);
bp->old_size = (((bp->old_instruction[0] >> 24) & DCMD_TYPE_MASK) ==
- DCMD_TYPE_MMI ? 3 : 2;
+ DCMD_TYPE_MMI ? 3 : 2);
bp->next = hostdata->breakpoints;
hostdata->breakpoints = bp->next;
memcpy ((void *) bp->address, (void *) hostdata->E_debug_break, 8);
static const struct debugger_token {
char *name;
int numargs;
- int (*fn)(struct debugger_token *token, unsigned long args[]);
+ int (*fn)(struct debugger_token *token, u32 args[]);
} debugger_tokens[] = {
TOKEN(bc,1), TOKEN(bl,0), TOKEN(bs,1), TOKEN(g,0), TOKEN(halt,0),
{DT_help, "?", 0} , TOKEN(h,0), TOKEN(i,0), TOKEN(mp,2),
struct NCR53c7x0_hostadata *hostdata;
char input_buf[80], /* Kernel space copy of buf */
*ptr; /* Pointer to argument list */
- unsigned long args[3]; /* Arguments */
+ u32 args[3]; /* Arguments */
int i, j, error, len;
if (!(host = inode_to_host(inode)))
host->hostdata;
int copy, left;
unsigned long flags;
+
save_flags(flags);
cli();
while (buflen) {
*/
NCR53c7x0_write8(ISTAT_REG_800, ISTAT_10_SRST);
+ mb();
NCR53c7x0_write8(ISTAT_REG_800, 0);
+ mb();
NCR53c7x0_write8(hostdata->dmode, hostdata->saved_dmode & ~DMODE_MAN);
/* Enable active negation */
NCR53c7x0_write8(STEST3_REG_800, STEST3_800_TE);
-
+ mb();
}
/*
dataout;
int data_transfer_instructions, /* Count of dynamic instructions */
i; /* Counter */
- unsigned long *cmd_datain, /* Address of datain/dataout code */
+ u32 *cmd_datain, /* Address of datain/dataout code */
*cmd_dataout; /* Incremented as we assemble */
#ifdef notyet
void *real; /* Real address */
int size; /* Size of *tmp */
- int alignment; /* Alignment adjustment (0 - 4) */
+ int alignment; /* Alignment adjustment (0 - sizeof(long)-1) */
#endif
unsigned long flags;
NCR53c7x0_local_setup(cmd->host);
#else
/* kmalloc() can allocate any size, but historically has returned
unaligned addresses, so we need to allow for alignment */
- size = hostdata->max_cmd_size + 4;
+ size = hostdata->max_cmd_size + sizeof(void*);
real = kmalloc (size, GFP_ATOMIC);
- alignment = 4 - (((unsigned) real) & 3);
+ alignment = sizeof(void*) - (((unsigned) real) & (sizeof(void*)-1));
tmp = (struct NCR53c7x0_cmd *) (((char *) real) + alignment);
if (!tmp)
break;
}
}
-
/*
* Decide whether we need to generate commands for DATA IN,
* DATA OUT, neither, or both based on the SCSI command
if (data_transfer_instructions < 2)
data_transfer_instructions = 2;
-
/*
* Initialize Linux specific fields.
*/
*/
tmp->data_transfer_start = tmp->dsa + (hostdata->dsa_end -
- hostdata->dsa_start) / sizeof(long);
+ hostdata->dsa_start) / sizeof(u32);
tmp->data_transfer_end = tmp->data_transfer_start +
2 * data_transfer_instructions;
if (hostdata->dsa_fixup)
hostdata->dsa_fixup(tmp);
- patch_dsa_32(tmp->dsa, dsa_next, 0, NULL);
- patch_dsa_32(tmp->dsa, dsa_cmnd, 0, cmd);
+ patch_dsa_32(tmp->dsa, dsa_next, 0, 0);
+ patch_dsa_32(tmp->dsa, dsa_cmnd, 0, virt_to_bus(cmd));
patch_dsa_32(tmp->dsa, dsa_select, 0, hostdata->sync[cmd->target].
select_indirect);
/*
#else
tmp->select[0] = IDENTIFY (0, cmd->lun);
#endif
- patch_dsa_32(tmp->dsa, dsa_msgout, 1, tmp->select);
+ 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, cmd->cmnd);
- patch_dsa_32(tmp->dsa, dsa_dataout, 0, cmd_dataout ?
- cmd_dataout : hostdata->script + hostdata->E_other_transfer /
- sizeof (long));
- patch_dsa_32(tmp->dsa, dsa_datain, 0, cmd_datain ?
- cmd_datain : hostdata->script + hostdata->E_other_transfer /
- sizeof (long));
+ 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);
- patch_dsa_32(tmp->dsa, dsa_msgin, 1, (((unsigned long) &cmd->result) + 1));
+ 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, &cmd->result);
+ patch_dsa_32(tmp->dsa, dsa_status, 1, virt_to_bus(&cmd->result));
patch_dsa_32(tmp->dsa, dsa_msgout_other, 0, 1);
patch_dsa_32(tmp->dsa, dsa_msgout_other, 1,
- &(hostdata->NCR53c7xx_msg_nop));
+ virt_to_bus(&hostdata->NCR53c7xx_msg_nop));
/*
for (i = 0; cmd->use_sg ? (i < cmd->use_sg) : !i; cmd_datain += 4,
cmd_dataout += 4, ++i) {
- unsigned long buf = (unsigned long) (cmd->use_sg ?
- ((struct scatterlist *)cmd->buffer)[i].address :
- cmd->request_buffer);
- unsigned long count = (unsigned long) (cmd->use_sg ?
+ 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);
+ cmd->request_bufflen;
if (datain) {
cmd_datain[0] = ((DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I | DCMD_BMI_IO)
cmd_datain[2] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_CALL |
DCMD_TCI_CD | DCMD_TCI_IO | DCMD_TCI_MSG) << 24) |
DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE | DBC_TCI_TRUE;
- cmd_datain[3] = (unsigned long) hostdata->script +
- hostdata->E_msg_in;
+ cmd_datain[3] = virt_to_bus(hostdata->script) +
+ hostdata->E_msg_in;
#if 0
print_insn (host, cmd_datain, "dynamic ", 1);
print_insn (host, cmd_datain + 2, "dynamic ", 1);
cmd_dataout[2] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_CALL |
DCMD_TCI_CD | DCMD_TCI_IO | DCMD_TCI_MSG) << 24) |
DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE | DBC_TCI_TRUE;
- cmd_dataout[3] = (unsigned long) hostdata->script +
- hostdata->E_msg_in;
+ cmd_dataout[3] = virt_to_bus(hostdata->script) +
+ hostdata->E_msg_in;
#if 0
print_insn (host, cmd_dataout, "dynamic ", 1);
print_insn (host, cmd_dataout + 2, "dynamic ", 1);
if (datain) {
cmd_datain[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP) << 24) |
DBC_TCI_TRUE;
- cmd_datain[1] = (unsigned long) hostdata->script +
- hostdata->E_other_transfer;
+ cmd_datain[1] = virt_to_bus(hostdata->script) +
+ hostdata->E_other_transfer;
#if 0
print_insn (host, cmd_datain, "dynamic jump ", 1);
#endif
if (dataout) {
cmd_dataout[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP) << 24) |
DBC_TCI_TRUE;
- cmd_dataout[1] = (unsigned long) hostdata->script +
- hostdata->E_other_transfer;
+ cmd_dataout[1] = virt_to_bus(hostdata->script) +
+ hostdata->E_other_transfer;
#if 0
print_insn (host, cmd_dataout, "dynamic jump ", 1);
#endif
- hostdata->dsa_start;
/* dsa start is negative, so subtraction is used */
#if 0
- printk("scsi%d : new dsa is 0x%x\n", host->host_no, (unsigned) dsa);
+ printk("scsi%d : new dsa is 0x%p\n", host->host_no, dsa);
#endif
if (hostdata->running_list)
hostdata->running_list->prev = tmp;
- tmp->next = hostdata->running_list;
+ tmp->next = (struct NCR53c7x0_cmd*) hostdata->running_list;
if (!hostdata->running_list)
- hostdata->running_list = tmp;
+ hostdata->running_list = (struct NCR53c7x0_cmd*) tmp;
if (hostdata->idle) {
hostdata->idle = 0;
hostdata->state = STATE_RUNNING;
- NCR53c7x0_write32 (DSP_REG, ((unsigned long) hostdata->script) +
+ NCR53c7x0_write32 (DSP_REG, virt_to_bus(hostdata->script) +
hostdata->E_schedule);
+ mb();
}
/* XXX - make function */
#if 0
printk ("scsi%d : no issue queue\n", host->host_no);
#endif
- hostdata->issue_dsa_tail = hostdata->issue_dsa_head = dsa;
+ hostdata->issue_dsa_tail = (u32 *) dsa;
+ hostdata->issue_dsa_head = virt_to_bus(dsa);
NCR53c7x0_write8(hostdata->istat,
NCR53c7x0_read8(hostdata->istat) | ISTAT_10_SIGP);
+ mb();
break;
/*
* Otherwise, we blindly perform an atomic write
*/
} else {
printk ("scsi%d : existing issue queue\n", host->host_no);
- /* XXX - Replace with XCHG or equivalent */
- hostdata->issue_dsa_tail = *((unsigned char **)
- (hostdata->issue_dsa_tail + hostdata->dsa_next)) = dsa;
+ hostdata->issue_dsa_tail[hostdata->dsa_next/sizeof(u32)]
+ = virt_to_bus(dsa);
+ hostdata->issue_dsa_tail = (u32 *) dsa;
/*
* After which, one of two things will happen :
* The NCR will have scheduled a command, either this
}
-int fix_pointers (unsigned long dsa) {
+int fix_pointers (u32 dsa) {
return 0;
}
unsigned char sstat0_sist0, sist1, /* Registers */
fatal; /* Did a fatal interrupt
occur ? */
+ int is_8xx_chip;
NCR53c7x0_local_setup(host);
fatal = 0;
- if ((hostdata->chip / 100) == 8) {
+ is_8xx_chip = ((unsigned) (hostdata->chip - 800)) < 100;
+ if (is_8xx_chip) {
sstat0_sist0 = NCR53c7x0_read8(SIST0_REG_800);
udelay(1);
sist1 = NCR53c7x0_read8(SIST1_REG_800);
printk ("scsi%d : SIST0 0x%0x, SIST1 0x%0x\n", host->host_no,
sstat0_sist0, sist1);
- /* 250ms selection timeout */
- if ((((hostdata->chip / 100) == 8) && (sist1 & SIST1_800_STO)) ||
- (((hostdata->chip / 100) != 8) && (sstat0_sist0 & SSTAT0_700_STO))) {
+ /* selection timeout */
+ if ((is_8xx_chip && (sist1 & SIST1_800_STO)) ||
+ (!is_8xx_chip && (sstat0_sist0 & SSTAT0_700_STO))) {
fatal = 1;
if (hostdata->options & OPTION_DEBUG_INTR) {
printk ("scsi%d : Selection Timeout\n", host->host_no);
abnormal_finished(cmd, DID_ERROR << 16);
}
hostdata->dsp = hostdata->script + hostdata->E_schedule /
- sizeof(long);
+ sizeof(u32);
hostdata->dsp_changed = 1;
/* SCSI PARITY error */
}
/* XXX - Reduce synchronous transfer rate! */
hostdata->dsp = hostdata->script + hostdata->E_initiator_abort /
- sizeof(long);
+ sizeof(u32);
hostdata->dsp_changed = 1;
/* SCSI GROSS error */
}
printk("scsi%d : gross error\n", host->host_no);
/* XXX Reduce synchronous transfer rate! */
hostdata->dsp = hostdata->script + hostdata->E_initiator_abort /
- sizeof(long);
+ sizeof(u32);
hostdata->dsp_changed = 1;
/* Phase mismatch */
}
#if 0
if (NCR53c7x0_read8 (CTEST2_REG_800) & CTEST2_800_DDIR) {
NCR53c7x0_write8 (CTEST3_REG_800, CTEST3_800_FLF);
+ mb();
while (!((hostdata->dstat = NCR53c7x0_read8(DSTAT_REG)) &
DSTAT_DFE));
} else
#endif
{
NCR53c7x0_write8 (CTEST3_REG_800, CTEST3_800_CLF);
+ mb();
while (NCR53c7x0_read8 (CTEST3_REG_800) & CTEST3_800_CLF);
}
}
NCR53c7x0_write8 (STEST3_REG_800, STEST3_800_CSF);
+ mb();
while (NCR53c7x0_read8 (STEST3_REG_800) & STEST3_800_CSF);
}
#endif
struct NCR53c7x0_hostdata *hostdata; /* host->hostdata */
struct NCR53c7x0_cmd *cmd, /* command which halted */
**cmd_prev_ptr;
- unsigned long *dsa; /* DSA */
+ u32 *dsa; /* DSA */
int done = 1; /* Indicates when handler
should terminate */
int interrupted = 0; /* This HA generated
an interrupt */
- unsigned long flags;
+ unsigned long flags;
#ifdef NCR_DEBUG
char buf[80]; /* Debugging sprintf buffer */
do {
done = 1;
- for (host = first_host; host; host = hostdata->next ?
- hostdata->next : NULL) {
+ for (host = first_host; host; host = hostdata->next) {
NCR53c7x0_local_setup(host);
hostdata = (struct NCR53c7x0_hostdata *) host->hostdata;
do {
+ int is_8xx_chip;
+
hostdata->dstat_valid = 0;
interrupted = 0;
/*
* at the contents of the DSA register and continue running.
*/
/* XXX - this is getting big, and should move to intr_intfly() */
+ is_8xx_chip = ((unsigned) (hostdata->chip - 800)) < 100;
if ((hostdata->options & OPTION_INTFLY) &&
- ((hostdata->chip / 100) == 8 && (istat & ISTAT_800_INTF))) {
+ (is_8xx_chip && (istat & ISTAT_800_INTF))) {
char search_found = 0; /* Got at least one ? */
done = 0;
interrupted = 1;
* is self-clearing.
*/
NCR53c7x0_write8(hostdata->istat, istat|ISTAT_800_INTF);
+ mb();
if (hostdata->options & OPTION_DEBUG_INTR)
printk ("scsi%d : INTFLY\n", host->host_no);
if (hostdata->options & OPTION_700) {
cmd = (struct NCR53c7x0_cmd *) hostdata->current;
} else {
- dsa = (unsigned long *) NCR53c7x0_read32(DSA_REG);
+ dsa = bus_to_virt(NCR53c7x0_read32(DSA_REG));
for (cmd = (struct NCR53c7x0_cmd *)
hostdata->running_list; cmd &&
- (dsa + (hostdata->dsa_start / sizeof(long))) !=
+ (dsa + (hostdata->dsa_start / sizeof(u32))) !=
cmd->dsa;
cmd = (struct NCR53c7x0_cmd *)(cmd->next));
}
#if 0
if (NCR53c7x0_read8 (CTEST2_REG_800) & CTEST2_800_DDIR) {
NCR53c7x0_write8 (CTEST3_REG_800, CTEST3_800_FLF);
+ mb();
while (!((hostdata->dstat = NCR53c7x0_read8(DSTAT_REG)) &
DSTAT_DFE));
} else
#endif
{
NCR53c7x0_write8 (CTEST3_REG_800, CTEST3_800_CLF);
+ mb();
while (NCR53c7x0_read8 (CTEST3_REG_800) & CTEST3_800_CLF);
}
}
if (!hostdata->idle && hostdata->state == STATE_HALTED) {
if (!hostdata->dsp_changed) {
- hostdata->dsp = (unsigned long *) NCR53c7x0_read32(DSP_REG);
+ hostdata->dsp = bus_to_virt(NCR53c7x0_read32(DSP_REG));
}
#if 0
- printk("scsi%d : new dsp is 0x%lx\n", host->host_no,
- (long) hostdata->dsp);
+ printk("scsi%d : new dsp is 0x%p\n", host->host_no,
+ hostdata->dsp);
#endif
hostdata->state = STATE_RUNNING;
- NCR53c7x0_write32 (DSP_REG, (unsigned long) hostdata->dsp);
+ NCR53c7x0_write32 (DSP_REG, virt_to_bus(hostdata->dsp));
+ mb();
}
}
} while (!done);
host->hostdata;
hostdata->dsp = hostdata->script + hostdata->E_initiator_abort /
- sizeof(long);
+ sizeof(u32);
hostdata->dsp_changed = 1;
printk ("scsi%d : DANGER : abort_connected() called \n",
host->host_no);
static void intr_phase_mismatch (struct Scsi_Host *host, struct NCR53c7x0_cmd
*cmd) {
NCR53c7x0_local_declare();
- unsigned long dbc_dcmd, *dsp, *dsp_next;
+ u32 dbc_dcmd, *dsp, *dsp_next;
unsigned char dcmd, sbcl;
struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
host->hostdata;
* occurred, as well as which SCSI phase we are currently in.
*/
- dsp_next = (unsigned long *) NCR53c7x0_read32(DSP_REG);
+ dsp_next = bus_to_virt(NCR53c7x0_read32(DSP_REG));
/*
* Like other processors, the NCR adjusts the DSP pointer before
* instruction decode. Set the DSP address back to what it should
- * be for this instruction based on its size (2 or 3 longs).
+ * be for this instruction based on its size (2 or 3 words).
*/
dbc_dcmd = NCR53c7x0_read32(DBC_REG);
* mismatches should only occur in the data transfer routines, or
* when a command is being aborted.
*/
- if (dsp >= cmd->data_transfer_start & dsp < cmd->data_transfer_end) {
+ if (dsp >= cmd->data_transfer_start && dsp < cmd->data_transfer_end) {
/*
* There are three instructions used in our data transfer routines with
if (hostdata->options & OPTION_DEBUG_INTR)
printk ("scsi%d : new phase = STATIN\n", host->host_no);
hostdata->dsp = hostdata->script + hostdata->E_command_complete /
- sizeof(long);
+ sizeof(u32);
hostdata->dsp_changed = 1;
return;
/*
DCMD_BMI_OP_MOVE_I)) {
dsp[0] = dbc_dcmd;
dsp[1] = NCR53c7x0_read32(DNAD_REG);
- NCR53c7x0_write32(TEMP_REG, (unsigned long) dsp);
+ NCR53c7x0_write32(TEMP_REG, virt_to_bus(dsp));
+ mb();
hostdata->dsp = hostdata->script + hostdata->E_msg_in /
- sizeof(long);
+ sizeof(u32);
hostdata->dsp_changed = 1;
} else {
printk("scsi%d : unexpected MSGIN in dynamic NCR code, dcmd=0x%x.\n",
* Any other phase mismatches abort the currently executing command.
*/
} else {
- printk ("scsi%d : unexpected phase %s at dsp = 0x%x\n",
- host->host_no, phase, (unsigned) dsp);
+ printk ("scsi%d : unexpected phase %s at dsp = 0x%p\n",
+ host->host_no, phase, dsp);
print_insn (host, dsp, "", 1);
print_insn (host, dsp_next, "", 1);
abort_connected(host);
struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
host->hostdata;
unsigned char dstat; /* DSTAT */
- unsigned long *dsp,
+ u32 *dsp,
*next_dsp, /* Current dsp */
*dsa,
dbc_dcmd; /* DCMD (high eight bits) + DBC */
printk("scsi%d : DSTAT=0x%x\n", host->host_no, (int) dstat);
dbc_dcmd = NCR53c7x0_read32 (DBC_REG);
- next_dsp = (unsigned long *) NCR53c7x0_read32(DSP_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 = (unsigned long *) NCR53c7x0_read32(DSA_REG);
+ dsa = bus_to_virt(NCR53c7x0_read32(DSA_REG));
/*
* DSTAT_ABRT is the aborted interrupt. This is set whenever the
NCR53c7x0_write8 (DCNTL_REG, (NCR53c7x0_read8(DCNTL_REG) &
~DCNTL_SSM) | DCNTL_STD);
+ mb();
restore_flags(flags);
} else {
printk("scsi%d : unexpected single step interrupt at\n"
* can ignore it.
*/
- if (((dsp >= (hostdata->script + hostdata->E_select / sizeof(long))) &&
+ if (((dsp >= (hostdata->script + hostdata->E_select / sizeof(u32))) &&
(dsp <= (hostdata->script + hostdata->E_select_msgout /
- sizeof(long) + 8))) || (hostdata->test_running == 2)) {
+ 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);
} else {
printk("scsi%d : illegal instruction ", host->host_no);
print_insn (host, dsp, "", 1);
- printk("scsi%d : DSP=0x%lx, DCMD|DBC=0x%lx, DSA=0x%lx\n"
- " DSPS=0x%lx, TEMP=0x%lx, DMODE=0x%x,\n"
- " DNAD=0x%lx\n",
- host->host_no, (unsigned long) dsp, dbc_dcmd,
- (unsigned long) dsa, NCR53c7x0_read32(DSPS_REG),
- NCR53c7x0_read32(TEMP_REG), (int) NCR53c7x0_read8(hostdata->dmode),
+ printk("scsi%d : DSP=0x%p, DCMD|DBC=0x%x, DSA=0x%p\n"
+ " DSPS=0x%x, TEMP=0x%x, DMODE=0x%x,\n"
+ " DNAD=0x%x\n",
+ host->host_no, dsp, dbc_dcmd,
+ dsa, NCR53c7x0_read32(DSPS_REG),
+ NCR53c7x0_read32(TEMP_REG), NCR53c7x0_read8(hostdata->dmode),
NCR53c7x0_read32(DNAD_REG));
panic(" mail drew@Colorado.EDU\n");
}
*/
if (dstat & DSTAT_800_BF) {
- printk("scsi%d : BUS FAULT, DSP=0x%lx, DCMD|DBC=0x%lx, DSA=0x%lx\n"
- " DSPS=0x%lx, TEMP=0x%lx, DMODE=0x%x\n",
- host->host_no, (unsigned long) dsp, NCR53c7x0_read32(DBC_REG),
- (unsigned long) dsa, NCR53c7x0_read32(DSPS_REG),
- NCR53c7x0_read32(TEMP_REG), (int) NCR53c7x0_read8(hostdata->dmode));
+ printk("scsi%d : BUS FAULT, DSP=0x%p, DCMD|DBC=0x%x, DSA=0x%p\n"
+ " DSPS=0x%x, TEMP=0x%x, DMODE=0x%x\n",
+ host->host_no, dsp, NCR53c7x0_read32(DBC_REG),
+ dsa, NCR53c7x0_read32(DSPS_REG),
+ NCR53c7x0_read32(TEMP_REG), NCR53c7x0_read8(hostdata->dmode));
print_dsa (host, dsa);
printk("scsi%d : DSP->\n", host->host_no);
print_insn(host, dsp, "", 1);
/* All DMA interrupts are fatal. Flush SCSI queue */
NCR53c7x0_write8 (STEST3_REG_800, STEST3_800_CSF);
+ mb();
while (NCR53c7x0_read8 (STEST3_REG_800) & STEST3_800_CSF);
}
/*
* Function : static int print_insn (struct Scsi_Host *host,
- * unsigned long *insn, int kernel)
+ * u32 *insn, int kernel)
*
* Purpose : print numeric representation of the instruction pointed
* to by insn to the debugging or kernel message buffer
* Inputs : host, insn - host, pointer to instruction, prefix -
* string to prepend, kernel - use printk instead of debugging buffer.
*
- * Returns : size, in longs, of instruction printed.
+ * Returns : size, in ints, of instruction printed.
*/
-static int print_insn (struct Scsi_Host *host, unsigned long *insn,
+static int print_insn (struct Scsi_Host *host, u32 *insn,
char *prefix, int kernel) {
char buf[80], /* Temporary buffer and pointer */
*tmp;
int size;
dcmd = (insn[0] >> 24) & 0xff;
- sprintf(buf, "%s%08lx : 0x%08lx 0x%08lx", (prefix ? prefix : ""),
- (unsigned long) insn, insn[0], insn[1]);
+ sprintf(buf, "%s%p : 0x%08x 0x%08x", (prefix ? prefix : ""),
+ insn, insn[0], insn[1]);
tmp = buf + strlen(buf);
if ((dcmd & DCMD_TYPE_MASK) == DCMD_TYPE_MMI) {
- sprintf (tmp, " 0x%08lx\n", insn[2]);
+ sprintf (tmp, " 0x%08x\n", insn[2]);
size = 3;
} else {
sprintf (tmp, "\n");
struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
host->hostdata;
unsigned long flags;
- struct NCR53c7x0_cmd *curr, **prev;
+ volatile struct NCR53c7x0_cmd *curr, **prev;
save_flags(flags);
cli();
* pull the command out of the old queue, and call it aborted.
*/
- for (curr = (struct NCR53c7x0_cmd *) hostdata->issue_queue,
- prev = (struct NCR53c7x0_cmd **) &(hostdata->issue_queue);
- curr && curr->cmd != cmd; prev = (struct NCR53c7x0_cmd **)
- &(curr->next), curr = (struct NCR53c7x0_cmd *) curr->next);
+ for (curr = (volatile struct NCR53c7x0_cmd *) hostdata->issue_queue,
+ prev = (volatile struct NCR53c7x0_cmd **) &(hostdata->issue_queue);
+ curr && curr->cmd != cmd; prev = (volatile struct NCR53c7x0_cmd **)
+ &(curr->next), curr = (volatile struct NCR53c7x0_cmd *) curr->next);
if (curr) {
*prev = (struct NCR53c7x0_cmd *) curr->next;
* 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);
+ for (curr = (volatile struct NCR53c7x0_cmd *) hostdata->running_list,
+ prev = (volatile struct NCR53c7x0_cmd **) &(hostdata->running_list);
+ curr && curr->cmd != cmd; prev = (volatile struct NCR53c7x0_cmd **)
+ &(curr->next), curr = (volatile struct NCR53c7x0_cmd *) curr->next);
if (curr) {
restore_flags(flags);
*
* Returns : 0 on success.
*/
-
-int
+
+int
NCR53c7xx_reset (Scsi_Cmnd *cmd) {
NCR53c7x0_local_declare();
unsigned long flags;
struct NCR53c7x0_hostdata *hostdata = host ?
(struct NCR53c7x0_hostdata *) host->hostdata : NULL;
NCR53c7x0_local_setup(host);
+
save_flags(flags);
- halt (host);
+ ncr_halt (host);
NCR53c7x0_write8(SCNTL1_REG, SCNTL1_RST);
udelay(25); /* Minimum amount of time to assert RST */
NCR53c7x0_write8(SCNTL1_REG, SCNTL1_RST);
cmd->scsi_done(cmd);
}
restore_flags(flags);
-
- printk ("scsi%d : DANGER : NCR53c7xx_reset is NOP\n",
- cmd->host->host_no);
return SCSI_RESET_SUCCESS;
}
* therefore shares the scsicam_bios_param function.
*/
-static void print_dsa (struct Scsi_Host *host, unsigned long *dsa) {
+static void print_dsa (struct Scsi_Host *host, u32 *dsa) {
struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
host->hostdata;
int i, len;
char *ptr;
- printk("scsi%d : dsa at 0x%x\n"
- " + %ld : dsa_msgout length = %lu, data = 0x%lx\n" ,
- host->host_no, (unsigned) dsa, hostdata->dsa_msgout,
- dsa[hostdata->dsa_msgout / sizeof(long)],
- dsa[hostdata->dsa_msgout / sizeof(long) + 1]);
+ printk("scsi%d : dsa at 0x%p\n"
+ " + %d : dsa_msgout length = %d, data = 0x%x\n" ,
+ host->host_no, dsa, hostdata->dsa_msgout,
+ dsa[hostdata->dsa_msgout / sizeof(u32)],
+ dsa[hostdata->dsa_msgout / sizeof(u32) + 1]);
- for (i = dsa[hostdata->dsa_msgout / sizeof(long)],
- ptr = (char *) dsa[hostdata->dsa_msgout / sizeof(long) + 1]; i > 0;
+ for (i = dsa[hostdata->dsa_msgout / sizeof(u32)],
+ ptr = bus_to_virt(dsa[hostdata->dsa_msgout / sizeof(u32) + 1]); i > 0;
ptr += len, i -= len) {
printk(" ");
len = print_msg (ptr);
NCR53c7x0_local_setup(host);
save_flags (flags);
cli();
- halt (host);
+ ncr_halt (host);
hostdata->soft_reset(host);
/*
* For now, we take the simplest solution : reset the SCSI bus. Eventually,
/*
- * Function : static int halt (struct Scsi_Host *host)
+ * Function : static int ncr_halt (struct Scsi_Host *host)
*
* Purpose : halts the SCSI SCRIPTS(tm) processor on the NCR chip
*
*/
static int
-halt (struct Scsi_Host *host) {
+ncr_halt (struct Scsi_Host *host) {
NCR53c7x0_local_declare();
unsigned long flags;
unsigned char istat, tmp;
#ifndef NCR53c7x0_H
#define NCR53c7x0_H
+#ifdef __alpha__
+
+# define ncr_readb(a) ((unsigned int)readb((unsigned long)(a)))
+# define ncr_readw(a) ((unsigned int)readw((unsigned long)(a)))
+# define ncr_readl(a) ((unsigned int)readl((unsigned long)(a)))
+# define ncr_writeb(v,a) (writeb((v), (unsigned long)(a)))
+# define ncr_writew(v,a) (writew((v), (unsigned long)(a)))
+# define ncr_writel(v,a) (writel((v), (unsigned long)(a)))
+
+#else
+
+# define ncr_readb(a) (*(unsigned char*)(a))
+# define ncr_readw(a) (*(unsigned short*)(a))
+# define ncr_readl(a) (*(unsigned int*)(a))
+# define ncr_writeb(v,a) (*(unsigned char*)(a) = (v))
+# define ncr_writew(v,a) (*(unsigned short*)(a) = (v))
+# define ncr_writel(v,a) (*(unsigned int*)(a) = (v))
+
+#endif
+
/*
* Prevent name space pollution in hosts.c, and only provide the
#define DCMD_RWRI_OP_ADDC 0x07
#define DCMD_TYPE_MMI 0xc0 /* Indicates a Memory Move instruction
- (three longs) */
+ (three words) */
#define DNAD_REG 0x28 /* through 0x2b DMA next address for
#endif
struct NCR53c7x0_synchronous {
- unsigned long select_indirect; /* Value used for indirect selection */
- unsigned long script[6]; /* Size ?? Script used when target is
+ u32 select_indirect; /* Value used for indirect selection */
+ u32 script[6]; /* Size ?? Script used when target is
reselected */
unsigned renegotiate:1; /* Force renegotiation on next
select */
#define CMD_FLAG_DID_SDTR 4 /* did SDTR */
struct NCR53c7x0_table_indirect {
- unsigned long count;
+ u32 count;
void *address;
};
- unsigned long *data_transfer_start; /* Start of data transfer routines */
- unsigned long *data_transfer_end; /* Address after end of data transfer o
+ u32 *data_transfer_start; /* Start of data transfer routines */
+ u32 *data_transfer_end; /* Address after end of data transfer o
routines */
- unsigned long residual[8]; /* Residual data transfer
+ u32 residual[8]; /* Residual data transfer
shadow of data_transfer code.
Has instruction with modified
CALL routine following command.
*/
- unsigned long dsa[0]; /* Variable length (depending
+ u32 dsa[0]; /* Variable length (depending
on host type, number of scatter /
gather buffers, etc). */
};
struct NCR53c7x0_break {
- unsigned long *address, old_instruction[2];
+ u32 *address, old_instruction[2];
struct NCR53c7x0_break *next;
unsigned char old_size; /* Size of old instruction */
};
unsigned char pci_bus, pci_device_fn;
unsigned pci_valid:1;
- unsigned long *dsp; /* dsp to restart with after
+ u32 *dsp; /* dsp to restart with after
all stacked interrupts are
handled. */
* chip.
*/
- long dsa_start;
- long dsa_end;
- long dsa_next;
- long dsa_prev;
- long dsa_cmnd;
- long dsa_select;
- long dsa_msgout;
- long dsa_cmdout;
- long dsa_dataout;
- long dsa_datain;
- long dsa_msgin;
- long dsa_msgout_other;
- long dsa_write_sync;
- long dsa_write_resume;
- long dsa_jump_resume;
- long dsa_check_reselect;
- long dsa_status;
+ 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_jump_resume;
+ s32 dsa_check_reselect;
+ s32 dsa_status;
/*
* Important entry points that generic fixup code needs
* to know about, fixed up.
*/
- long E_accept_message;
- long E_dsa_code_template;
- long E_dsa_code_template_end;
- long E_command_complete;
- long E_msg_in;
- long E_initiator_abort;
- long E_other_transfer;
- long E_target_abort;
- long E_schedule;
- long E_debug_break;
- long E_reject_message;
- long E_respond_message;
- long E_select;
- long E_select_msgout;
- long E_test_0;
- long E_test_1;
- long E_test_2;
- long E_test_3;
- long E_dsa_zero;
- long E_dsa_jump_resume;
+ s32 E_accept_message;
+ s32 E_dsa_code_template;
+ s32 E_dsa_code_template_end;
+ s32 E_command_complete;
+ s32 E_msg_in;
+ s32 E_initiator_abort;
+ s32 E_other_transfer;
+ s32 E_target_abort;
+ s32 E_schedule;
+ 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_dsa_jump_resume;
int options; /* Bitfielded set of options enabled */
- long test_completed; /* Test completed */
+ volatile u32 test_completed; /* Test completed */
int test_running; /* Test currently running */
int test_source;
volatile int test_dest;
/* Shared variables between SCRIPT and host driver */
- volatile unsigned char *issue_dsa_head;
+ volatile u32 issue_dsa_head;
/* commands waiting to be
issued, insertions are
done by Linux driver,
deletions are done by
NCR */
- volatile unsigned char *issue_dsa_tail;
+ u32 *issue_dsa_tail; /* issue queue tail pointer;
+ used by Linux driver only */
volatile unsigned char msg_buf[16]; /* buffer for messages
other than the command
complete message */
- volatile unsigned char *reconnect_dsa_head;
+ volatile u32 reconnect_dsa_head;
/* disconnected commands,
maintained by NCR */
/* Data identifying nexus we are trying to match during reselection */
message or 0 */
/* These were static variables before we moved them */
- long NCR53c7xx_zero;
- long NCR53c7xx_sink;
+ s32 NCR53c7xx_zero;
+ s32 NCR53c7xx_sink;
char NCR53c7xx_msg_reject;
char NCR53c7xx_msg_abort;
char NCR53c7xx_msg_nop;
- int script_count; /* Size of script in longs */
- unsigned long script[0]; /* Relocated SCSI script */
+ int script_count; /* Size of script in words */
+ u32 script[0]; /* Relocated SCSI script */
};
#define NCR53c7x0_local_declare() \
volatile unsigned char *NCR53c7x0_address_memory; \
- unsigned short NCR53c7x0_address_io; \
+ unsigned int NCR53c7x0_address_io; \
int NCR53c7x0_memory_mapped
#define NCR53c7x0_local_setup(host) \
NCR53c7x0_address_memory = (void *) (host)->base; \
- NCR53c7x0_address_io = (unsigned short) (host)->io_port; \
+ NCR53c7x0_address_io = (unsigned int) (host)->io_port; \
NCR53c7x0_memory_mapped = ((struct NCR53c7x0_hostdata *) \
host->hostdata)-> options & OPTION_MEMORY_MAPPED
#define NCR53c7x0_read8(address) \
(NCR53c7x0_memory_mapped ? \
- *( (NCR53c7x0_address_memory) + (address)) : \
+ ncr_readb(NCR53c7x0_address_memory + (address)) : \
inb(NCR53c7x0_address_io + (address)))
#define NCR53c7x0_read16(address) \
(NCR53c7x0_memory_mapped ? \
- *((unsigned short *) (NCR53c7x0_address_memory) + (address)) : \
+ ncr_readw(NCR53c7x0_address_memory + (address)) : \
inw(NCR53c7x0_address_io + (address)))
#define NCR53c7x0_read32(address) \
(NCR53c7x0_memory_mapped ? \
- *((unsigned long *) (NCR53c7x0_address_memory) + (address)) : \
+ ncr_readl(NCR53c7x0_address_memory + (address)) : \
inl(NCR53c7x0_address_io + (address)))
#define NCR53c7x0_write8(address,value) \
(NCR53c7x0_memory_mapped ? \
- *((unsigned char *) (NCR53c7x0_address_memory) + (address)) = \
- (value) : \
+ ncr_writeb((value), NCR53c7x0_address_memory + (address)) : \
outb((value), NCR53c7x0_address_io + (address)))
#define NCR53c7x0_write16(address,value) \
(NCR53c7x0_memory_mapped ? \
- *((unsigned short *) (NCR53c7x0_address_memory) + (address)) = \
- (value) : \
+ ncr_writew((value), NCR53c7x0_address_memory + (address)) : \
outw((value), NCR53c7x0_address_io + (address)))
#define NCR53c7x0_write32(address,value) \
(NCR53c7x0_memory_mapped ? \
- *((unsigned long *) (NCR53c7x0_address_memory) + (address)) = \
- (value) : \
+ ncr_writel((value), NCR53c7x0_address_memory + (address)) : \
outl((value), NCR53c7x0_address_io + (address)))
#define patch_abs_32(script, offset, symbol, value) \
for (i = 0; i < (sizeof (A_##symbol##_used) / sizeof \
- (unsigned long)); ++i) { \
+ (u32)); ++i) { \
(script)[A_##symbol##_used[i] - (offset)] += (value); \
if (hostdata->options & OPTION_DEBUG_FIXUP) \
- printk("scsi%d : %s reference %d at 0x%lx in %s is now 0x%lx\n",\
+ 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] - \
- (offset), #script, (script)[A_##symbol##_used[i] - \
+ (int)(offset), #script, (script)[A_##symbol##_used[i] - \
(offset)]); \
}
#define patch_abs_rwri_data(script, offset, symbol, value) \
for (i = 0; i < (sizeof (A_##symbol##_used) / sizeof \
- (unsigned long)); ++i) \
+ (u32)); ++i) \
(script)[A_##symbol##_used[i] - (offset)] = \
((script)[A_##symbol##_used[i] - (offset)] & \
~DBC_RWRI_IMMEDIATE_MASK) | \
#define patch_dsa_32(dsa, symbol, word, value) \
{ \
- (dsa)[(hostdata->##symbol - hostdata->dsa_start) / sizeof(long) \
- + (word)] = (unsigned long) (value); \
+ (dsa)[(hostdata->##symbol - hostdata->dsa_start) / sizeof(u32) \
+ + (word)] = (value); \
if (hostdata->options & OPTION_DEBUG_DSA) \
- printk("scsi : dsa %s symbol %s(%ld) word %d now 0x%lx\n", \
- #dsa, #symbol, (long) hostdata->##symbol, \
- (int) (word), (long) (value)); \
+ printk("scsi : dsa %s symbol %s(%d) word %d now 0x%x\n", \
+ #dsa, #symbol, hostdata->##symbol, \
+ (word), (u32)(value)); \
}
JUMP reselected_check_next
ABSOLUTE dsa_check_reselect = 0
-; dsa_check_reselect determines weather or not the current target and
+; 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:
issue_remove:
; The actual UPDATE of the issue_dsa_head variable is
; atomic, with all of the setup code being irrelevant to
-; weather the updated value being the old or new contents of
+; whether the updated value being the old or new contents of
; dsa_next field.
;
; To insure synchronization, the host system merely needs to
-unsigned long SCRIPT[] = {
+u32 SCRIPT[] = {
/*
; NCR 53c810 driver, main script
; Sponsored by
/*
ABSOLUTE dsa_check_reselect = 0
-; dsa_check_reselect determines weather or not the current target and
+; 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:
issue_remove:
; The actual UPDATE of the issue_dsa_head variable is
; atomic, with all of the setup code being irrelevant to
-; weather the updated value being the old or new contents of
+; whether the updated value being the old or new contents of
; dsa_next field.
;
; To insure synchronization, the host system merely needs to
};
#define A_NCR53c7xx_msg_abort 0x00000000
-unsigned long A_NCR53c7xx_msg_abort_used[] = {
+u32 A_NCR53c7xx_msg_abort_used[] = {
0x000001f5,
};
#define A_NCR53c7xx_msg_reject 0x00000000
-unsigned long A_NCR53c7xx_msg_reject_used[] = {
+u32 A_NCR53c7xx_msg_reject_used[] = {
0x00000133,
};
#define A_NCR53c7xx_sink 0x00000000
-unsigned long A_NCR53c7xx_sink_used[] = {
+u32 A_NCR53c7xx_sink_used[] = {
0x000001e5,
0x000001e9,
0x000001ed,
};
#define A_NCR53c7xx_zero 0x00000000
-unsigned long A_NCR53c7xx_zero_used[] = {
+u32 A_NCR53c7xx_zero_used[] = {
0x0000015d,
0x000001e1,
0x000001f1,
};
#define A_addr_scratch 0x00000000
-unsigned long A_addr_scratch_used[] = {
+u32 A_addr_scratch_used[] = {
0x00000007,
0x0000003a,
0x00000046,
};
#define A_addr_sfbr 0x00000000
-unsigned long A_addr_sfbr_used[] = {
+u32 A_addr_sfbr_used[] = {
0x00000016,
};
#define A_addr_temp 0x00000000
-unsigned long A_addr_temp_used[] = {
+u32 A_addr_temp_used[] = {
0x00000027,
};
#define A_dmode_memory_to_memory 0x00000000
-unsigned long A_dmode_memory_to_memory_used[] = {
+u32 A_dmode_memory_to_memory_used[] = {
0x00000008,
0x00000019,
0x00000029,
};
#define A_dmode_memory_to_ncr 0x00000000
-unsigned long A_dmode_memory_to_ncr_used[] = {
+u32 A_dmode_memory_to_ncr_used[] = {
0x00000003,
0x00000012,
0x0000004c,
};
#define A_dmode_ncr_to_memory 0x00000000
-unsigned long A_dmode_ncr_to_memory_used[] = {
+u32 A_dmode_ncr_to_memory_used[] = {
0x00000024,
0x00000037,
0x00000043,
};
#define A_dmode_ncr_to_ncr 0x00000000
-unsigned long A_dmode_ncr_to_ncr_used[] = {
+u32 A_dmode_ncr_to_ncr_used[] = {
};
#define A_dsa_check_reselect 0x00000000
-unsigned long A_dsa_check_reselect_used[] = {
+u32 A_dsa_check_reselect_used[] = {
0x0000017f,
};
#define A_dsa_cmdout 0x00000048
-unsigned long A_dsa_cmdout_used[] = {
+u32 A_dsa_cmdout_used[] = {
0x0000008c,
};
#define A_dsa_cmnd 0x00000038
-unsigned long A_dsa_cmnd_used[] = {
+u32 A_dsa_cmnd_used[] = {
};
#define A_dsa_datain 0x00000054
-unsigned long A_dsa_datain_used[] = {
+u32 A_dsa_datain_used[] = {
0x000000b3,
};
#define A_dsa_dataout 0x00000050
-unsigned long A_dsa_dataout_used[] = {
+u32 A_dsa_dataout_used[] = {
0x0000009d,
};
#define A_dsa_end 0x00000070
-unsigned long A_dsa_end_used[] = {
+u32 A_dsa_end_used[] = {
};
#define A_dsa_fields_start 0x00000024
-unsigned long A_dsa_fields_start_used[] = {
+u32 A_dsa_fields_start_used[] = {
};
#define A_dsa_msgin 0x00000058
-unsigned long A_dsa_msgin_used[] = {
+u32 A_dsa_msgin_used[] = {
0x00000147,
};
#define A_dsa_msgout 0x00000040
-unsigned long A_dsa_msgout_used[] = {
+u32 A_dsa_msgout_used[] = {
0x0000006c,
};
#define A_dsa_msgout_other 0x00000068
-unsigned long A_dsa_msgout_other_used[] = {
+u32 A_dsa_msgout_other_used[] = {
0x0000013f,
};
#define A_dsa_next 0x00000030
-unsigned long A_dsa_next_used[] = {
+u32 A_dsa_next_used[] = {
0x0000002f,
0x0000006f,
};
#define A_dsa_select 0x0000003c
-unsigned long A_dsa_select_used[] = {
+u32 A_dsa_select_used[] = {
0x00000067,
};
#define A_dsa_status 0x00000060
-unsigned long A_dsa_status_used[] = {
+u32 A_dsa_status_used[] = {
0x00000143,
};
#define A_dsa_temp_dsa_next 0x00000000
-unsigned long A_dsa_temp_dsa_next_used[] = {
+u32 A_dsa_temp_dsa_next_used[] = {
0x00000001,
0x00000006,
0x0000001c,
};
#define A_dsa_temp_jump_resume 0x00000000
-unsigned long A_dsa_temp_jump_resume_used[] = {
+u32 A_dsa_temp_jump_resume_used[] = {
0x00000028,
};
#define A_dsa_temp_lun 0x00000000
-unsigned long A_dsa_temp_lun_used[] = {
+u32 A_dsa_temp_lun_used[] = {
0x00000017,
};
#define A_dsa_temp_sync 0x00000000
-unsigned long A_dsa_temp_sync_used[] = {
+u32 A_dsa_temp_sync_used[] = {
0x00000021,
};
#define A_dsa_temp_target 0x00000000
-unsigned long A_dsa_temp_target_used[] = {
+u32 A_dsa_temp_target_used[] = {
0x00000010,
};
#define A_int_debug_break 0x03000000
-unsigned long A_int_debug_break_used[] = {
+u32 A_int_debug_break_used[] = {
0x000001d3,
};
#define A_int_debug_dsa_loaded 0x03030000
-unsigned long A_int_debug_dsa_loaded_used[] = {
+u32 A_int_debug_dsa_loaded_used[] = {
};
#define A_int_debug_head 0x03050000
-unsigned long A_int_debug_head_used[] = {
+u32 A_int_debug_head_used[] = {
};
#define A_int_debug_idle 0x03020000
-unsigned long A_int_debug_idle_used[] = {
+u32 A_int_debug_idle_used[] = {
};
#define A_int_debug_reselected 0x03040000
-unsigned long A_int_debug_reselected_used[] = {
+u32 A_int_debug_reselected_used[] = {
};
#define A_int_debug_scheduled 0x03010000
-unsigned long A_int_debug_scheduled_used[] = {
+u32 A_int_debug_scheduled_used[] = {
};
#define A_int_err_check_condition 0x00030000
-unsigned long A_int_err_check_condition_used[] = {
+u32 A_int_err_check_condition_used[] = {
0x00000157,
};
#define A_int_err_no_phase 0x00040000
-unsigned long A_int_err_no_phase_used[] = {
+u32 A_int_err_no_phase_used[] = {
};
#define A_int_err_selected 0x00010000
-unsigned long A_int_err_selected_used[] = {
+u32 A_int_err_selected_used[] = {
0x0000019e,
};
#define A_int_err_unexpected_phase 0x00000000
-unsigned long A_int_err_unexpected_phase_used[] = {
+u32 A_int_err_unexpected_phase_used[] = {
0x00000084,
0x0000008a,
0x0000008e,
};
#define A_int_err_unexpected_reselect 0x00020000
-unsigned long A_int_err_unexpected_reselect_used[] = {
+u32 A_int_err_unexpected_reselect_used[] = {
0x0000017c,
};
#define A_int_msg_1 0x01020000
-unsigned long A_int_msg_1_used[] = {
+u32 A_int_msg_1_used[] = {
0x000000e2,
0x000000e4,
};
#define A_int_msg_sdtr 0x01010000
-unsigned long A_int_msg_sdtr_used[] = {
+u32 A_int_msg_sdtr_used[] = {
0x0000012d,
};
#define A_int_msg_wdtr 0x01000000
-unsigned long A_int_msg_wdtr_used[] = {
+u32 A_int_msg_wdtr_used[] = {
0x00000121,
};
#define A_int_norm_aborted 0x02040000
-unsigned long A_int_norm_aborted_used[] = {
+u32 A_int_norm_aborted_used[] = {
0x000001f9,
};
#define A_int_norm_command_complete 0x02020000
-unsigned long A_int_norm_command_complete_used[] = {
+u32 A_int_norm_command_complete_used[] = {
};
#define A_int_norm_disconnected 0x02030000
-unsigned long A_int_norm_disconnected_used[] = {
+u32 A_int_norm_disconnected_used[] = {
};
#define A_int_norm_reselect_complete 0x02010000
-unsigned long A_int_norm_reselect_complete_used[] = {
+u32 A_int_norm_reselect_complete_used[] = {
};
#define A_int_norm_reset 0x02050000
-unsigned long A_int_norm_reset_used[] = {
+u32 A_int_norm_reset_used[] = {
};
#define A_int_norm_select_complete 0x02000000
-unsigned long A_int_norm_select_complete_used[] = {
+u32 A_int_norm_select_complete_used[] = {
};
#define A_int_test_1 0x04000000
-unsigned long A_int_test_1_used[] = {
+u32 A_int_test_1_used[] = {
0x000001b9,
};
#define A_int_test_2 0x04010000
-unsigned long A_int_test_2_used[] = {
+u32 A_int_test_2_used[] = {
0x000001d1,
};
#define A_int_test_3 0x04020000
-unsigned long A_int_test_3_used[] = {
+u32 A_int_test_3_used[] = {
};
#define A_issue_dsa_head 0x00000000
-unsigned long A_issue_dsa_head_used[] = {
+u32 A_issue_dsa_head_used[] = {
0x0000004f,
0x00000080,
};
#define A_msg_buf 0x00000000
-unsigned long A_msg_buf_used[] = {
+u32 A_msg_buf_used[] = {
0x000000d6,
0x0000010f,
0x00000119,
};
#define A_reconnect_dsa_head 0x00000000
-unsigned long A_reconnect_dsa_head_used[] = {
+u32 A_reconnect_dsa_head_used[] = {
0x0000003f,
0x00000047,
0x00000162,
};
#define A_reselected_identify 0x00000000
-unsigned long A_reselected_identify_used[] = {
+u32 A_reselected_identify_used[] = {
0x00000015,
0x0000015b,
};
#define A_reselected_tag 0x00000000
-unsigned long A_reselected_tag_used[] = {
+u32 A_reselected_tag_used[] = {
0x0000015e,
};
#define A_test_dest 0x00000000
-unsigned long A_test_dest_used[] = {
+u32 A_test_dest_used[] = {
0x000001b7,
};
#define A_test_src 0x00000000
-unsigned long A_test_src_used[] = {
+u32 A_test_src_used[] = {
0x000001b6,
};
#define Ent_test_1 0x000006d4
#define Ent_test_2 0x000006e8
#define Ent_test_2_msgout 0x00000700
-unsigned long LABELPATCHES[] = {
+u32 LABELPATCHES[] = {
0x00000002,
0x0000000b,
0x0000000d,
0x000001ef,
};
-unsigned long INSTRUCTIONS = 0x00000104;
-unsigned long PATCHES = 0x00000048;
+struct {
+ u32 offset;
+ void *address;
+} EXTERNAL_PATCHES[] = {
+};
+
+u32 INSTRUCTIONS = 260;
+u32 PATCHES = 72;
+u32 EXTERNAL_PATCHES_LEN = 0;
SCSI_SRCS := $(SCSI_SRCS) aha1740.c
endif
-ifdef CONFIG_SCSI_AHA274X
-SCSI_OBJS := $(SCSI_OBJS) aha274x.o
-SCSI_SRCS := $(SCSI_SRCS) aha274x.c
+ifdef CONFIG_SCSI_AIC7XXX
+SCSI_OBJS := $(SCSI_OBJS) aic7xxx.o
+SCSI_SRCS := $(SCSI_SRCS) aic7xxx.c
endif
ifdef CONFIG_SCSI_BUSLOGIC
aic7770: aic7770.c
$(CC) $(CFLAGS) -o $@ aic7770.c
-aha274x_seq.h: aic7770 aha274x.seq
- ./aic7770 -o $@ aha274x.seq
+aic7xxx_seq.h: aic7770 aic7xxx.seq
+ ./aic7770 -o $@ aic7xxx.seq
seagate.o: seagate.c
$(CC) $(CFLAGS) -DARBITRATE -DSLOW_HANDSHAKE -DFAST32 -c seagate.c
--- /dev/null
+The 274x/284x support is basically the same as in the kernel, with
+some minor structural changes. The 294x support is preliminary, with
+a few things still hardwired (like the SCSI ID and FIFO threshold).
+You have been warned.
+
+To function properly, you must use this with a kernel version 1.1.68
+or above - these have the SCSI changes in the kernel which support
+varying numbers of outstanding SCSI commands per driver instance (they
+also contain the Adaptec PCI definitions).
+
+You can find instructions in the SCSI-HOWTO on how to install SCSI
+drivers into the kernel. In a nutshell, find all the aha274x stuff
+and change it to aic7xxx.
+
+Please direct all problems to the AIC7770 mailing list - see the README
+for details.
+:ja
+++ /dev/null
-@(#)README 1.15 94/10/29 jda
-
-AHA274x/284x DRIVER
-
-*** THIS SHOULD BE CONSIDERED BETA SOFTWARE ***
-
-BACKGROUND & LIMITATIONS
-
-For various reasons, we ended up with one of these cards under the
-impression that support was soon forthcoming. In mid-May, I asked
-Scott Ferris (the official person who's supposed to be writing this
-driver) what documentation he used, _finally_ got it from Adaptec,
-and started writing this driver. It is now at what I would consider
-a stable state - it runs our news server and is battered by SCSI
-requests 24 hours a day without dying. There are a few devices it
-reportedly doesn't like working with - those are being sorted out. Due
-to some unexpected equipment loans, I am able to support this at least
-for the time being.
-
-YOU MUST HAVE THE BIOS ENABLED OR THIS WILL NOT WORK. The BIOS extracts
-some configuration information that I cannot get to portably yet, as
-well as provides some self-tests which this driver does not attempt to
-duplicate.
-
-Scott's driver development is stalled for now, and after discussions
-with him, this is now officially out of "pre-alpha" status and into
-beta until the remaining device problems can be resolved. The latest
-patches can be obtained via anonymous ftp from ftp.cpsc.ucalgary.ca in
-/pub/systems/linux/aha274x.
-
-It supports both EISA 274x and VL-bus 284x, either single or twin-bus cards
-(but not the second SCSI bus of twin cards - see aha274x.c), and supports
-disconnection, synchronous SCSI, and scatter-gather. Unlike previous
-versions, abort() and reset() are now implemented, and both hosts.c and
-aha274x.c should give a clean compile. Code is now present to detect parity
-errors, but has not been tested.
-
-I wrote this using a 1.0.9 kernel. Unfortunately, I'm getting tired of
-#ifdef'ing everything to handle two or three different evolutionary steps
-in the SCSI kernel code, so I've upgraded my system to 1.1.49, and will
-only leave in code to support versions from about 1.1.45 onward.
-
-Thanks to patches supplied by Mark Olson <molson@tricord.com>, this driver
-will now work with the 284x series (the VL-bus version of this card). The
-294x (PCI-bus) is being worked on, and initial support for it will be ready
-soon.
-
-Under protest, this driver is subject to the GPL - see the file
-COPYING for details.
-
-Thanks to the following people for bug fixes/code improvements (also
-thanks to the people who have sent me feedback):
-
- "David F. Carlson" <dave@ee.rochester.edu>
- Jimen Ching <jiching@wiliki.eng.hawaii.edu>
- mday@artisoft.com (Matt Day)
- "Dean W. Gehnert" <deang@ims.com>
- Darcy Grant <darcy@cpsc.ucalgary.ca>
- Alan Hourihane <alanh@fairlite.demon.co.uk>
- isely@fncrd8.fnal.gov (Mike Isely)
- Mike Jerger <jerger@ux1.cso.uiuc.edu>
- tm@netcom.com (Toshiyasu Morita)
- neal@interact.org (Neal Norwitz)
- Mark Olson <molson@tricord.com>
- map@europa.ecn.uoknor.edu (Michael A. Parker)
- Thomas Scheunemann <thomas@dagobert.uni-duisburg.de>
-
-Special thanks to Drew Eckhardt <drew@kinglear.cs.Colorado.EDU> for
-fielding my questions about synchronous negotiation. Steffen Moeller
-<smoe0024@rz.uni-hildesheim.de> sent me installation instructions which
-were previously included in this README.
-
-David Pirie <pirie@cpsc.ucalgary.ca> was nice enough to loan me his
-2842 card for a week so I could track down one bug, as well as his
-CD-ROM drive later, and also thanks to Doug Fortune at Riley's Data Share
-in Calgary, who arranged a long-term loan of a 2842 board for further work.
-
-Many thanks to the fearless prerelease testers! Dean Gehnert has been
-building Slackware boot disks for the driver, which are available from
-ftp.cpsc.ucalgary.ca in /pub/systems/linux/aha274x/slackware_boot.
-
-Carl Riches <cgr@poplar1.cfr.washington.edu> has set up a mailing list
-for aic7xxx driver development. To subscribe, send a message to
-aic7770-list@poplar1.cfr.washington.edu with a message body of:
-
- subscribe AIC7770-LIST <your name here, without the angle brackets>
-
-Please direct questions and discussions to that list instead of me. When
-sending bug reports, please include a description of your hardware, the
-release numbers displayed by the driver at boot time, and as accurate a
-facsimile of any error message you're mailing about.
-
-John Aycock
-aycock@cpsc.ucalgary.ca
--- /dev/null
+@(#)README 1.19 94/11/30 jda
+
+AHA274x/284x/294x DRIVER
+
+*** THIS SHOULD BE CONSIDERED BETA SOFTWARE ***
+
+BACKGROUND & LIMITATIONS
+
+For various reasons, we ended up with one of these cards under the
+impression that support was soon forthcoming. In mid-May, I asked
+Scott Ferris (the official person who's supposed to be writing this
+driver) what documentation he used, _finally_ got it from Adaptec,
+and started writing this driver. It is now at what I would consider
+a stable state - it runs our news server and is battered by SCSI
+requests 24 hours a day without dying. There are a few devices it
+reportedly doesn't like working with - those are being sorted out. Due
+to some unexpected equipment loans, I am able to support this at least
+for the time being.
+
+YOU MUST HAVE THE BIOS ENABLED OR THIS WILL NOT WORK. The BIOS extracts
+some configuration information that I cannot get to portably yet, as
+well as provides some self-tests which this driver does not attempt to
+duplicate.
+
+Scott's driver development is stalled for now, and after discussions
+with him, this is now officially out of "pre-alpha" status and into
+beta until the remaining device problems can be resolved. The latest
+patches can be obtained via anonymous ftp from ftp.cpsc.ucalgary.ca in
+/pub/systems/linux/aha274x.
+
+It supports EISA 274x, VL-bus 284x, and PCI 294x, either single or twin-bus
+cards (but not the second SCSI bus of twin cards - see aha274x.c), and supports
+disconnection, synchronous SCSI, and scatter-gather. Unlike previous
+versions, abort() and reset() are now implemented, and both hosts.c and
+aha274x.c should give a clean compile. Code is now present to detect parity
+errors, but has not been tested. Wide cards are not yet supported.
+
+I wrote this using a 1.0.9 kernel. Unfortunately, I'm getting tired of
+#ifdef'ing everything to handle two or three different evolutionary steps
+in the SCSI kernel code, so I've upgraded my system to 1.1.49, and will
+only leave in code to support versions from about 1.1.45 onward.
+
+Thanks to patches supplied by Mark Olson <molson@tricord.com>, this driver
+will now work with the 284x series (the VL-bus version of this card). The
+294x (PCI-bus) support is based on patches sent to me by Mark Olson and
+Alan Hourihane <alanh@fairlite.demon.co.uk>.
+
+Under protest, this driver is subject to the GPL - see the file
+COPYING for details.
+
+Thanks to the following people for bug fixes/code improvements (also
+thanks to the people who have sent me feedback):
+
+ "David F. Carlson" <dave@ee.rochester.edu>
+ Jimen Ching <jiching@wiliki.eng.hawaii.edu>
+ mday@artisoft.com (Matt Day)
+ "Dean W. Gehnert" <deang@ims.com>
+ Darcy Grant <darcy@cpsc.ucalgary.ca>
+ Alan Hourihane <alanh@fairlite.demon.co.uk>
+ isely@fncrd8.fnal.gov (Mike Isely)
+ Mike Jerger <jerger@ux1.cso.uiuc.edu>
+ tm@netcom.com (Toshiyasu Morita)
+ neal@interact.org (Neal Norwitz)
+ Mark Olson <molson@tricord.com>
+ map@europa.ecn.uoknor.edu (Michael A. Parker)
+ Thomas Scheunemann <thomas@dagobert.uni-duisburg.de>
+ Eric Youngdale <eric@aib.com>
+
+Special thanks to Drew Eckhardt <drew@kinglear.cs.Colorado.EDU> for
+fielding my questions about synchronous negotiation. Steffen Moeller
+<smoe0024@rz.uni-hildesheim.de> sent me installation instructions which
+were previously included in this README.
+
+David Pirie <pirie@cpsc.ucalgary.ca> was nice enough to loan me his
+2842 card for a week so I could track down one bug, as well as his
+CD-ROM drive later, and also thanks to Doug Fortune at Riley's Data Share
+in Calgary, who arranged a long-term loan of a 2842 board for further work.
+
+Many thanks to the fearless prerelease testers! Dean Gehnert has been
+building Slackware boot disks for the driver, which are available from
+ftp.cpsc.ucalgary.ca in /pub/systems/linux/aha274x/slackware_boot.
+
+Carl Riches <cgr@poplar1.cfr.washington.edu> has set up a mailing list
+for aic7xxx driver development. To subscribe, send a message to
+aic7770-list-request@poplar1.cfr.washington.edu with a message body of:
+
+ subscribe AIC7770-LIST <your name here, without the angle brackets>
+
+Please direct questions and discussions to that list instead of me. When
+sending bug reports, please include a description of your hardware, the
+release numbers displayed by the driver at boot time, and as accurate a
+facsimile of any error message you're mailing about.
+
+John Aycock
+aycock@cpsc.ucalgary.ca
+++ /dev/null
-/*
- * @(#)aha274x.c 1.29 94/10/29 jda
- *
- * Adaptec 274x device driver for Linux.
- * Copyright (c) 1994 The University of Calgary Department of Computer Science.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Sources include the Adaptec 1740 driver (aha1740.c), the
- * Ultrastor 24F driver (ultrastor.c), various Linux kernel
- * source, the Adaptec EISA config file (!adp7771.cfg), the
- * Adaptec AHA-2740A Series User's Guide, the Linux Kernel
- * Hacker's Guide, Writing a SCSI Device Driver for Linux,
- * the Adaptec 1542 driver (aha1542.c), the Adaptec EISA
- * overlay file (adp7770.ovl), the Adaptec AHA-2740 Series
- * Technical Reference Manual, the Adaptec AIC-7770 Data
- * Book, the ANSI SCSI specification, the ANSI SCSI-2
- * specification (draft 10c), ...
- *
- * On a twin-bus adapter card, channel B is ignored. Rationale:
- * it would greatly complicate the sequencer and host driver code,
- * and both busses are multiplexed on to the EISA bus anyway. So
- * I don't really see any technical advantage to supporting both.
- *
- * As well, multiple adapter card using the same IRQ level are
- * not supported. It doesn't make sense to configure the cards
- * this way from a performance standpoint. Not to mention that
- * the kernel would have to support two devices per registered IRQ.
- */
-
-#include <stdarg.h>
-#include <asm/io.h>
-#include <linux/string.h>
-#include <linux/kernel.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
-
-#include "../block/blk.h"
-#include "sd.h"
-#include "scsi.h"
-#include "hosts.h"
-#include "aha274x.h"
-
-/*
- * There should be a specific return value for this in scsi.h, but
- * it seems that most drivers ignore it.
- */
-#define DID_UNDERFLOW DID_ERROR
-
-/* EISA stuff */
-
-#define MINEISA 1
-#define MAXEISA 15
-#define SLOTBASE(x) ((x) << 12)
-
-#define MAXIRQ 15
-
-/* AIC-7770 offset definitions */
-
-#define O_MINREG(x) ((x) + 0xc00) /* i/o range to reserve */
-#define O_MAXREG(x) ((x) + 0xcbf)
-
-#define O_SCSISEQ(x) ((x) + 0xc00) /* scsi sequence control */
-#define O_SCSISIGI(x) ((x) + 0xc03) /* scsi control signal read */
-#define O_SCSISIGO(x) ((x) + 0xc03) /* scsi control signal write */
-#define O_SCSIID(x) ((x) + 0xc05) /* scsi id */
-#define O_SSTAT0(x) ((x) + 0xc0b) /* scsi status register 0 */
-#define O_CLRSINT1(x) ((x) + 0xc0c) /* clear scsi interrupt 1 */
-#define O_SSTAT1(x) ((x) + 0xc0c) /* scsi status register 1 */
-#define O_SELID(x) ((x) + 0xc19) /* [re]selection id */
-#define O_SBLKCTL(x) ((x) + 0xc1f) /* scsi block control */
-#define O_SEQCTL(x) ((x) + 0xc60) /* sequencer control */
-#define O_SEQRAM(x) ((x) + 0xc61) /* sequencer ram data */
-#define O_SEQADDR(x) ((x) + 0xc62) /* sequencer address (W) */
-#define O_BIDx(x) ((x) + 0xc80) /* board id */
-#define O_BCTL(x) ((x) + 0xc84) /* board control */
-#define O_HCNTRL(x) ((x) + 0xc87) /* host control */
-#define O_SCBPTR(x) ((x) + 0xc90) /* scb pointer */
-#define O_INTSTAT(x) ((x) + 0xc91) /* interrupt status */
-#define O_ERROR(x) ((x) + 0xc92) /* hard error */
-#define O_CLRINT(x) ((x) + 0xc92) /* clear interrupt status */
-#define O_SCBCNT(x) ((x) + 0xc9a) /* scb auto increment */
-#define O_QINFIFO(x) ((x) + 0xc9b) /* queue in fifo */
-#define O_QINCNT(x) ((x) + 0xc9c) /* queue in count */
-#define O_QOUTFIFO(x) ((x) + 0xc9d) /* queue out fifo */
-#define O_QOUTCNT(x) ((x) + 0xc9e) /* queue out count */
-#define O_SCBARRAY(x) ((x) + 0xca0) /* scb array start */
-
-/* host adapter offset definitions */
-
-#define HA_REJBYTE(x) ((x) + 0xc31) /* 1st message in byte */
-#define HA_MSG_FLAGS(x) ((x) + 0xc35) /* outgoing message flag */
-#define HA_MSG_LEN(x) ((x) + 0xc36) /* outgoing message length */
-#define HA_MSG_START(x) ((x) + 0xc37) /* outgoing message body */
-#define HA_ARG_1(x) ((x) + 0xc4c) /* sdtr <-> rate parameters */
-#define HA_ARG_2(x) ((x) + 0xc4d)
-#define HA_RETURN_1(x) ((x) + 0xc4c)
-#define HA_RETURN_2(x) ((x) + 0xc4d)
-#define HA_SIGSTATE(x) ((x) + 0xc4e) /* value in SCSISIGO */
-#define HA_NEEDSDTR(x) ((x) + 0xc4f) /* synchronous negotiation? */
-
-#define HA_SCSICONF(x) ((x) + 0xc5a) /* SCSI config register */
-#define HA_INTDEF(x) ((x) + 0xc5c) /* interrupt def'n register */
-#define HA_HOSTCONF(x) ((x) + 0xc5d) /* host config def'n register */
-
-/* debugging code */
-
-#define AHA274X_DEBUG
-
-/*
- * If a parity error occurs during a data transfer phase, run the
- * command to completion - it's easier that way - making a note
- * of the error condition in this location. This then will modify
- * a DID_OK status into a DID_PARITY one for the higher-level SCSI
- * code.
- */
-#define aha274x_parity(cmd) ((cmd)->SCp.Status)
-
-/*
- * Since the sequencer code DMAs the scatter-gather structures
- * directly from memory, we use this macro to assert that the
- * kernel structure hasn't changed.
- */
-#define SG_STRUCT_CHECK(sg) \
- ((char *)&(sg).address - (char *)&(sg) != 0 || \
- (char *)&(sg).length - (char *)&(sg) != 8 || \
- sizeof((sg).address) != 4 || \
- sizeof((sg).length) != 4 || \
- sizeof(sg) != 12)
-
-/*
- * "Static" structures. Note that these are NOT initialized
- * to zero inside the kernel - we have to initialize them all
- * explicitly.
- *
- * We support a maximum of one adapter card per IRQ level (see the
- * rationale for this above). On an interrupt, use the IRQ as an
- * index into aha274x_boards[] to locate the card information.
- */
-static struct Scsi_Host *aha274x_boards[MAXIRQ + 1];
-
-struct aha274x_host {
- int base; /* card base address */
- int startup; /* intr type check */
- volatile int unpause; /* value for HCNTRL */
- volatile Scsi_Cmnd *SCB_array[AHA274X_MAXSCB]; /* active commands */
-};
-
-struct aha274x_scb {
- unsigned char control;
- unsigned char target_channel_lun; /* 4/1/3 bits */
- unsigned char SG_segment_count;
- unsigned char SG_list_pointer[4];
- unsigned char SCSI_cmd_pointer[4];
- unsigned char SCSI_cmd_length;
- unsigned char RESERVED[2]; /* must be zero */
- unsigned char target_status;
- unsigned char residual_data_count[3];
- unsigned char residual_SG_segment_count;
- unsigned char data_pointer[4];
- unsigned char data_count[3];
-#if 0
- /*
- * No real point in transferring this to the
- * SCB registers.
- */
- unsigned char RESERVED[6];
-#endif
-};
-
-/*
- * NB. This table MUST be ordered shortest period first.
- */
-static struct {
- short period;
- short rate;
- char *english;
-} aha274x_synctab[] = {
- {100, 0, "10.0"},
- {125, 1, "8.0"},
- {150, 2, "6.67"},
- {175, 3, "5.7"},
- {200, 4, "5.0"},
- {225, 5, "4.4"},
- {250, 6, "4.0"},
- {275, 7, "3.6"}
-};
-
-static int aha274x_synctab_max =
- sizeof(aha274x_synctab) / sizeof(aha274x_synctab[0]);
-
-enum aha_type {
- T_NONE,
- T_274X,
- T_284X,
- T_MAX
-};
-
-#ifdef AHA274X_DEBUG
-
- extern int vsprintf(char *, const char *, va_list);
-
- static
- void debug(const char *fmt, ...)
- {
- va_list ap;
- char buf[256];
-
- va_start(ap, fmt);
- vsprintf(buf, fmt, ap);
- printk(buf);
- va_end(ap);
- }
-
- static
- void debug_config(enum aha_type type, int base)
- {
- int ioport2, ioport3, ioport4;
-
- static char *BRT[T_MAX][16] = {
- { }, /* T_NONE */
- {
- "2", "???", "???", "12", /* T_274X */
- "???", "???", "???", "28",
- "???", "???", "???", "44",
- "???", "???", "???", "60"
- },
- {
- "2", "4", "8", "12", /* T_284X */
- "16", "20", "24", "28",
- "32", "36", "40", "44",
- "48", "52", "56", "60"
- }
- };
- static int DFT[4] = {
- 0, 50, 75, 100
- };
- static int SST[4] = {
- 256, 128, 64, 32
- };
-
- ioport2 = inb(HA_HOSTCONF(base));
- ioport3 = inb(HA_SCSICONF(base));
- ioport4 = inb(HA_INTDEF(base));
-
- if (type == T_284X)
- printk("AHA284X AT SLOT %d:\n", base >> 12);
- else
- printk("AHA274X AT EISA SLOT %d:\n", base >> 12);
-
- printk(" irq %d\n"
- " bus release time %s bclks\n"
- " data fifo threshold %d%%\n",
- ioport4 & 0xf,
- BRT[type][(ioport2 >> 2) & 0xf],
- DFT[(ioport2 >> 6) & 0x3]);
-
- printk(" SCSI CHANNEL A:\n"
- " scsi id %d\n"
- " scsi bus parity check %sabled\n"
- " scsi selection timeout %d ms\n"
- " scsi bus reset at power-on %sabled\n",
- ioport3 & 0x7,
- (ioport3 & 0x20) ? "en" : "dis",
- SST[(ioport3 >> 3) & 0x3],
- (ioport3 & 0x40) ? "en" : "dis");
-
- if (type == T_274X) {
- printk(" scsi bus termination %sabled\n",
- (ioport3 & 0x80) ? "en" : "dis");
- }
- }
-
- static
- void debug_rate(int base, int rate)
- {
- int target = inb(O_SCSIID(base)) >> 4;
-
- if (rate) {
- printk("aha274x: target %d now synchronous at %sMb/s\n",
- target,
- aha274x_synctab[(rate >> 4) & 0x7].english);
- } else {
- printk("aha274x: target %d using asynchronous mode\n",
- target);
- }
- }
-
-#else
-
-# define debug(fmt, args...)
-# define debug_config(x)
-# define debug_rate(x,y)
-
-#endif AHA274X_DEBUG
-
-/*
- * XXX - these options apply unilaterally to _all_ 274x/284x
- * cards in the system. This should be fixed, but then,
- * does anyone really have more than one in a machine?
- */
-static int aha274x_extended = 0; /* extended translation on? */
-
-void aha274x_setup(char *s, int *dummy)
-{
- int i;
- char *p;
-
- static struct {
- char *name;
- int *flag;
- } options[] = {
- {"extended", &aha274x_extended},
- {NULL, NULL }
- };
-
- for (p = strtok(s, ","); p; p = strtok(NULL, ",")) {
- for (i = 0; options[i].name; i++)
- if (!strcmp(options[i].name, p))
- *(options[i].flag) = !0;
- }
-}
-
-static
-void aha274x_getscb(int base, struct aha274x_scb *scb)
-{
- /*
- * This is almost identical to aha274x_putscb().
- */
- outb(0x80, O_SCBCNT(base)); /* SCBAUTO */
-
- asm volatile("cld\n\t"
- "rep\n\t"
- "insb"
- : /* no output */
- :"D" (scb), "c" (sizeof(*scb)), "d" (O_SCBARRAY(base))
- :"di", "cx", "dx");
-
- outb(0, O_SCBCNT(base));
-}
-
-/*
- * How much data should be transferred for this SCSI command? Stop
- * at segment sg_last if it's a scatter-gather command so we can
- * compute underflow easily.
- */
-static
-unsigned aha274x_length(Scsi_Cmnd *cmd, int sg_last)
-{
- int i, segments;
- unsigned length;
- struct scatterlist *sg;
-
- segments = cmd->use_sg - sg_last;
- sg = (struct scatterlist *)cmd->buffer;
-
- if (cmd->use_sg) {
- for (i = length = 0;
- i < cmd->use_sg && i < segments;
- i++)
- {
- length += sg[i].length;
- }
- } else
- length = cmd->request_bufflen;
-
- return(length);
-}
-
-static
-void aha274x_sg_check(Scsi_Cmnd *cmd)
-{
- int i;
- struct scatterlist *sg = (struct scatterlist *)cmd->buffer;
-
- if (cmd->use_sg) {
- for (i = 0; i < cmd->use_sg; i++)
- if ((unsigned)sg[i].length > 0xffff)
- panic("aha274x_sg_check: s/g segment > 64k\n");
- }
-}
-
-static
-void aha274x_to_scsirate(unsigned char *rate,
- unsigned char transfer,
- unsigned char offset)
-{
- int i;
-
- transfer *= 4;
-
- for (i = 0; i < aha274x_synctab_max-1; i++) {
-
- if (transfer == aha274x_synctab[i].period) {
- *rate = (aha274x_synctab[i].rate << 4) | (offset & 0xf);
- return;
- }
-
- if (transfer > aha274x_synctab[i].period &&
- transfer < aha274x_synctab[i+1].period)
- {
- *rate = (aha274x_synctab[i+1].rate << 4) |
- (offset & 0xf);
- return;
- }
- }
- *rate = 0;
-}
-
-/*
- * Pause the sequencer and wait for it to actually stop - this
- * is important since the sequencer can disable pausing for critical
- * sections.
- */
-#define PAUSE_SEQUENCER(p) \
- do { \
- outb(0xe, O_HCNTRL(p->base)); /* IRQMS|PAUSE|INTEN */ \
- \
- while ((inb(O_HCNTRL(p->base)) & 0x4) == 0) \
- ; \
- } while (0)
-
-/*
- * Unpause the sequencer. Unremarkable, yet done often enough to
- * warrant an easy way to do it.
- */
-#define UNPAUSE_SEQUENCER(p) \
- outb(p->unpause, O_HCNTRL(p->base)) /* IRQMS|INTEN */
-
-/*
- * See comments in aha274x_loadram() wrt this.
- */
-#define RESTART_SEQUENCER(p) \
- do { \
- do { \
- outb(0x2, O_SEQCTL(p->base)); \
- } while (inw(O_SEQADDR(p->base)) != 0); \
- \
- UNPAUSE_SEQUENCER(p); \
- } while (0)
-
-/*
- * Since we declared this using SA_INTERRUPT, interrupts should
- * be disabled all through this function unless we say otherwise.
- */
-static
-void aha274x_isr(int irq, struct pt_regs * regs)
-{
- int base, intstat;
- struct aha274x_host *p;
-
- p = (struct aha274x_host *)aha274x_boards[irq]->hostdata;
- base = p->base;
-
- /*
- * Check the startup flag - if no commands have been queued,
- * we probably have the interrupt type set wrong. Reverse
- * the stored value and the active one in the host control
- * register.
- */
- if (p->startup) {
- p->unpause ^= 0x8;
- outb(inb(O_HCNTRL(p->base)) ^ 0x8, O_HCNTRL(p->base));
- return;
- }
-
- /*
- * Handle all the interrupt sources - especially for SCSI
- * interrupts, we won't get a second chance at them.
- */
- intstat = inb(O_INTSTAT(base));
-
- if (intstat & 0x8) { /* BRKADRINT */
-
- panic("aha274x_isr: brkadrint, error = 0x%x, seqaddr = 0x%x\n",
- inb(O_ERROR(base)), inw(O_SEQADDR(base)));
- }
-
- if (intstat & 0x4) { /* SCSIINT */
-
- int scbptr = inb(O_SCBPTR(base));
- int status = inb(O_SSTAT1(base));
- Scsi_Cmnd *cmd;
-
- cmd = (Scsi_Cmnd *)p->SCB_array[scbptr];
- if (!cmd) {
- printk("aha274x_isr: no command for scb (scsiint)\n");
- /*
- * Turn off the interrupt and set status
- * to zero, so that it falls through the
- * reset of the SCSIINT code.
- */
- outb(status, O_CLRSINT1(base));
- UNPAUSE_SEQUENCER(p);
- outb(0x4, O_CLRINT(base)); /* undocumented */
- status = 0;
- }
- p->SCB_array[scbptr] = NULL;
-
- /*
- * Only the SCSI Status 1 register has information
- * about exceptional conditions that we'd have a
- * SCSIINT about; anything in SSTAT0 will be handled
- * by the sequencer. Note that there can be multiple
- * bits set.
- */
- if (status & 0x80) { /* SELTO */
- /*
- * Hardware selection timer has expired. Turn
- * off SCSI selection sequence.
- */
- outb(0, O_SCSISEQ(base));
- cmd->result = DID_TIME_OUT << 16;
-
- /*
- * If there's an active message, it belongs to the
- * command that is getting punted - remove it.
- */
- outb(0, HA_MSG_FLAGS(base));
-
- /*
- * Shut off the offending interrupt sources, reset
- * the sequencer address to zero and unpause it,
- * then call the high-level SCSI completion routine.
- *
- * WARNING! This is a magic sequence! After many
- * hours of guesswork, turning off the SCSI interrupts
- * in CLRSINT? does NOT clear the SCSIINT bit in
- * INTSTAT. By writing to the (undocumented, unused
- * according to the AIC-7770 manual) third bit of
- * CLRINT, you can clear INTSTAT. But, if you do it
- * while the sequencer is paused, you get a BRKADRINT
- * with an Illegal Host Address status, so the
- * sequencer has to be restarted first.
- */
- outb(0x80, O_CLRSINT1(base)); /* CLRSELTIMO */
- RESTART_SEQUENCER(p);
-
- outb(0x4, O_CLRINT(base)); /* undocumented */
- cmd->scsi_done(cmd);
- }
-
- if (status & 0x4) { /* SCSIPERR */
- /*
- * A parity error has occurred during a data
- * transfer phase. Flag it and continue.
- */
- printk("aha274x: parity error on target %d, lun %d\n",
- cmd->target,
- cmd->lun);
- aha274x_parity(cmd) = DID_PARITY;
-
- /*
- * Clear interrupt and resume as above.
- */
- outb(0x4, O_CLRSINT1(base)); /* CLRSCSIPERR */
- UNPAUSE_SEQUENCER(p);
-
- outb(0x4, O_CLRINT(base)); /* undocumented */
- }
-
- if ((status & (0x8|0x4)) == 0 && status) {
- /*
- * We don't know what's going on. Turn off the
- * interrupt source and try to continue.
- */
- printk("aha274x_isr: sstat1 = 0x%x\n", status);
- outb(status, O_CLRSINT1(base));
- UNPAUSE_SEQUENCER(p);
- outb(0x4, O_CLRINT(base)); /* undocumented */
- }
- }
-
- if (intstat & 0x2) { /* CMDCMPLT */
-
- int complete, old_scbptr;
- struct aha274x_scb scb;
- unsigned actual;
- Scsi_Cmnd *cmd;
-
- /*
- * The sequencer will continue running when it
- * issues this interrupt. There may be >1 commands
- * finished, so loop until we've processed them all.
- */
- do {
- complete = inb(O_QOUTFIFO(base));
-
- cmd = (Scsi_Cmnd *)p->SCB_array[complete];
- if (!cmd) {
- printk("aha274x warning: "
- "no command for scb (cmdcmplt)\n");
- continue;
- }
- p->SCB_array[complete] = NULL;
-
- PAUSE_SEQUENCER(p);
-
- /*
- * After pausing the sequencer (and waiting
- * for it to stop), save its SCB pointer, then
- * write in our completed one and read the SCB
- * registers. Afterwards, restore the saved
- * pointer, unpause the sequencer and call the
- * higher-level completion function - unpause
- * first since we have no idea how long done()
- * will take.
- */
- old_scbptr = inb(O_SCBPTR(base));
- outb(complete, O_SCBPTR(base));
-
- aha274x_getscb(base, &scb);
- outb(old_scbptr, O_SCBPTR(base));
-
- UNPAUSE_SEQUENCER(p);
-
- cmd->result = scb.target_status |
- (aha274x_parity(cmd) << 16);
-
- /*
- * Did we underflow? At this time, there's only
- * one other driver that bothers to check for this,
- * and cmd->underflow seems to be set rather half-
- * heartedly in the higher-level SCSI code.
- */
- actual = aha274x_length(cmd,
- scb.residual_SG_segment_count);
-
- actual -= ((scb.residual_data_count[2] << 16) |
- (scb.residual_data_count[1] << 8) |
- (scb.residual_data_count[0]));
-
- if (actual < cmd->underflow) {
- printk("aha274x: target %d underflow - "
- "wanted (at least) %u, got %u\n",
- cmd->target, cmd->underflow, actual);
-
- cmd->result = scb.target_status |
- (DID_UNDERFLOW << 16);
- }
-
- cmd->scsi_done(cmd);
-
- /*
- * Clear interrupt status before checking
- * the output queue again. This eliminates
- * a race condition whereby a command could
- * complete between the queue poll and the
- * interrupt clearing, so notification of the
- * command being complete never made it back
- * up to the kernel.
- */
- outb(0x2, O_CLRINT(base)); /* CLRCMDINT */
-
- } while (inb(O_QOUTCNT(base)));
- }
-
- if (intstat & 0x1) { /* SEQINT */
-
- unsigned char transfer, offset, rate;
-
- /*
- * Although the sequencer is paused immediately on
- * a SEQINT, an interrupt for a SCSIINT or a CMDCMPLT
- * condition will have unpaused the sequencer before
- * this point.
- */
- PAUSE_SEQUENCER(p);
-
- switch (intstat & 0xf0) {
- case 0x00:
- panic("aha274x_isr: unknown scsi bus phase\n");
- case 0x10:
- debug("aha274x_isr warning: "
- "issuing message reject, 1st byte 0x%x\n",
- inb(HA_REJBYTE(base)));
- break;
- case 0x20:
- panic("aha274x_isr: reconnecting target %d "
- "didn't issue IDENTIFY message\n",
- (inb(O_SELID(base)) >> 4) & 0xf);
- case 0x30:
- debug("aha274x_isr: sequencer couldn't find match "
- "for reconnecting target %d - issuing ABORT\n",
- (inb(O_SELID(base)) >> 4) & 0xf);
- break;
- case 0x40:
- transfer = inb(HA_ARG_1(base));
- offset = inb(HA_ARG_2(base));
- aha274x_to_scsirate(&rate, transfer, offset);
- outb(rate, HA_RETURN_1(base));
- debug_rate(base, rate);
- break;
- default:
- debug("aha274x_isr: seqint, "
- "intstat = 0x%x, scsisigi = 0x%x\n",
- intstat, inb(O_SCSISIGI(base)));
- break;
- }
-
- outb(0x1, O_CLRINT(base)); /* CLRSEQINT */
- UNPAUSE_SEQUENCER(p);
- }
-}
-
-/*
- * Probing for EISA boards: it looks like the first two bytes
- * are a manufacturer code - three characters, five bits each:
- *
- * BYTE 0 BYTE 1 BYTE 2 BYTE 3
- * ?1111122 22233333 PPPPPPPP RRRRRRRR
- *
- * The characters are baselined off ASCII '@', so add that value
- * to each to get the real ASCII code for it. The next two bytes
- * appear to be a product and revision number, probably vendor-
- * specific. This is what is being searched for at each port,
- * and what should probably correspond to the ID= field in the
- * ECU's .cfg file for the card - if your card is not detected,
- * make sure your signature is listed in the array.
- *
- * The fourth byte's lowest bit seems to be an enabled/disabled
- * flag (rest of the bits are reserved?).
- */
-
-static
-enum aha_type aha274x_probe(int slot, int s_base)
-{
- int i;
- unsigned char buf[4];
-
- static struct {
- int n;
- unsigned char signature[sizeof(buf)];
- enum aha_type type;
- } S[] = {
- {4, { 0x04, 0x90, 0x77, 0x71 }, T_274X}, /* host adapter 274x */
- {4, { 0x04, 0x90, 0x77, 0x70 }, T_274X}, /* motherboard 274x */
- {4, { 0x04, 0x90, 0x77, 0x56 }, T_284X}, /* 284x, BIOS enabled */
- };
-
- for (i = 0; i < sizeof(buf); i++) {
- /*
- * The VL-bus cards need to be primed by
- * writing before a signature check.
- */
- outb(0x80 + i, s_base);
- buf[i] = inb(s_base + i);
- }
-
- for (i = 0; i < sizeof(S)/sizeof(S[0]); i++) {
- if (!memcmp(buf, S[i].signature, S[i].n)) {
- /*
- * Signature match on enabled card?
- */
- if (inb(s_base + 4) & 1)
- return(S[i].type);
- printk("aha274x disabled at slot %d, ignored\n", slot);
- }
- }
- return(T_NONE);
-}
-
-/*
- * Return ' ' for plain 274x, 'T' for twin-channel, 'W' for
- * wide channel, '?' for anything else.
- */
-
-static
-char aha274x_type(int base)
-{
- /*
- * The AIC-7770 can be wired so that, on chip reset,
- * the SCSI Block Control register indicates how many
- * busses the chip is configured for.
- */
- switch (inb(O_SBLKCTL(base))) {
- case 0:
- return(' ');
- case 2:
- return('W');
- case 8:
- return('T');
- default:
- printk("aha274x has unknown bus configuration\n");
- return('?');
- }
-}
-
-static
-void aha274x_loadram(int base)
-{
- static unsigned char seqprog[] = {
- /*
- * Each sequencer instruction is 29 bits
- * long (fill in the excess with zeroes)
- * and has to be loaded from least -> most
- * significant byte, so this table has the
- * byte ordering reversed.
- */
-# include "aha274x_seq.h"
- };
-
- /*
- * When the AIC-7770 is paused (as on chip reset), the
- * sequencer address can be altered and a sequencer
- * program can be loaded by writing it, byte by byte, to
- * the sequencer RAM port - the Adaptec documentation
- * recommends using REP OUTSB to do this, hence the inline
- * assembly. Since the address autoincrements as we load
- * the program, reset it back to zero afterward. Disable
- * sequencer RAM parity error detection while loading, and
- * make sure the LOADRAM bit is enabled for loading.
- */
- outb(0x83, O_SEQCTL(base)); /* PERRORDIS|SEQRESET|LOADRAM */
-
- asm volatile("cld\n\t"
- "rep\n\t"
- "outsb"
- : /* no output */
- :"S" (seqprog), "c" (sizeof(seqprog)), "d" (O_SEQRAM(base))
- :"si", "cx", "dx");
-
- /*
- * WARNING! This is a magic sequence! After extensive
- * experimentation, it seems that you MUST turn off the
- * LOADRAM bit before you play with SEQADDR again, else
- * you will end up with parity errors being flagged on
- * your sequencer program. (You would also think that
- * turning off LOADRAM and setting SEQRESET to reset the
- * address to zero would work, but you need to do it twice
- * for it to take effect on the address. Timing problem?)
- */
- outb(0, O_SEQCTL(base));
- do {
- /*
- * Actually, reset it until
- * the address shows up as
- * zero just to be safe..
- */
- outb(0x2, O_SEQCTL(base)); /* SEQRESET */
-
- } while (inw(O_SEQADDR(base)) != 0);
-}
-
-static
-int aha274x_register(Scsi_Host_Template *template,
- enum aha_type type,
- int base)
-{
- int i, irq, scsi_id;
- struct Scsi_Host *host;
- struct aha274x_host *p;
-
- /*
- * Give the AIC-7770 a reset - reading the 274x's registers
- * returns zeroes unless you do. This forces a pause of the
- * Sequencer.
- */
- outb(1, O_HCNTRL(base)); /* CHIPRST */
-
- /*
- * The IRQ level in i/o port 4 maps directly onto the real
- * IRQ number. If it's ok, register it with the kernel.
- *
- * NB. the Adaptec documentation says the IRQ number is only
- * in the lower four bits; the ECU information shows the
- * high bit being used as well. Which is correct?
- */
- irq = inb(HA_INTDEF(base)) & 0xf;
- if (irq < 9 || irq > 15) {
- printk("aha274x uses unsupported IRQ level, ignoring\n");
- return(0);
- }
-
- /*
- * Lock out other contenders for our i/o space.
- */
- request_region(O_MINREG(base), O_MAXREG(base)-O_MINREG(base), "aha27x");
-
- /*
- * Any card-type-specific adjustments before we register
- * the scsi host(s).
- */
-
- scsi_id = inb(HA_SCSICONF(base)) & 0x7;
-
- switch (aha274x_type(base)) {
- case 'T':
- printk("aha274x warning: ignoring channel B of 274x-twin\n");
- break;
- case ' ':
- break;
- default:
- printk("aha274x is an unsupported type, ignoring\n");
- return(0);
- }
-
- /*
- * Before registry, make sure that the offsets of the
- * struct scatterlist are what the sequencer will expect,
- * otherwise disable scatter-gather altogether until someone
- * can fix it. This is important since the sequencer will
- * DMA elements of the SG array in while executing commands.
- */
- if (template->sg_tablesize != SG_NONE) {
- struct scatterlist sg;
-
- if (SG_STRUCT_CHECK(sg)) {
- printk("aha274x warning: kernel scatter-gather "
- "structures changed, disabling it\n");
- template->sg_tablesize = SG_NONE;
- }
- }
-
- /*
- * Register each "host" and fill in the returned Scsi_Host
- * structure as best we can. Some of the parameters aren't
- * really relevant for EISA, and none of the high-level SCSI
- * code looks at it anyway.. why are the fields there? Also
- * save the pointer so that we can find the information when
- * an IRQ is triggered.
- */
- host = scsi_register(template, sizeof(struct aha274x_host));
- host->this_id = scsi_id;
- host->irq = irq;
-
- aha274x_boards[irq] = host;
-
- p = (struct aha274x_host *)host->hostdata;
- for (i = 0; i < AHA274X_MAXSCB; i++)
- p->SCB_array[i] = NULL;
- p->base = base;
-
- /*
- * The interrupt trigger is different depending
- * on whether the card is EISA or VL-bus - sometimes.
- * The startup variable will be cleared once the first
- * command is queued, and is checked in the isr to
- * try and detect when the interrupt type is set
- * incorrectly, triggering an interrupt immediately.
- */
- p->unpause = (type != T_274X ? 0x2 : 0xa);
- p->startup = !0;
-
- /*
- * Register IRQ with the kernel _after_ the host information
- * is set up, in case we take an interrupt right away, due to
- * the interrupt type being set wrong.
- */
- if (request_irq(irq, aha274x_isr, SA_INTERRUPT, "AHA274x/284x")) {
- printk("aha274x couldn't register irq %d, ignoring\n", irq);
- return(0);
- }
-
- /*
- * A reminder until this can be detected automatically.
- */
- printk("aha274x: extended translation %sabled\n",
- aha274x_extended ? "en" : "dis");
-
- /*
- * Print out debugging information before re-enabling
- * the card - a lot of registers on it can't be read
- * when the sequencer is active.
- */
- debug_config(type, base);
-
- /*
- * Load the sequencer program, then re-enable the board -
- * resetting the AIC-7770 disables it, leaving the lights
- * on with nobody home.
- */
- aha274x_loadram(base);
- outb(1, O_BCTL(base)); /* ENABLE */
-
- /*
- * Set the host adapter registers to indicate that synchronous
- * negotiation should be attempted the first time the targets
- * are communicated with. Also initialize the active message
- * flag to indicate that there is no message.
- */
- outb(0xff, HA_NEEDSDTR(base));
- outb(0, HA_MSG_FLAGS(base));
-
- /*
- * Unpause the sequencer before returning and enable
- * interrupts - we shouldn't get any until the first
- * command is sent to us by the high-level SCSI code.
- */
- UNPAUSE_SEQUENCER(p);
- return(1);
-}
-
-int aha274x_detect(Scsi_Host_Template *template)
-{
- enum aha_type type;
- int found = 0, slot, base;
-
- for (slot = MINEISA; slot <= MAXEISA; slot++) {
-
- base = SLOTBASE(slot);
-
- if (check_region(O_MINREG(base),
- O_MAXREG(base)-O_MINREG(base)))
- {
- /*
- * Some other driver has staked a
- * claim to this i/o region already.
- */
- continue;
- }
-
- type = aha274x_probe(slot, O_BIDx(base));
-
- if (type != T_NONE) {
- /*
- * We "find" a 274x if we locate the card
- * signature and we can set it up and register
- * it with the kernel without incident.
- */
- found += aha274x_register(template, type, base);
- }
- }
- template->name = (char *)aha274x_info(NULL);
- return(found);
-}
-
-const char *aha274x_info(struct Scsi_Host * shost)
-{
- return("Adaptec AHA274x/284x (EISA/VL-bus -> Fast SCSI) "
- AHA274X_SEQ_VERSION "/"
- AHA274X_H_VERSION "/"
- "1.29");
-}
-
-int aha274x_command(Scsi_Cmnd *cmd)
-{
- /*
- * This is a relic of non-interrupt-driven SCSI
- * drivers. With the can_queue variable set, this
- * should never be called.
- */
- panic("aha274x_command was called\n");
-}
-
-static
-void aha274x_buildscb(struct aha274x_host *p,
- Scsi_Cmnd *cmd,
- struct aha274x_scb *scb)
-{
- void *addr;
- unsigned length;
-
- memset(scb, 0, sizeof(*scb));
-
- /*
- * NB. channel selection (bit 3) is always zero.
- */
- scb->target_channel_lun = ((cmd->target << 4) & 0xf0) |
- (cmd->lun & 0x7);
-
- /*
- * The interpretation of request_buffer and request_bufflen
- * changes depending on whether or not use_sg is zero; a
- * non-zero use_sg indicates the number of elements in the
- * scatter-gather array.
- *
- * The AIC-7770 can't support transfers of any sort larger
- * than 2^24 (three-byte count) without backflips. For what
- * the kernel is doing, this shouldn't occur. I hope.
- */
- length = aha274x_length(cmd, 0);
-
- /*
- * The sequencer code cannot yet handle scatter-gather segments
- * larger than 64k (two-byte length). The 1.1.x kernels, however,
- * have a four-byte length field in the struct scatterlist, so
- * make sure we don't exceed 64k on these kernels for now.
- */
- aha274x_sg_check(cmd);
-
- if (length > 0xffffff) {
- panic("aha274x_buildscb: can't transfer > 2^24 - 1 bytes\n");
- }
-
- /*
- * XXX - this relies on the host data being stored in a
- * little-endian format.
- */
- addr = cmd->cmnd;
- scb->SCSI_cmd_length = cmd->cmd_len;
- memcpy(scb->SCSI_cmd_pointer, &addr, sizeof(scb->SCSI_cmd_pointer));
-
- if (cmd->use_sg) {
-#if 0
- debug("aha274x_buildscb: SG used, %d segments, length %u\n",
- cmd->use_sg,
- length);
-#endif
- scb->SG_segment_count = cmd->use_sg;
- memcpy(scb->SG_list_pointer,
- &cmd->request_buffer,
- sizeof(scb->SG_list_pointer));
- } else {
- scb->SG_segment_count = 0;
- memcpy(scb->data_pointer,
- &cmd->request_buffer,
- sizeof(scb->data_pointer));
- memcpy(scb->data_count,
- &cmd->request_bufflen,
- sizeof(scb->data_count));
- }
-}
-
-static
-void aha274x_putscb(int base, struct aha274x_scb *scb)
-{
- /*
- * By turning on the SCB auto increment, any reference
- * to the SCB I/O space postincrements the SCB address
- * we're looking at. So turn this on and dump the relevant
- * portion of the SCB to the card.
- */
- outb(0x80, O_SCBCNT(base)); /* SCBAUTO */
-
- asm volatile("cld\n\t"
- "rep\n\t"
- "outsb"
- : /* no output */
- :"S" (scb), "c" (sizeof(*scb)), "d" (O_SCBARRAY(base))
- :"si", "cx", "dx");
-
- outb(0, O_SCBCNT(base));
-}
-
-int aha274x_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
-{
- long flags;
- int empty, old_scbptr;
- struct aha274x_host *p;
- struct aha274x_scb scb;
-
-#if 0
- debug("aha274x_queue: cmd 0x%x (size %u), target %d, lun %d\n",
- cmd->cmnd[0],
- cmd->cmd_len,
- cmd->target,
- cmd->lun);
-#endif
-
- p = (struct aha274x_host *)cmd->host->hostdata;
-
- /*
- * Construct the SCB beforehand, so the sequencer is
- * paused a minimal amount of time.
- */
- aha274x_buildscb(p, cmd, &scb);
-
- /*
- * Clear the startup flag - we can now legitimately
- * expect interrupts.
- */
- p->startup = 0;
-
- /*
- * This is a critical section, since we don't want the
- * interrupt routine mucking with the host data or the
- * card. Since the kernel documentation is vague on
- * whether or not we are in a cli/sti pair already, save
- * the flags to be on the safe side.
- */
- save_flags(flags);
- cli();
-
- /*
- * Find a free slot in the SCB array to load this command
- * into. Since can_queue is set to AHA274X_MAXSCB, we
- * should always find one.
- */
- for (empty = 0; empty < AHA274X_MAXSCB; empty++)
- if (!p->SCB_array[empty])
- break;
- if (empty == AHA274X_MAXSCB)
- panic("aha274x_queue: couldn't find a free scb\n");
-
- /*
- * Pause the sequencer so we can play with its registers -
- * wait for it to acknowledge the pause.
- *
- * XXX - should the interrupts be left on while doing this?
- */
- PAUSE_SEQUENCER(p);
-
- /*
- * Save the SCB pointer and put our own pointer in - this
- * selects one of the four banks of SCB registers. Load
- * the SCB, then write its pointer into the queue in FIFO
- * and restore the saved SCB pointer.
- */
- old_scbptr = inb(O_SCBPTR(p->base));
- outb(empty, O_SCBPTR(p->base));
-
- aha274x_putscb(p->base, &scb);
-
- outb(empty, O_QINFIFO(p->base));
- outb(old_scbptr, O_SCBPTR(p->base));
-
- /*
- * Make sure the Scsi_Cmnd pointer is saved, the struct it
- * points to is set up properly, and the parity error flag
- * is reset, then unpause the sequencer and watch the fun
- * begin.
- */
- cmd->scsi_done = fn;
- p->SCB_array[empty] = cmd;
- aha274x_parity(cmd) = DID_OK;
-
- UNPAUSE_SEQUENCER(p);
-
- restore_flags(flags);
- return(0);
-}
-
-/* return values from aha274x_kill */
-
-enum k_state {
- k_ok, /* scb found and message sent */
- k_busy, /* message already present */
- k_absent, /* couldn't locate scb */
- k_disconnect, /* scb found, but disconnected */
-};
-
-/*
- * This must be called with interrupts disabled - it's going to
- * be messing around with the host data, and an interrupt being
- * fielded in the middle could get ugly.
- *
- * Since so much of the abort and reset code is shared, this
- * function performs more magic than it really should. If the
- * command completes ok, then it will call scsi_done with the
- * result code passed in. The unpause parameter controls whether
- * or not the sequencer gets unpaused - the reset function, for
- * instance, may want to do something more aggressive.
- *
- * Note that the command is checked for in our SCB_array first
- * before the sequencer is paused, so if k_absent is returned,
- * then the sequencer is NOT paused.
- */
-
-static
-enum k_state aha274x_kill(Scsi_Cmnd *cmd, unsigned char message,
- unsigned int result, int unpause)
-{
- struct aha274x_host *p;
- int i, scb, found, queued;
- unsigned char scbsave[AHA274X_MAXSCB];
-
- p = (struct aha274x_host *)cmd->host->hostdata;
-
- /*
- * If we can't find the command, assume it just completed
- * and shrug it away.
- */
- for (scb = 0; scb < AHA274X_MAXSCB; scb++)
- if (p->SCB_array[scb] == cmd)
- break;
-
- if (scb == AHA274X_MAXSCB)
- return(k_absent);
-
- PAUSE_SEQUENCER(p);
-
- /*
- * This is the best case, really. Check to see if the
- * command is still in the sequencer's input queue. If
- * so, simply remove it. Reload the queue afterward.
- */
- queued = inb(O_QINCNT(p->base));
-
- for (i = found = 0; i < queued; i++) {
- scbsave[i] = inb(O_QINFIFO(p->base));
-
- if (scbsave[i] == scb) {
- found = 1;
- i -= 1;
- }
- }
-
- queued -= found;
- for (i = 0; i < queued; i++)
- outb(scbsave[i], O_QINFIFO(p->base));
-
- if (found)
- goto complete;
-
- /*
- * Check the current SCB bank. If it's not the one belonging
- * to the command we want to kill, assume that the command
- * is disconnected. It's rather a pain to force a reconnect
- * and send a message to the target, so we abdicate responsibility
- * in this case.
- */
- if (inb(O_SCBPTR(p->base)) != scb) {
- if (unpause)
- UNPAUSE_SEQUENCER(p);
- return(k_disconnect);
- }
-
- /*
- * Presumably at this point our target command is active. Check
- * to see if there's a message already in effect. If not, place
- * our message in and assert ATN so the target goes into MESSAGE
- * OUT phase.
- */
- if (inb(HA_MSG_FLAGS(p->base)) & 0x80) {
- if (unpause)
- UNPAUSE_SEQUENCER(p);
- return(k_busy);
- }
-
- outb(0x80, HA_MSG_FLAGS(p->base)); /* active message */
- outb(1, HA_MSG_LEN(p->base)); /* length = 1 */
- outb(message, HA_MSG_START(p->base)); /* message body */
-
- /*
- * Assert ATN. Use the value of SCSISIGO saved by the
- * sequencer code so we don't alter its contents radically
- * in the middle of something critical.
- */
- outb(inb(HA_SIGSTATE(p->base)) | 0x10, O_SCSISIGO(p->base));
-
- /*
- * The command has been killed. Do the bookkeeping, unpause
- * the sequencer, and notify the higher-level SCSI code.
- */
-complete:
- p->SCB_array[scb] = NULL;
- if (unpause)
- UNPAUSE_SEQUENCER(p);
-
- cmd->result = result << 16;
- cmd->scsi_done(cmd);
- return(k_ok);
-}
-
-int aha274x_abort(Scsi_Cmnd *cmd)
-{
- int rv;
- long flags;
-
- save_flags(flags);
- cli();
-
- switch (aha274x_kill(cmd, ABORT, DID_ABORT, !0)) {
- case k_ok: rv = SCSI_ABORT_SUCCESS; break;
- case k_busy: rv = SCSI_ABORT_BUSY; break;
- case k_absent: rv = SCSI_ABORT_NOT_RUNNING; break;
- case k_disconnect: rv = SCSI_ABORT_SNOOZE; break;
- default:
- panic("aha274x_do_abort: internal error\n");
- }
-
- restore_flags(flags);
- return(rv);
-}
-
-/*
- * Resetting the bus always succeeds - is has to, otherwise the
- * kernel will panic! Try a surgical technique - sending a BUS
- * DEVICE RESET message - on the offending target before pulling
- * the SCSI bus reset line.
- */
-
-int aha274x_reset(Scsi_Cmnd *cmd)
-{
- int i;
- long flags;
- Scsi_Cmnd *reset;
- struct aha274x_host *p;
-
- p = (struct aha274x_host *)cmd->host->hostdata;
- save_flags(flags);
- cli();
-
- switch (aha274x_kill(cmd, BUS_DEVICE_RESET, DID_RESET, 0)) {
-
- case k_ok:
- /*
- * The RESET message was sent to the target
- * with no problems. Flag that target as
- * needing a SDTR negotiation on the next
- * connection and restart the sequencer.
- */
- outb((1 << cmd->target), HA_NEEDSDTR(p->base));
- UNPAUSE_SEQUENCER(p);
- break;
-
- case k_absent:
- /*
- * The sequencer will not be paused if aha274x_kill()
- * couldn't find the command.
- */
- PAUSE_SEQUENCER(p);
- /* falls through */
-
- case k_busy:
- case k_disconnect:
- /*
- * Do a hard reset of the SCSI bus. According to the
- * SCSI-2 draft specification, reset has to be asserted
- * for at least 25us. I'm invoking the kernel delay
- * function for 30us since I'm not totally trusting of
- * the busy loop timing.
- *
- * XXX - I'm not convinced this works. I tried resetting
- * the bus before, trying to get the devices on the
- * bus to revert to asynchronous transfer, and it
- * never seemed to work.
- */
- debug("aha274x: attempting to reset scsi bus and card\n");
-
- outb(1, O_SCSISEQ(p->base)); /* SCSIRSTO */
- udelay(30);
- outb(0, O_SCSISEQ(p->base)); /* !SCSIRSTO */
-
- outb(0xff, HA_NEEDSDTR(p->base));
- UNPAUSE_SEQUENCER(p);
-
- /*
- * Locate the command and return a "reset" status
- * for it. This is not completely correct and will
- * probably return to haunt me later.
- */
- for (i = 0; i < AHA274X_MAXSCB; i++) {
- if (cmd == p->SCB_array[i]) {
- reset = (Scsi_Cmnd *)p->SCB_array[i];
- p->SCB_array[i] = NULL;
- reset->result = DID_RESET << 16;
- reset->scsi_done(reset);
- break;
- }
- }
- break;
-
- default:
- panic("aha274x_reset: internal error\n");
- }
-
- restore_flags(flags);
- return(SCSI_RESET_SUCCESS);
-}
-
-int aha274x_biosparam(Disk *disk, int devno, int geom[])
-{
- int heads, sectors, cylinders;
-
- /*
- * XXX - if I could portably find the card's configuration
- * information, then this could be autodetected instead
- * of left to a boot-time switch.
- */
- heads = 64;
- sectors = 32;
- cylinders = disk->capacity / (heads * sectors);
-
- if (aha274x_extended && cylinders > 1024) {
- heads = 255;
- sectors = 63;
- cylinders = disk->capacity / (255 * 63);
- }
-
- geom[0] = heads;
- geom[1] = sectors;
- geom[2] = cylinders;
-
- return(0);
-}
-
+++ /dev/null
-/* @(#)aha274x.h 1.11 94/09/06 jda */
-
-/*
- * Adaptec 274x device driver for Linux.
- * Copyright (c) 1994 The University of Calgary Department of Computer Science.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef aha274x_h
-#define aha274x_h
-
-#define AHA274X_MAXSCB 4
-#define AHA274X_H_VERSION "1.11"
-
-/*
- * Scsi_Host_Template (see hosts.h) for 274x - some fields
- * to do with card config are filled in after the card is
- * detected.
- */
-#define AHA274X { \
- NULL, \
- NULL, \
- NULL, \
- aha274x_detect, \
- NULL, \
- aha274x_info, \
- aha274x_command, \
- aha274x_queue, \
- aha274x_abort, \
- aha274x_reset, \
- NULL, \
- aha274x_biosparam, \
- AHA274X_MAXSCB, /* max simultaneous cmds */\
- -1, /* scsi id of host adapter */\
- SG_ALL, /* max scatter-gather cmds */\
- 1, /* cmds per lun (linked cmds) */\
- 0, /* number of 274x's present */\
- 0, /* no memory DMA restrictions */\
- DISABLE_CLUSTERING \
-}
-
-extern int aha274x_queue(Scsi_Cmnd *, void (*)(Scsi_Cmnd *));
-extern int aha274x_biosparam(Disk *, int, int[]);
-extern int aha274x_detect(Scsi_Host_Template *);
-extern int aha274x_command(Scsi_Cmnd *);
-extern int aha274x_abort(Scsi_Cmnd *);
-extern int aha274x_reset(Scsi_Cmnd *);
-extern const char *aha274x_info(struct Scsi_Host *);
-
-#endif
+++ /dev/null
-# @(#)aha274x.seq 1.28 94/10/04 jda
-#
-# Adaptec 274x device driver for Linux.
-# Copyright (c) 1994 The University of Calgary Department of Computer Science.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-VERSION AHA274X_SEQ_VERSION 1.28
-
-MAXSCB = 4
-
-SCSISEQ = 0x00
-SXFRCTL0 = 0x01
-SXFRCTL1 = 0x02
-SCSISIGI = 0x03
-SCSISIGO = 0x03
-SCSIRATE = 0x04
-SCSIID = 0x05
-SCSIDATL = 0x06
-STCNT = 0x08
-STCNT+0 = 0x08
-STCNT+1 = 0x09
-STCNT+2 = 0x0a
-SSTAT0 = 0x0b
-CLRSINT1 = 0x0c
-SSTAT1 = 0x0c
-SIMODE1 = 0x11
-SCSIBUSL = 0x12
-SHADDR = 0x14
-SELID = 0x19
-SBLKCTL = 0x1f
-SEQCTL = 0x60
-A = 0x64 # == ACCUM
-SINDEX = 0x65
-DINDEX = 0x66
-ALLZEROS = 0x6a
-NONE = 0x6a
-SINDIR = 0x6c
-DINDIR = 0x6d
-FUNCTION1 = 0x6e
-HADDR = 0x88
-HCNT = 0x8c
-HCNT+0 = 0x8c
-HCNT+1 = 0x8d
-HCNT+2 = 0x8e
-SCBPTR = 0x90
-INTSTAT = 0x91
-DFCNTRL = 0x93
-DFSTATUS = 0x94
-DFDAT = 0x99
-QINFIFO = 0x9b
-QINCNT = 0x9c
-QOUTFIFO = 0x9d
-
-SCSICONF = 0x5a
-
-# The two reserved bytes at SCBARRAY+1[23] are expected to be set to
-# zero, and the reserved bit in SCBARRAY+0 is used as an internal flag
-# to indicate whether or not to reload scatter-gather parameters after
-# a disconnect.
-#
-SCBARRAY+0 = 0xa0
-SCBARRAY+1 = 0xa1
-SCBARRAY+2 = 0xa2
-SCBARRAY+3 = 0xa3
-SCBARRAY+7 = 0xa7
-SCBARRAY+11 = 0xab
-SCBARRAY+14 = 0xae
-SCBARRAY+15 = 0xaf
-SCBARRAY+16 = 0xb0
-SCBARRAY+17 = 0xb1
-SCBARRAY+18 = 0xb2
-SCBARRAY+19 = 0xb3
-SCBARRAY+20 = 0xb4
-SCBARRAY+21 = 0xb5
-SCBARRAY+22 = 0xb6
-SCBARRAY+23 = 0xb7
-SCBARRAY+24 = 0xb8
-SCBARRAY+25 = 0xb9
-
-SIGNAL_0 = 0x01 # unknown scsi bus phase
-SIGNAL_1 = 0x11 # message reject
-SIGNAL_2 = 0x21 # no IDENTIFY after reconnect
-SIGNAL_3 = 0x31 # no cmd match for reconnect
-SIGNAL_4 = 0x41 # SDTR -> SCSIRATE conversion
-
-# The host adapter card (at least the BIOS) uses 20-2f for SCSI
-# device information, 32-33 and 5a-5f as well. Since we don't support
-# wide or twin-bus SCSI, 28-2f can be reclaimed. As it turns out, the
-# BIOS trashes 20-27 anyway, writing the synchronous negotiation results
-# on top of the BIOS values, so we re-use those for our per-target
-# scratchspace (actually a value that can be copied directly into
-# SCSIRATE). This implies, since we can't get the BIOS config values,
-# that all targets will be negotiated with for synchronous transfer.
-# NEEDSDTR has one bit per target indicating if an SDTR message is
-# needed for that device - this will be set initially, as well as
-# after a bus reset condition.
-#
-# The high bit of DROPATN is set if ATN should be dropped before the ACK
-# when outb is called. REJBYTE contains the first byte of a MESSAGE IN
-# message, so the driver can report an intelligible error if a message is
-# rejected.
-#
-# RESELECT's high bit is true if we are currently handling a reselect;
-# its next-highest bit is true ONLY IF we've seen an IDENTIFY message
-# from the reselecting target. If we haven't had IDENTIFY, then we have
-# no idea what the lun is, and we can't select the right SCB register
-# bank, so force a kernel panic if the target attempts a data in/out or
-# command phase instead of corrupting something.
-#
-# Note that SG_NEXT occupies four bytes.
-#
-SYNCNEG = 0x20
-DISC_DSB_A = 0x32
-
-DROPATN = 0x30
-REJBYTE = 0x31
-RESELECT = 0x34
-
-MSG_FLAGS = 0x35
-MSG_LEN = 0x36
-MSG_START+0 = 0x37
-MSG_START+1 = 0x38
-MSG_START+2 = 0x39
-MSG_START+3 = 0x3a
-MSG_START+4 = 0x3b
-MSG_START+5 = 0x3c
--MSG_START+0 = 0xc9 # 2's complement of MSG_START+0
-
-ARG_1 = 0x4c # sdtr conversion args & return
-ARG_2 = 0x4d
-RETURN_1 = 0x4c
-
-SIGSTATE = 0x4e # value written to SCSISIGO
-NEEDSDTR = 0x4f # send SDTR message, 1 bit/trgt
-
-SG_SIZEOF = 12 # sizeof(struct scatterlist)
-SG_NOLOAD = 0x50 # load SG pointer/length?
-SG_COUNT = 0x51 # working value of SG count
-SG_NEXT = 0x52 # working value of SG pointer
-SG_NEXT+0 = 0x52
-SG_NEXT+1 = 0x53
-SG_NEXT+2 = 0x54
-SG_NEXT+3 = 0x55
-
-# Poll QINCNT for work - the lower three bits contain
-# the number of entries in the Queue In FIFO.
-#
-start:
- test SCSISIGI,0x4 jnz reselect # BSYI
- test QINCNT,0x7 jz start
-
-# We have at least one queued SCB now. Set the SCB pointer
-# from the FIFO so we see the right bank of SCB registers,
-# then set SCSI options and set the initiator and target
-# SCSI IDs.
-#
- mov SCBPTR,QINFIFO
- mov SCBARRAY+1 call initialize
- clr SG_NOLOAD
- clr RESELECT
-
-# As soon as we get a successful selection, the target should go
-# into the message out phase since we have ATN asserted. Prepare
-# the message to send, locking out the device driver. If the device
-# driver hasn't beaten us with an ABORT or RESET message, then tack
-# on a SDTR negotiation if required.
-#
-# Messages are stored in scratch RAM starting with a flag byte (high bit
-# set means active message), one length byte, and then the message itself.
-#
- mov SCBARRAY+1 call disconnect # disconnect ok?
-
- and SINDEX,0x7,SCBARRAY+1 # lun
- or SINDEX,A # return value from disconnect
- or SINDEX,0x80 call mk_mesg # IDENTIFY message
-
- mov A,SINDEX
- cmp MSG_START+0,A jne !message # did driver beat us?
- mvi MSG_START+1 call mk_sdtr # build SDTR message if needed
-
-!message:
-
-# Enable selection phase as an initiator, and do automatic ATN
-# after the selection.
-#
- mvi SCSISEQ,0x48 # ENSELO|ENAUTOATNO
-
-# Wait for successful arbitration. The AIC-7770 documentation says
-# that SELINGO indicates successful arbitration, and that it should
-# be used to look for SELDO. However, if the sequencer is paused at
-# just the right time - a parallel fsck(8) on two drives did it for
-# me - then SELINGO can flip back to false before we've seen it. This
-# makes the sequencer sit in the arbitration loop forever. This is
-# Not Good.
-#
-# Therefore, I've added a check in the arbitration loop for SELDO
-# too. This could arguably be made a critical section by disabling
-# pauses, but I don't want to make a potentially infinite loop a CS.
-# I suppose you could fold it into the select loop, too, but since
-# I've been hunting this bug for four days it's kinda like a trophy.
-#
-arbitrate:
- test SSTAT0,0x40 jnz *select # SELDO
- test SSTAT0,0x10 jz arbitrate # SELINGO
-
-# Wait for a successful selection. If the hardware selection
-# timer goes off, then the driver gets the interrupt, so we don't
-# need to worry about it.
-#
-select:
- test SSTAT0,0x40 jz select # SELDO
- jmp *select
-
-# Reselection is being initiated by a target - we've seen the BSY
-# line driven active, and we didn't do it! Enable the reselection
-# hardware, and wait for it to finish. Make a note that we've been
-# reselected, but haven't seen an IDENTIFY message from the target
-# yet.
-#
-reselect:
- mvi SCSISEQ,0x10 # ENRSELI
-
-reselect1:
- test SSTAT0,0x20 jz reselect1 # SELDI
- mov SELID call initialize
-
- mvi RESELECT,0x80 # reselected, no IDENTIFY
-
-# After the [re]selection, make sure that the [re]selection enable
-# bit is off. This chip is flaky enough without extra things
-# turned on. Also clear the BUSFREE bit in SSTAT1 since we'll be
-# using it shortly.
-#
-*select:
- clr SCSISEQ
- mvi CLRSINT1,0x8 # CLRBUSFREE
-
-# Main loop for information transfer phases. If BSY is false, then
-# we have a bus free condition, expected or not. Otherwise, wait
-# for the target to assert REQ before checking MSG, C/D and I/O
-# for the bus phase.
-#
-# We can't simply look at the values of SCSISIGI here (if we want
-# to do synchronous data transfer), because the target won't assert
-# REQ if it's already sent us some data that we haven't acknowledged
-# yet.
-#
-ITloop:
- test SSTAT1,0x8 jnz p_busfree # BUSFREE
- test SSTAT1,0x1 jz ITloop # REQINIT
-
- and A,0xe0,SCSISIGI # CDI|IOI|MSGI
-
- cmp ALLZEROS,A je p_dataout
- cmp A,0x40 je p_datain
- cmp A,0x80 je p_command
- cmp A,0xc0 je p_status
- cmp A,0xa0 je p_mesgout
- cmp A,0xe0 je p_mesgin
-
- mvi INTSTAT,SIGNAL_0 # unknown - signal driver
-
-p_dataout:
- mvi 0 call scsisig # !CDO|!IOO|!MSGO
- call assert
- call sg_load
-
- mvi A,3
- mvi DINDEX,HCNT
- mvi SCBARRAY+23 call bcopy
-
- mvi A,3
- mvi DINDEX,STCNT
- mvi SCBARRAY+23 call bcopy
-
- mvi A,4
- mvi DINDEX,HADDR
- mvi SCBARRAY+19 call bcopy
-
- mvi 0x3d call dma # SCSIEN|SDMAEN|HDMAEN|
- # DIRECTION|FIFORESET
-
-# After a DMA finishes, save the final transfer pointer and count
-# back into the SCB, in case a device disconnects in the middle of
-# a transfer. Use SHADDR and STCNT instead of HADDR and HCNT, since
-# it's a reflection of how many bytes were transferred on the SCSI
-# (as opposed to the host) bus.
-#
- mvi A,3
- mvi DINDEX,SCBARRAY+23
- mvi STCNT call bcopy
-
- mvi A,4
- mvi DINDEX,SCBARRAY+19
- mvi SHADDR call bcopy
-
- call sg_advance
- mov SCBARRAY+18,SG_COUNT # residual S/G count
-
- jmp ITloop
-
-p_datain:
- mvi 0x40 call scsisig # !CDO|IOO|!MSGO
- call assert
- call sg_load
-
- mvi A,3
- mvi DINDEX,HCNT
- mvi SCBARRAY+23 call bcopy
-
- mvi A,3
- mvi DINDEX,STCNT
- mvi SCBARRAY+23 call bcopy
-
- mvi A,4
- mvi DINDEX,HADDR
- mvi SCBARRAY+19 call bcopy
-
- mvi 0x39 call dma # SCSIEN|SDMAEN|HDMAEN|
- # !DIRECTION|FIFORESET
- mvi A,3
- mvi DINDEX,SCBARRAY+23
- mvi STCNT call bcopy
-
- mvi A,4
- mvi DINDEX,SCBARRAY+19
- mvi SHADDR call bcopy
-
- call sg_advance
- mov SCBARRAY+18,SG_COUNT # residual S/G count
-
- jmp ITloop
-
-# Command phase. Set up the DMA registers and let 'er rip - the
-# two bytes after the SCB SCSI_cmd_length are zeroed by the driver,
-# so we can copy those three bytes directly into HCNT.
-#
-p_command:
- mvi 0x80 call scsisig # CDO|!IOO|!MSGO
- call assert
-
- mvi A,3
- mvi DINDEX,HCNT
- mvi SCBARRAY+11 call bcopy
-
- mvi A,3
- mvi DINDEX,STCNT
- mvi SCBARRAY+11 call bcopy
-
- mvi A,4
- mvi DINDEX,HADDR
- mvi SCBARRAY+7 call bcopy
-
- mvi 0x3d call dma # SCSIEN|SDMAEN|HDMAEN|
- # DIRECTION|FIFORESET
- jmp ITloop
-
-# Status phase. Wait for the data byte to appear, then read it
-# and store it into the SCB.
-#
-p_status:
- mvi 0xc0 call scsisig # CDO|IOO|!MSGO
-
- mvi SCBARRAY+14 call inb
- jmp ITloop
-
-# Message out phase. If there is no active message, but the target
-# took us into this phase anyway, build a no-op message and send it.
-#
-p_mesgout:
- mvi 0xa0 call scsisig # CDO|!IOO|MSGO
- mvi 0x8 call mk_mesg # build NOP message
-
-# Set up automatic PIO transfer from MSG_START. Bit 3 in
-# SXFRCTL0 (SPIOEN) is already on.
-#
- mvi SINDEX,MSG_START+0
- mov DINDEX,MSG_LEN
- clr A
-
-# When target asks for a byte, drop ATN if it's the last one in
-# the message. Otherwise, keep going until the message is exhausted.
-# (We can't use outb for this since it wants the input in SINDEX.)
-#
-# Keep an eye out for a phase change, in case the target issues
-# a MESSAGE REJECT.
-#
-p_mesgout2:
- test SSTAT0,0x2 jz p_mesgout2 # SPIORDY
- test SSTAT1,0x10 jnz p_mesgout6 # PHASEMIS
-
- cmp DINDEX,1 jne p_mesgout3 # last byte?
- mvi CLRSINT1,0x40 # CLRATNO - drop ATN
-
-# Write a byte to the SCSI bus. The AIC-7770 refuses to automatically
-# send ACKs in automatic PIO or DMA mode unless you make sure that the
-# "expected" bus phase in SCSISIGO matches the actual bus phase. This
-# behaviour is completely undocumented and caused me several days of
-# grief.
-#
-# After plugging in different drives to test with and using a longer
-# SCSI cable, I found that I/O in Automatic PIO mode ceased to function,
-# especially when transferring >1 byte. It seems to be much more stable
-# if STCNT is set to one before the transfer, and SDONE (in SSTAT0) is
-# polled for transfer completion - for both output _and_ input. The
-# only theory I have is that SPIORDY doesn't drop right away when SCSIDATL
-# is accessed (like the documentation says it does), and that on a longer
-# cable run, the sequencer code was fast enough to loop back and see
-# an SPIORDY that hadn't dropped yet.
-#
-p_mesgout3:
- call one_stcnt
- mov SCSIDATL,SINDIR
-
-p_mesgout4:
- test SSTAT0,0x4 jz p_mesgout4 # SDONE
- dec DINDEX
- inc A
- cmp MSG_LEN,A jne p_mesgout2
-
-# If the next bus phase after ATN drops is a message out, it means
-# that the target is requesting that the last message(s) be resent.
-#
-p_mesgout5:
- test SSTAT1,0x8 jnz p_mesgout6 # BUSFREE
- test SSTAT1,0x1 jz p_mesgout5 # REQINIT
-
- and A,0xe0,SCSISIGI # CDI|IOI|MSGI
- cmp A,0xa0 jne p_mesgout6
- mvi 0x10 call scsisig # ATNO - re-assert ATN
-
- jmp ITloop
-
-p_mesgout6:
- mvi CLRSINT1,0x40 # CLRATNO - in case of PHASEMIS
- clr MSG_FLAGS # no active msg
- jmp ITloop
-
-# Message in phase. Bytes are read using Automatic PIO mode, but not
-# using inb. This alleviates a race condition, namely that if ATN had
-# to be asserted under Automatic PIO mode, it had to beat the SCSI
-# circuitry sending an ACK to the target. This showed up under heavy
-# loads and really confused things, since ABORT commands wouldn't be
-# seen by the drive after an IDENTIFY message in until it had changed
-# to a data I/O phase.
-#
-p_mesgin:
- mvi 0xe0 call scsisig # CDO|IOO|MSGO
- mvi A call inb_first # read the 1st message byte
- mvi REJBYTE,A # save it for the driver
-
- cmp ALLZEROS,A jne p_mesgin1
-
-# We got a "command complete" message, so put the SCB pointer
-# into the Queue Out, and trigger a completion interrupt.
-#
- mov QOUTFIFO,SCBPTR
- mvi INTSTAT,0x2 # CMDCMPLT
- jmp p_mesgin_done
-
-# Is it an extended message? We only support the synchronous data
-# transfer request message, which will probably be in response to
-# an SDTR message out from us. If it's not an SDTR, reject it -
-# apparently this can be done after any message in byte, according
-# to the SCSI-2 spec.
-#
-# XXX - we should really reject this if we didn't initiate the SDTR
-# negotiation; this may cause problems with unusual devices.
-#
-p_mesgin1:
- cmp A,1 jne p_mesgin2 # extended message code?
-
- mvi A call inb_next
- cmp A,3 jne p_mesginN # extended mesg length = 3
- mvi A call inb_next
- cmp A,1 jne p_mesginN # SDTR code
-
- mvi ARG_1 call inb_next # xfer period
- mvi ARG_2 call inb_next # REQ/ACK offset
- mvi INTSTAT,SIGNAL_4 # call driver to convert
-
- call ndx_sdtr # index sync config for target
- mov DINDEX,SINDEX
- mov DINDIR,RETURN_1 # save returned value
-
- not A # turn off "need sdtr" flag
- and NEEDSDTR,A
-
-# Even though the SCSI-2 specification says that a device responding
-# to our SDTR message should honor our parameters for transmitting
-# to us, it doesn't seem to work too well in real life. In particular,
-# a lot of CD-ROM and tape units don't function: try using the SDTR
-# parameters the device sent us for both transmitting and receiving.
-#
- mov SCSIRATE,RETURN_1
- jmp p_mesgin_done
-
-# Is it a disconnect message? Set a flag in the SCB to remind us
-# and await the bus going free.
-#
-p_mesgin2:
- cmp A,4 jne p_mesgin3 # disconnect code?
-
- or SCBARRAY+0,0x4 # set "disconnected" bit
- jmp p_mesgin_done
-
-# Save data pointers message? Copy working values into the SCB,
-# usually in preparation for a disconnect.
-#
-p_mesgin3:
- cmp A,2 jne p_mesgin4 # save data pointers code?
-
- call sg_ram2scb
- jmp p_mesgin_done
-
-# Restore pointers message? Data pointers are recopied from the
-# SCB anyway at the start of any DMA operation, so the only thing
-# to copy is the scatter-gather values.
-#
-p_mesgin4:
- cmp A,3 jne p_mesgin5 # restore pointers code?
-
- call sg_scb2ram
- jmp p_mesgin_done
-
-# Identify message? For a reconnecting target, this tells us the lun
-# that the reconnection is for - find the correct SCB and switch to it,
-# clearing the "disconnected" bit so we don't "find" it by accident later.
-#
-p_mesgin5:
- test A,0x80 jz p_mesgin6 # identify message?
-
- test A,0x78 jnz p_mesginN # !DiscPriv|!LUNTAR|!Reserved
-
- mov A call findSCB # switch to correct SCB
-
-# If a active message is present after calling findSCB, then either it
-# or the driver is trying to abort the command. Either way, something
-# untoward has happened and we should just leave it alone.
-#
- test MSG_FLAGS,0x80 jnz p_mesgin_done
-
- xor SCBARRAY+0,0x4 # clear disconnect bit in SCB
- mvi RESELECT,0xc0 # make note of IDENTIFY
-
- call sg_scb2ram # implied restore pointers
- # required on reselect
- jmp p_mesgin_done
-
-# Message reject? If we have an outstanding SDTR negotiation, assume
-# that it's a response from the target selecting asynchronous transfer,
-# otherwise just ignore it since we have no clue what it pertains to.
-#
-# XXX - I don't have a device that responds this way. Does this code
-# actually work?
-#
-p_mesgin6:
- cmp A,7 jne p_mesgin7 # message reject code?
-
- and FUNCTION1,0x70,SCSIID # outstanding SDTR message?
- mov A,FUNCTION1
- test NEEDSDTR,A jz p_mesgin_done # no - ignore rejection
-
- call ndx_sdtr # note use of asynch xfer
- mov DINDEX,SINDEX
- clr DINDIR
-
- not A # turn off "active sdtr" flag
- and NEEDSDTR,A
-
- clr SCSIRATE # select asynch xfer
- jmp p_mesgin_done
-
-# [ ADD MORE MESSAGE HANDLING HERE ]
-#
-p_mesgin7:
-
-# We have no idea what this message in is, and there's no way
-# to pass it up to the kernel, so we issue a message reject and
-# hope for the best. Since we're now using manual PIO mode to
-# read in the message, there should no longer be a race condition
-# present when we assert ATN. In any case, rejection should be a
-# rare occurrence - signal the driver when it happens.
-#
-p_mesginN:
- or SINDEX,0x10,SIGSTATE # turn on ATNO
- call scsisig
- mvi INTSTAT,SIGNAL_1 # let driver know
-
- mvi 0x7 call mk_mesg # MESSAGE REJECT message
-
-p_mesgin_done:
- call inb_last # ack & turn auto PIO back on
- jmp ITloop
-
-# Bus free phase. It might be useful to interrupt the device
-# driver if we aren't expecting this. For now, make sure that
-# ATN isn't being asserted and look for a new command.
-#
-p_busfree:
- mvi CLRSINT1,0x40 # CLRATNO
- clr SIGSTATE
- jmp start
-
-# Bcopy: number of bytes to transfer should be in A, DINDEX should
-# contain the destination address, and SINDEX should contain the
-# source address. All input parameters are trashed on return.
-#
-bcopy:
- mov DINDIR,SINDIR
- dec A
- cmp ALLZEROS,A jne bcopy
- ret
-
-# Locking the driver out, build a one-byte message passed in SINDEX
-# if there is no active message already. SINDEX is returned intact.
-#
-mk_mesg:
- mvi SEQCTL,0x40 # PAUSEDIS
- test MSG_FLAGS,0x80 jnz mk_mesg1 # active message?
-
- mvi MSG_FLAGS,0x80 # if not, there is now
- mvi MSG_LEN,1 # length = 1
- mov MSG_START+0,SINDEX # 1-byte message
-
-mk_mesg1:
- clr SEQCTL # !PAUSEDIS
- ret
-
-# Input byte in Automatic PIO mode. The address to store the byte
-# in should be in SINDEX. DINDEX will be used by this routine.
-#
-inb:
- test SSTAT0,0x2 jz inb # SPIORDY
- mov DINDEX,SINDEX
- call one_stcnt # xfer one byte
- mov DINDIR,SCSIDATL
-inb1:
- test SSTAT0,0x4 jz inb1 # SDONE - wait to "finish"
- ret
-
-# Carefully read data in Automatic PIO mode. I first tried this using
-# Manual PIO mode, but it gave me continual underrun errors, probably
-# indicating that I did something wrong, but I feel more secure leaving
-# Automatic PIO on all the time.
-#
-# According to Adaptec's documentation, an ACK is not sent on input from
-# the target until SCSIDATL is read from. So we wait until SCSIDATL is
-# latched (the usual way), then read the data byte directly off the bus
-# using SCSIBUSL. When we have pulled the ATN line, or we just want to
-# acknowledge the byte, then we do a dummy read from SCISDATL. The SCSI
-# spec guarantees that the target will hold the data byte on the bus until
-# we send our ACK.
-#
-# The assumption here is that these are called in a particular sequence,
-# and that REQ is already set when inb_first is called. inb_{first,next}
-# use the same calling convention as inb.
-#
-inb_first:
- mov DINDEX,SINDEX
- mov DINDIR,SCSIBUSL ret # read byte directly from bus
-
-inb_next:
- mov DINDEX,SINDEX # save SINDEX
-
- call one_stcnt # xfer one byte
- mov NONE,SCSIDATL # dummy read from latch to ACK
-inb_next1:
- test SSTAT0,0x4 jz inb_next1 # SDONE
-inb_next2:
- test SSTAT0,0x2 jz inb_next2 # SPIORDY - wait for next byte
- mov DINDIR,SCSIBUSL ret # read byte directly from bus
-
-inb_last:
- call one_stcnt # ACK with dummy read
- mov NONE,SCSIDATL
-inb_last1:
- test SSTAT0,0x4 jz inb_last1 # wait for completion
- ret
-
-# Output byte in Automatic PIO mode. The byte to output should be
-# in SINDEX. If DROPATN's high bit is set, then ATN will be dropped
-# before the byte is output.
-#
-outb:
- test SSTAT0,0x2 jz outb # SPIORDY
- call one_stcnt # xfer one byte
-
- test DROPATN,0x80 jz outb1
- mvi CLRSINT1,0x40 # CLRATNO
- clr DROPATN
-outb1:
- mov SCSIDATL,SINDEX
-outb2:
- test SSTAT0,0x4 jz outb2 # SDONE
- ret
-
-# Write the value "1" into the STCNT registers, for Automatic PIO
-# transfers.
-#
-one_stcnt:
- clr STCNT+2
- clr STCNT+1
- mvi STCNT+0,1 ret
-
-# DMA data transfer. HADDR and HCNT must be loaded first, and
-# SINDEX should contain the value to load DFCNTRL with - 0x3d for
-# host->scsi, or 0x39 for scsi->host. The SCSI channel is cleared
-# during initialization.
-#
-dma:
- mov DFCNTRL,SINDEX
-dma1:
-dma2:
- test SSTAT0,0x1 jnz dma3 # DMADONE
- test SSTAT1,0x10 jz dma1 # PHASEMIS, ie. underrun
-
-# We will be "done" DMAing when the transfer count goes to zero, or
-# the target changes the phase (in light of this, it makes sense that
-# the DMA circuitry doesn't ACK when PHASEMIS is active). If we are
-# doing a SCSI->Host transfer, flush the data FIFO.
-#
-dma3:
- test SINDEX,0x4 jnz dma5 # DIRECTION
- and SINDEX,0xfe # mask out FIFORESET
- or DFCNTRL,0x2,SINDEX # FIFOFLUSH
-dma4:
- test DFCNTRL,0x2 jnz dma4 # FIFOFLUSHACK
-
-# Now shut the DMA enables off, and copy STCNT (ie. the underrun
-# amount, if any) to the SCB registers; SG_COUNT will get copied to
-# the SCB's residual S/G count field after sg_advance is called. Make
-# sure that the DMA enables are actually off first lest we get an ILLSADDR.
-#
-dma5:
- clr DFCNTRL # disable DMA
-dma6:
- test DFCNTRL,0x38 jnz dma6 # SCSIENACK|SDMAENACK|HDMAENACK
-
- mvi A,3
- mvi DINDEX,SCBARRAY+15
- mvi STCNT call bcopy
-
- ret
-
-# Common SCSI initialization for selection and reselection. Expects
-# the target SCSI ID to be in the upper four bits of SINDEX, and A's
-# contents are stomped on return.
-#
-initialize:
- clr SBLKCTL # channel A, !wide
- and SCSIID,0xf0,SINDEX # target ID
- and A,0x7,SCSICONF # SCSI_ID_A[210]
- or SCSIID,A
-
-# Esundry initialization.
-#
- clr DROPATN
- clr SIGSTATE
-
-# Turn on Automatic PIO mode now, before we expect to see an REQ
-# from the target. It shouldn't hurt anything to leave it on. Set
-# CLRCHN here before the target has entered a data transfer mode -
-# with synchronous SCSI, if you do it later, you blow away some
-# data in the SCSI FIFO that the target has already sent to you.
-#
- mvi SXFRCTL0,0xa # SPIOEN|CLRCHN
-
-# Set SCSI bus parity checking and the selection timeout value,
-# and enable the hardware selection timer. Set the SELTO interrupt
-# to signal the driver.
-#
- and A,0x38,SCSICONF # PARITY_ENB_A|SEL_TIM_A[10]
- or SXFRCTL1,0x4,A # ENSTIMER
- mvi SIMODE1,0x84 # ENSELTIMO|ENSCSIPERR
-
-# Initialize scatter-gather pointers by setting up the working copy
-# in scratch RAM.
-#
- call sg_scb2ram
-
-# Initialize SCSIRATE with the appropriate value for this target.
-#
- call ndx_sdtr
- mov SCSIRATE,SINDIR
- ret
-
-# Assert that if we've been reselected, then we've seen an IDENTIFY
-# message.
-#
-assert:
- test RESELECT,0x80 jz assert1 # reselected?
- test RESELECT,0x40 jnz assert1 # seen IDENTIFY?
-
- mvi INTSTAT,SIGNAL_2 # no - cause a kernel panic
-
-assert1:
- ret
-
-# Find out if disconnection is ok from the information the BIOS has left
-# us. The target ID should be in the upper four bits of SINDEX; A will
-# contain either 0x40 (disconnection ok) or 0x00 (disconnection not ok)
-# on exit.
-#
-# This is the only place the target ID is limited to three bits, so we
-# can use the FUNCTION1 register.
-#
-disconnect:
- and FUNCTION1,0x70,SINDEX # strip off extra just in case
- mov A,FUNCTION1
- test DISC_DSB_A,A jz disconnect1 # bit nonzero if DISabled
-
- clr A ret
-disconnect1:
- mvi A,0x40 ret
-
-# Locate the SCB matching the target ID in SELID and the lun in the lower
-# three bits of SINDEX, and switch the SCB to it. Have the kernel print
-# a warning message if it can't be found - this seems to happen occasionally
-# under high loads. Also, if not found, generate an ABORT message to the
-# target.
-#
-findSCB:
- and A,0x7,SINDEX # lun in lower three bits
- or A,A,SELID # can I do this?
- and A,0xf7 # only channel A implemented
-
- clr SINDEX
-
-findSCB1:
- mov SCBPTR,SINDEX # switch to new SCB
- cmp SCBARRAY+1,A jne findSCB2 # target ID/channel/lun match?
- test SCBARRAY+0,0x4 jz findSCB2 # should be disconnected
-
- ret
-
-findSCB2:
- inc SINDEX
- cmp SINDEX,MAXSCB jne findSCB1
-
- mvi INTSTAT,SIGNAL_3 # not found - signal kernel
- mvi 0x6 call mk_mesg # ABORT message
-
- or SINDEX,0x10,SIGSTATE # assert ATNO
- call scsisig
- ret
-
-# Make a working copy of the scatter-gather parameters in the SCB.
-#
-sg_scb2ram:
- mov SG_COUNT,SCBARRAY+2
-
- mvi A,4
- mvi DINDEX,SG_NEXT
- mvi SCBARRAY+3 call bcopy
-
- mvi SG_NOLOAD,0x80
- test SCBARRAY+0,0x10 jnz sg_scb2ram1 # don't reload s/g?
- clr SG_NOLOAD
-
-sg_scb2ram1:
- ret
-
-# Copying RAM values back to SCB, for Save Data Pointers message.
-#
-sg_ram2scb:
- mov SCBARRAY+2,SG_COUNT
-
- mvi A,4
- mvi DINDEX,SCBARRAY+3
- mvi SG_NEXT call bcopy
-
- and SCBARRAY+0,0xef,SCBARRAY+0
- test SG_NOLOAD,0x80 jz sg_ram2scb1 # reload s/g?
- or SCBARRAY+0,0x10
-
-sg_ram2scb1:
- ret
-
-# Load a struct scatter if needed and set up the data address and
-# length. If the working value of the SG count is nonzero, then
-# we need to load a new set of values.
-#
-# This, like the above DMA, assumes a little-endian host data storage.
-#
-sg_load:
- test SG_COUNT,0xff jz sg_load3 # SG being used?
- test SG_NOLOAD,0x80 jnz sg_load3 # don't reload s/g?
-
- clr HCNT+2
- clr HCNT+1
- mvi HCNT+0,SG_SIZEOF
-
- mvi A,4
- mvi DINDEX,HADDR
- mvi SG_NEXT call bcopy
-
- mvi DFCNTRL,0xd # HDMAEN|DIRECTION|FIFORESET
-
-# Wait for DMA from host memory to data FIFO to complete, then disable
-# DMA and wait for it to acknowledge that it's off.
-#
-sg_load1:
- test DFSTATUS,0x8 jz sg_load1 # HDONE
-
- clr DFCNTRL # disable DMA
-sg_load2:
- test DFCNTRL,0x8 jnz sg_load2 # HDMAENACK
-
-# Copy data from FIFO into SCB data pointer and data count. This assumes
-# that the struct scatterlist has this structure (this and sizeof(struct
-# scatterlist) == 12 are asserted in aha274x.c):
-#
-# struct scatterlist {
-# char *address; /* four bytes, little-endian order */
-# ... /* four bytes, ignored */
-# unsigned short length; /* two bytes, little-endian order */
-# }
-#
- mov SCBARRAY+19,DFDAT # new data address
- mov SCBARRAY+20,DFDAT
- mov SCBARRAY+21,DFDAT
- mov SCBARRAY+22,DFDAT
-
- mov NONE,DFDAT # throw away four bytes
- mov NONE,DFDAT
- mov NONE,DFDAT
- mov NONE,DFDAT
-
- mov SCBARRAY+23,DFDAT
- mov SCBARRAY+24,DFDAT
- clr SCBARRAY+25
-
-sg_load3:
- ret
-
-# Advance the scatter-gather pointers only IF NEEDED. If SG is enabled,
-# and the SCSI transfer count is zero (note that this should be called
-# right after a DMA finishes), then move the working copies of the SG
-# pointer/length along. If the SCSI transfer count is not zero, then
-# presumably the target is disconnecting - do not reload the SG values
-# next time.
-#
-sg_advance:
- test SG_COUNT,0xff jz sg_advance2 # s/g enabled?
-
- test STCNT+0,0xff jnz sg_advance1 # SCSI transfer count nonzero?
- test STCNT+1,0xff jnz sg_advance1
- test STCNT+2,0xff jnz sg_advance1
-
- clr SG_NOLOAD # reload s/g next time
- dec SG_COUNT # one less segment to go
-
- clr A # add sizeof(struct scatter)
- add SG_NEXT+0,SG_SIZEOF,SG_NEXT+0
- adc SG_NEXT+1,A,SG_NEXT+1
- adc SG_NEXT+2,A,SG_NEXT+2
- adc SG_NEXT+3,A,SG_NEXT+3
-
- ret
-
-sg_advance1:
- mvi SG_NOLOAD,0x80 # don't reload s/g next time
-sg_advance2:
- ret
-
-# Add the array base SYNCNEG to the target offset (the target address
-# is in SCSIID), and return the result in SINDEX. The accumulator
-# contains the 3->8 decoding of the target ID on return.
-#
-ndx_sdtr:
- shr A,SCSIID,4
- and A,0x7
- add SINDEX,SYNCNEG,A
-
- and FUNCTION1,0x70,SCSIID # 3-bit target address decode
- mov A,FUNCTION1 ret
-
-# If we need to negotiate transfer parameters, build the SDTR message
-# starting at the address passed in SINDEX. DINDEX is modified on return.
-#
-mk_sdtr:
- mov DINDEX,SINDEX # save SINDEX
-
- call ndx_sdtr
- test NEEDSDTR,A jnz mk_sdtr1 # do we need negotiation?
- ret
-
-mk_sdtr1:
- mvi DINDIR,1 # extended message
- mvi DINDIR,3 # extended message length = 3
- mvi DINDIR,1 # SDTR code
- mvi DINDIR,25 # REQ/ACK transfer period
- mvi DINDIR,15 # REQ/ACK offset
-
- add MSG_LEN,-MSG_START+0,DINDEX # update message length
- ret
-
-# Set SCSI bus control signal state. This also saves the last-written
-# value into a location where the higher-level driver can read it - if
-# it has to send an ABORT or RESET message, then it needs to know this
-# so it can assert ATN without upsetting SCSISIGO. The new value is
-# expected in SINDEX. Change the actual state last to avoid contention
-# from the driver.
-#
-scsisig:
- mov SIGSTATE,SINDEX
- mov SCSISIGO,SINDEX ret
+++ /dev/null
-#define AHA274X_SEQ_VERSION "1.28"
- 0x04, 0x03, 0x12, 0x1a,
- 0x07, 0x9c, 0x00, 0x1e,
- 0xff, 0x9b, 0x90, 0x02,
- 0x00, 0xa1, 0xe6, 0x16,
- 0xff, 0x6a, 0x50, 0x02,
- 0xff, 0x6a, 0x34, 0x02,
- 0x00, 0xa1, 0xf8, 0x16,
- 0x07, 0xa1, 0x65, 0x02,
- 0x00, 0x65, 0x65, 0x00,
- 0x80, 0x65, 0xb5, 0x16,
- 0xff, 0x65, 0x64, 0x02,
- 0x00, 0x37, 0x0d, 0x18,
- 0x38, 0x6a, 0x47, 0x17,
- 0x48, 0x6a, 0x00, 0x00,
- 0x40, 0x0b, 0x16, 0x1a,
- 0x10, 0x0b, 0x0e, 0x1e,
- 0x40, 0x0b, 0x10, 0x1e,
- 0x00, 0x65, 0x16, 0x10,
- 0x10, 0x6a, 0x00, 0x00,
- 0x20, 0x0b, 0x13, 0x1e,
- 0x00, 0x19, 0xe6, 0x16,
- 0x80, 0x6a, 0x34, 0x00,
- 0xff, 0x6a, 0x00, 0x02,
- 0x08, 0x6a, 0x0c, 0x00,
- 0x08, 0x0c, 0xae, 0x1a,
- 0x01, 0x0c, 0x18, 0x1e,
- 0xe0, 0x03, 0x64, 0x02,
- 0x00, 0x6a, 0x22, 0x1c,
- 0x40, 0x64, 0x38, 0x1c,
- 0x80, 0x64, 0x4e, 0x1c,
- 0xc0, 0x64, 0x5b, 0x1c,
- 0xa0, 0x64, 0x5e, 0x1c,
- 0xe0, 0x64, 0x76, 0x1c,
- 0x01, 0x6a, 0x91, 0x00,
- 0x00, 0x6a, 0x52, 0x17,
- 0x00, 0x65, 0xf4, 0x16,
- 0x00, 0x65, 0x1c, 0x17,
- 0x03, 0x6a, 0x64, 0x00,
- 0x8c, 0x6a, 0x66, 0x00,
- 0xb7, 0x6a, 0xb1, 0x16,
- 0x03, 0x6a, 0x64, 0x00,
- 0x08, 0x6a, 0x66, 0x00,
- 0xb7, 0x6a, 0xb1, 0x16,
- 0x04, 0x6a, 0x64, 0x00,
- 0x88, 0x6a, 0x66, 0x00,
- 0xb3, 0x6a, 0xb1, 0x16,
- 0x3d, 0x6a, 0xd9, 0x16,
- 0x03, 0x6a, 0x64, 0x00,
- 0xb7, 0x6a, 0x66, 0x00,
- 0x08, 0x6a, 0xb1, 0x16,
- 0x04, 0x6a, 0x64, 0x00,
- 0xb3, 0x6a, 0x66, 0x00,
- 0x14, 0x6a, 0xb1, 0x16,
- 0x00, 0x65, 0x34, 0x17,
- 0xff, 0x51, 0xb2, 0x02,
- 0x00, 0x65, 0x18, 0x10,
- 0x40, 0x6a, 0x52, 0x17,
- 0x00, 0x65, 0xf4, 0x16,
- 0x00, 0x65, 0x1c, 0x17,
- 0x03, 0x6a, 0x64, 0x00,
- 0x8c, 0x6a, 0x66, 0x00,
- 0xb7, 0x6a, 0xb1, 0x16,
- 0x03, 0x6a, 0x64, 0x00,
- 0x08, 0x6a, 0x66, 0x00,
- 0xb7, 0x6a, 0xb1, 0x16,
- 0x04, 0x6a, 0x64, 0x00,
- 0x88, 0x6a, 0x66, 0x00,
- 0xb3, 0x6a, 0xb1, 0x16,
- 0x39, 0x6a, 0xd9, 0x16,
- 0x03, 0x6a, 0x64, 0x00,
- 0xb7, 0x6a, 0x66, 0x00,
- 0x08, 0x6a, 0xb1, 0x16,
- 0x04, 0x6a, 0x64, 0x00,
- 0xb3, 0x6a, 0x66, 0x00,
- 0x14, 0x6a, 0xb1, 0x16,
- 0x00, 0x65, 0x34, 0x17,
- 0xff, 0x51, 0xb2, 0x02,
- 0x00, 0x65, 0x18, 0x10,
- 0x80, 0x6a, 0x52, 0x17,
- 0x00, 0x65, 0xf4, 0x16,
- 0x03, 0x6a, 0x64, 0x00,
- 0x8c, 0x6a, 0x66, 0x00,
- 0xab, 0x6a, 0xb1, 0x16,
- 0x03, 0x6a, 0x64, 0x00,
- 0x08, 0x6a, 0x66, 0x00,
- 0xab, 0x6a, 0xb1, 0x16,
- 0x04, 0x6a, 0x64, 0x00,
- 0x88, 0x6a, 0x66, 0x00,
- 0xa7, 0x6a, 0xb1, 0x16,
- 0x3d, 0x6a, 0xd9, 0x16,
- 0x00, 0x65, 0x18, 0x10,
- 0xc0, 0x6a, 0x52, 0x17,
- 0xae, 0x6a, 0xbc, 0x16,
- 0x00, 0x65, 0x18, 0x10,
- 0xa0, 0x6a, 0x52, 0x17,
- 0x08, 0x6a, 0xb5, 0x16,
- 0x37, 0x6a, 0x65, 0x00,
- 0xff, 0x36, 0x66, 0x02,
- 0xff, 0x6a, 0x64, 0x02,
- 0x02, 0x0b, 0x63, 0x1e,
- 0x10, 0x0c, 0x73, 0x1a,
- 0x01, 0x66, 0x67, 0x18,
- 0x40, 0x6a, 0x0c, 0x00,
- 0x00, 0x65, 0xd6, 0x16,
- 0xff, 0x6c, 0x06, 0x02,
- 0x04, 0x0b, 0x69, 0x1e,
- 0xff, 0x66, 0x66, 0x06,
- 0x01, 0x64, 0x64, 0x06,
- 0x00, 0x36, 0x63, 0x18,
- 0x08, 0x0c, 0x73, 0x1a,
- 0x01, 0x0c, 0x6d, 0x1e,
- 0xe0, 0x03, 0x64, 0x02,
- 0xa0, 0x64, 0x73, 0x18,
- 0x10, 0x6a, 0x52, 0x17,
- 0x00, 0x65, 0x18, 0x10,
- 0x40, 0x6a, 0x0c, 0x00,
- 0xff, 0x6a, 0x35, 0x02,
- 0x00, 0x65, 0x18, 0x10,
- 0xe0, 0x6a, 0x52, 0x17,
- 0x64, 0x6a, 0xc2, 0x16,
- 0x00, 0x6a, 0x31, 0x00,
- 0x00, 0x6a, 0x7d, 0x18,
- 0xff, 0x90, 0x9d, 0x02,
- 0x02, 0x6a, 0x91, 0x00,
- 0x00, 0x65, 0xac, 0x10,
- 0x01, 0x64, 0x8c, 0x18,
- 0x64, 0x6a, 0xc4, 0x16,
- 0x03, 0x64, 0xa8, 0x18,
- 0x64, 0x6a, 0xc4, 0x16,
- 0x01, 0x64, 0xa8, 0x18,
- 0x4c, 0x6a, 0xc4, 0x16,
- 0x4d, 0x6a, 0xc4, 0x16,
- 0x41, 0x6a, 0x91, 0x00,
- 0x00, 0x65, 0x42, 0x17,
- 0xff, 0x65, 0x66, 0x02,
- 0xff, 0x4c, 0x6d, 0x02,
- 0xff, 0x64, 0x64, 0x04,
- 0x00, 0x4f, 0x4f, 0x02,
- 0xff, 0x4c, 0x04, 0x02,
- 0x00, 0x65, 0xac, 0x10,
- 0x04, 0x64, 0x8f, 0x18,
- 0x04, 0xa0, 0xa0, 0x00,
- 0x00, 0x65, 0xac, 0x10,
- 0x02, 0x64, 0x92, 0x18,
- 0x00, 0x65, 0x14, 0x17,
- 0x00, 0x65, 0xac, 0x10,
- 0x03, 0x64, 0x95, 0x18,
- 0x00, 0x65, 0x0c, 0x17,
- 0x00, 0x65, 0xac, 0x10,
- 0x80, 0x64, 0x9d, 0x1e,
- 0x78, 0x64, 0xa8, 0x1a,
- 0x00, 0x64, 0xfd, 0x16,
- 0x80, 0x35, 0xac, 0x1a,
- 0x04, 0xa0, 0xa0, 0x04,
- 0xc0, 0x6a, 0x34, 0x00,
- 0x00, 0x65, 0x0c, 0x17,
- 0x00, 0x65, 0xac, 0x10,
- 0x07, 0x64, 0xa8, 0x18,
- 0x70, 0x05, 0x6e, 0x02,
- 0xff, 0x6e, 0x64, 0x02,
- 0x00, 0x4f, 0xac, 0x1e,
- 0x00, 0x65, 0x42, 0x17,
- 0xff, 0x65, 0x66, 0x02,
- 0xff, 0x6a, 0x6d, 0x02,
- 0xff, 0x64, 0x64, 0x04,
- 0x00, 0x4f, 0x4f, 0x02,
- 0xff, 0x6a, 0x04, 0x02,
- 0x00, 0x65, 0xac, 0x10,
- 0x10, 0x4e, 0x65, 0x00,
- 0x00, 0x65, 0x52, 0x17,
- 0x11, 0x6a, 0x91, 0x00,
- 0x07, 0x6a, 0xb5, 0x16,
- 0x00, 0x65, 0xca, 0x16,
- 0x00, 0x65, 0x18, 0x10,
- 0x40, 0x6a, 0x0c, 0x00,
- 0xff, 0x6a, 0x4e, 0x02,
- 0x00, 0x65, 0x00, 0x10,
- 0xff, 0x6c, 0x6d, 0x02,
- 0xff, 0x64, 0x64, 0x06,
- 0x00, 0x6a, 0xb1, 0x18,
- 0xff, 0x6a, 0x6a, 0x03,
- 0x40, 0x6a, 0x60, 0x00,
- 0x80, 0x35, 0xba, 0x1a,
- 0x80, 0x6a, 0x35, 0x00,
- 0x01, 0x6a, 0x36, 0x00,
- 0xff, 0x65, 0x37, 0x02,
- 0xff, 0x6a, 0x60, 0x02,
- 0xff, 0x6a, 0x6a, 0x03,
- 0x02, 0x0b, 0xbc, 0x1e,
- 0xff, 0x65, 0x66, 0x02,
- 0x00, 0x65, 0xd6, 0x16,
- 0xff, 0x06, 0x6d, 0x02,
- 0x04, 0x0b, 0xc0, 0x1e,
- 0xff, 0x6a, 0x6a, 0x03,
- 0xff, 0x65, 0x66, 0x02,
- 0xff, 0x12, 0x6d, 0x03,
- 0xff, 0x65, 0x66, 0x02,
- 0x00, 0x65, 0xd6, 0x16,
- 0xff, 0x06, 0x6a, 0x02,
- 0x04, 0x0b, 0xc7, 0x1e,
- 0x02, 0x0b, 0xc8, 0x1e,
- 0xff, 0x12, 0x6d, 0x03,
- 0x00, 0x65, 0xd6, 0x16,
- 0xff, 0x06, 0x6a, 0x02,
- 0x04, 0x0b, 0xcc, 0x1e,
- 0xff, 0x6a, 0x6a, 0x03,
- 0x02, 0x0b, 0xce, 0x1e,
- 0x00, 0x65, 0xd6, 0x16,
- 0x80, 0x30, 0xd3, 0x1e,
- 0x40, 0x6a, 0x0c, 0x00,
- 0xff, 0x6a, 0x30, 0x02,
- 0xff, 0x65, 0x06, 0x02,
- 0x04, 0x0b, 0xd4, 0x1e,
- 0xff, 0x6a, 0x6a, 0x03,
- 0xff, 0x6a, 0x0a, 0x02,
- 0xff, 0x6a, 0x09, 0x02,
- 0x01, 0x6a, 0x08, 0x01,
- 0xff, 0x65, 0x93, 0x02,
- 0x01, 0x0b, 0xdc, 0x1a,
- 0x10, 0x0c, 0xda, 0x1e,
- 0x04, 0x65, 0xe0, 0x1a,
- 0xfe, 0x65, 0x65, 0x02,
- 0x02, 0x65, 0x93, 0x00,
- 0x02, 0x93, 0xdf, 0x1a,
- 0xff, 0x6a, 0x93, 0x02,
- 0x38, 0x93, 0xe1, 0x1a,
- 0x03, 0x6a, 0x64, 0x00,
- 0xaf, 0x6a, 0x66, 0x00,
- 0x08, 0x6a, 0xb1, 0x16,
- 0xff, 0x6a, 0x6a, 0x03,
- 0xff, 0x6a, 0x1f, 0x02,
- 0xf0, 0x65, 0x05, 0x02,
- 0x07, 0x5a, 0x64, 0x02,
- 0x00, 0x05, 0x05, 0x00,
- 0xff, 0x6a, 0x30, 0x02,
- 0xff, 0x6a, 0x4e, 0x02,
- 0x0a, 0x6a, 0x01, 0x00,
- 0x38, 0x5a, 0x64, 0x02,
- 0x04, 0x64, 0x02, 0x00,
- 0x84, 0x6a, 0x11, 0x00,
- 0x00, 0x65, 0x0c, 0x17,
- 0x00, 0x65, 0x42, 0x17,
- 0xff, 0x6c, 0x04, 0x02,
- 0xff, 0x6a, 0x6a, 0x03,
- 0x80, 0x34, 0xf7, 0x1e,
- 0x40, 0x34, 0xf7, 0x1a,
- 0x21, 0x6a, 0x91, 0x00,
- 0xff, 0x6a, 0x6a, 0x03,
- 0x70, 0x65, 0x6e, 0x02,
- 0xff, 0x6e, 0x64, 0x02,
- 0x00, 0x32, 0xfc, 0x1e,
- 0xff, 0x6a, 0x64, 0x03,
- 0x40, 0x6a, 0x64, 0x01,
- 0x07, 0x65, 0x64, 0x02,
- 0x00, 0x19, 0x64, 0x00,
- 0xf7, 0x64, 0x64, 0x02,
- 0xff, 0x6a, 0x65, 0x02,
- 0xff, 0x65, 0x90, 0x02,
- 0x00, 0xa1, 0x05, 0x19,
- 0x04, 0xa0, 0x05, 0x1f,
- 0xff, 0x6a, 0x6a, 0x03,
- 0x01, 0x65, 0x65, 0x06,
- 0x04, 0x65, 0x01, 0x19,
- 0x31, 0x6a, 0x91, 0x00,
- 0x06, 0x6a, 0xb5, 0x16,
- 0x10, 0x4e, 0x65, 0x00,
- 0x00, 0x65, 0x52, 0x17,
- 0xff, 0x6a, 0x6a, 0x03,
- 0xff, 0xa2, 0x51, 0x02,
- 0x04, 0x6a, 0x64, 0x00,
- 0x52, 0x6a, 0x66, 0x00,
- 0xa3, 0x6a, 0xb1, 0x16,
- 0x80, 0x6a, 0x50, 0x00,
- 0x10, 0xa0, 0x13, 0x1b,
- 0xff, 0x6a, 0x50, 0x02,
- 0xff, 0x6a, 0x6a, 0x03,
- 0xff, 0x51, 0xa2, 0x02,
- 0x04, 0x6a, 0x64, 0x00,
- 0xa3, 0x6a, 0x66, 0x00,
- 0x52, 0x6a, 0xb1, 0x16,
- 0xef, 0xa0, 0xa0, 0x02,
- 0x80, 0x50, 0x1b, 0x1f,
- 0x10, 0xa0, 0xa0, 0x00,
- 0xff, 0x6a, 0x6a, 0x03,
- 0xff, 0x51, 0x33, 0x1f,
- 0x80, 0x50, 0x33, 0x1b,
- 0xff, 0x6a, 0x8e, 0x02,
- 0xff, 0x6a, 0x8d, 0x02,
- 0x0c, 0x6a, 0x8c, 0x00,
- 0x04, 0x6a, 0x64, 0x00,
- 0x88, 0x6a, 0x66, 0x00,
- 0x52, 0x6a, 0xb1, 0x16,
- 0x0d, 0x6a, 0x93, 0x00,
- 0x08, 0x94, 0x25, 0x1f,
- 0xff, 0x6a, 0x93, 0x02,
- 0x08, 0x93, 0x27, 0x1b,
- 0xff, 0x99, 0xb3, 0x02,
- 0xff, 0x99, 0xb4, 0x02,
- 0xff, 0x99, 0xb5, 0x02,
- 0xff, 0x99, 0xb6, 0x02,
- 0xff, 0x99, 0x6a, 0x02,
- 0xff, 0x99, 0x6a, 0x02,
- 0xff, 0x99, 0x6a, 0x02,
- 0xff, 0x99, 0x6a, 0x02,
- 0xff, 0x99, 0xb7, 0x02,
- 0xff, 0x99, 0xb8, 0x02,
- 0xff, 0x6a, 0xb9, 0x02,
- 0xff, 0x6a, 0x6a, 0x03,
- 0xff, 0x51, 0x41, 0x1f,
- 0xff, 0x08, 0x40, 0x1b,
- 0xff, 0x09, 0x40, 0x1b,
- 0xff, 0x0a, 0x40, 0x1b,
- 0xff, 0x6a, 0x50, 0x02,
- 0xff, 0x51, 0x51, 0x06,
- 0xff, 0x6a, 0x64, 0x02,
- 0x0c, 0x52, 0x52, 0x06,
- 0x00, 0x53, 0x53, 0x08,
- 0x00, 0x54, 0x54, 0x08,
- 0x00, 0x55, 0x55, 0x08,
- 0xff, 0x6a, 0x6a, 0x03,
- 0x80, 0x6a, 0x50, 0x00,
- 0xff, 0x6a, 0x6a, 0x03,
- 0x4c, 0x05, 0x64, 0x0a,
- 0x07, 0x64, 0x64, 0x02,
- 0x20, 0x64, 0x65, 0x06,
- 0x70, 0x05, 0x6e, 0x02,
- 0xff, 0x6e, 0x64, 0x03,
- 0xff, 0x65, 0x66, 0x02,
- 0x00, 0x65, 0x42, 0x17,
- 0x00, 0x4f, 0x4b, 0x1b,
- 0xff, 0x6a, 0x6a, 0x03,
- 0x01, 0x6a, 0x6d, 0x00,
- 0x03, 0x6a, 0x6d, 0x00,
- 0x01, 0x6a, 0x6d, 0x00,
- 0x19, 0x6a, 0x6d, 0x00,
- 0x0f, 0x6a, 0x6d, 0x00,
- 0xc9, 0x66, 0x36, 0x06,
- 0xff, 0x6a, 0x6a, 0x03,
- 0xff, 0x65, 0x4e, 0x02,
- 0xff, 0x65, 0x03, 0x03,
/*
- * Adaptec 274x device driver for Linux.
+ * Adaptec 274x/284x/294x device driver for Linux.
* Copyright (c) 1994 The University of Calgary Department of Computer Science.
*
* This program is free software; you can redistribute it and/or modify
--- /dev/null
+/*
+ * @(#)aic7xxx.c 1.34 94/11/30 jda
+ *
+ * Adaptec 274x/284x/294x device driver for Linux.
+ * Copyright (c) 1994 The University of Calgary Department of Computer Science.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Sources include the Adaptec 1740 driver (aha1740.c), the
+ * Ultrastor 24F driver (ultrastor.c), various Linux kernel
+ * source, the Adaptec EISA config file (!adp7771.cfg), the
+ * Adaptec AHA-2740A Series User's Guide, the Linux Kernel
+ * Hacker's Guide, Writing a SCSI Device Driver for Linux,
+ * the Adaptec 1542 driver (aha1542.c), the Adaptec EISA
+ * overlay file (adp7770.ovl), the Adaptec AHA-2740 Series
+ * Technical Reference Manual, the Adaptec AIC-7770 Data
+ * Book, the ANSI SCSI specification, the ANSI SCSI-2
+ * specification (draft 10c), ...
+ *
+ * On a twin-bus adapter card, channel B is ignored. Rationale:
+ * it would greatly complicate the sequencer and host driver code,
+ * and both busses are multiplexed on to the EISA bus anyway. So
+ * I don't really see any technical advantage to supporting both.
+ *
+ * As well, multiple adapter card using the same IRQ level are
+ * not supported. It doesn't make sense to configure the cards
+ * this way from a performance standpoint. Not to mention that
+ * the kernel would have to support two devices per registered IRQ.
+ */
+
+#include <stdarg.h>
+#include <asm/io.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/bios32.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+
+#include "../block/blk.h"
+#include "sd.h"
+#include "scsi.h"
+#include "hosts.h"
+#include "aic7xxx.h"
+
+/*
+ * There should be a specific return value for this in scsi.h, but
+ * it seems that most drivers ignore it.
+ */
+#define DID_UNDERFLOW DID_ERROR
+
+/* EISA/VL-bus stuff */
+
+#define MINSLOT 1
+#define MAXSLOT 15
+#define SLOTBASE(x) ((x) << 12)
+
+#define MAXIRQ 15
+
+/* AIC-7770 offset definitions */
+
+#define O_MINREG(x) ((x) + 0xc00) /* i/o range to reserve */
+#define O_MAXREG(x) ((x) + 0xcbf)
+
+#define O_SCSISEQ(x) ((x) + 0xc00) /* scsi sequence control */
+#define O_SCSISIGI(x) ((x) + 0xc03) /* scsi control signal read */
+#define O_SCSISIGO(x) ((x) + 0xc03) /* scsi control signal write */
+#define O_SCSIID(x) ((x) + 0xc05) /* scsi id */
+#define O_SSTAT0(x) ((x) + 0xc0b) /* scsi status register 0 */
+#define O_CLRSINT1(x) ((x) + 0xc0c) /* clear scsi interrupt 1 */
+#define O_SSTAT1(x) ((x) + 0xc0c) /* scsi status register 1 */
+#define O_SELID(x) ((x) + 0xc19) /* [re]selection id */
+#define O_SBLKCTL(x) ((x) + 0xc1f) /* scsi block control */
+#define O_SEQCTL(x) ((x) + 0xc60) /* sequencer control */
+#define O_SEQRAM(x) ((x) + 0xc61) /* sequencer ram data */
+#define O_SEQADDR(x) ((x) + 0xc62) /* sequencer address (W) */
+#define O_BIDx(x) ((x) + 0xc80) /* board id */
+#define O_BCTL(x) ((x) + 0xc84) /* board control */
+#define O_HCNTRL(x) ((x) + 0xc87) /* host control */
+#define O_SCBPTR(x) ((x) + 0xc90) /* scb pointer */
+#define O_INTSTAT(x) ((x) + 0xc91) /* interrupt status */
+#define O_ERROR(x) ((x) + 0xc92) /* hard error */
+#define O_CLRINT(x) ((x) + 0xc92) /* clear interrupt status */
+#define O_SCBCNT(x) ((x) + 0xc9a) /* scb auto increment */
+#define O_QINFIFO(x) ((x) + 0xc9b) /* queue in fifo */
+#define O_QINCNT(x) ((x) + 0xc9c) /* queue in count */
+#define O_QOUTFIFO(x) ((x) + 0xc9d) /* queue out fifo */
+#define O_QOUTCNT(x) ((x) + 0xc9e) /* queue out count */
+#define O_SCBARRAY(x) ((x) + 0xca0) /* scb array start */
+
+/* AIC-7870-only definitions */
+
+#define O_DSPCISTATUS(x) ((x) + 0xc86) /* ??? */
+
+/* host adapter offset definitions */
+
+#define HA_REJBYTE(x) ((x) + 0xc31) /* 1st message in byte */
+#define HA_MSG_FLAGS(x) ((x) + 0xc35) /* outgoing message flag */
+#define HA_MSG_LEN(x) ((x) + 0xc36) /* outgoing message length */
+#define HA_MSG_START(x) ((x) + 0xc37) /* outgoing message body */
+#define HA_ARG_1(x) ((x) + 0xc4c) /* sdtr <-> rate parameters */
+#define HA_ARG_2(x) ((x) + 0xc4d)
+#define HA_RETURN_1(x) ((x) + 0xc4c)
+#define HA_RETURN_2(x) ((x) + 0xc4d)
+#define HA_SIGSTATE(x) ((x) + 0xc4e) /* value in SCSISIGO */
+#define HA_NEEDSDTR(x) ((x) + 0xc4f) /* synchronous negotiation? */
+#define HA_SCBCOUNT(x) ((x) + 0xc56) /* number of hardware SCBs */
+
+#define HA_SCSICONF(x) ((x) + 0xc5a) /* SCSI config register */
+#define HA_INTDEF(x) ((x) + 0xc5c) /* interrupt def'n register */
+#define HA_HOSTCONF(x) ((x) + 0xc5d) /* host config def'n register */
+
+/* debugging code */
+
+/*
+#define AIC7XXX_DEBUG
+*/
+
+/*
+ * If a parity error occurs during a data transfer phase, run the
+ * command to completion - it's easier that way - making a note
+ * of the error condition in this location. This then will modify
+ * a DID_OK status into a DID_PARITY one for the higher-level SCSI
+ * code.
+ */
+#define aic7xxx_parity(cmd) ((cmd)->SCp.Status)
+
+/*
+ * Since the sequencer code DMAs the scatter-gather structures
+ * directly from memory, we use this macro to assert that the
+ * kernel structure hasn't changed.
+ */
+#define SG_STRUCT_CHECK(sg) \
+ ((char *)&(sg).address - (char *)&(sg) != 0 || \
+ (char *)&(sg).length - (char *)&(sg) != 8 || \
+ sizeof((sg).address) != 4 || \
+ sizeof((sg).length) != 4 || \
+ sizeof(sg) != 12)
+
+/*
+ * "Static" structures. Note that these are NOT initialized
+ * to zero inside the kernel - we have to initialize them all
+ * explicitly.
+ *
+ * We support a maximum of one adapter card per IRQ level (see the
+ * rationale for this above). On an interrupt, use the IRQ as an
+ * index into aic7xxx_boards[] to locate the card information.
+ */
+static struct Scsi_Host *aic7xxx_boards[MAXIRQ + 1];
+
+/*
+ * The maximum number of SCBs we could have for ANY type
+ * of card. DON'T FORGET TO CHANGE THE SCB MASK IN THE
+ * SEQUENCER CODE IF THIS IS MODIFIED!
+ */
+#define AIC7XXX_MAXSCB 16
+
+struct aic7xxx_host {
+ int base; /* card base address */
+ int maxscb; /* hardware SCBs */
+ int startup; /* intr type check */
+ int extended; /* extended xlate? */
+ volatile int unpause; /* value for HCNTRL */
+ volatile Scsi_Cmnd *SCB_array[AIC7XXX_MAXSCB]; /* active commands */
+};
+
+struct aic7xxx_host_config {
+ int irq; /* IRQ number */
+ int base; /* I/O base */
+ int maxscb; /* hardware SCBs */
+ int unpause; /* value for HCNTRL */
+ int scsi_id; /* host SCSI id */
+ int extended; /* extended xlate? */
+};
+
+struct aic7xxx_scb {
+ unsigned char control;
+ unsigned char target_channel_lun; /* 4/1/3 bits */
+ unsigned char SG_segment_count;
+ unsigned char SG_list_pointer[4];
+ unsigned char SCSI_cmd_pointer[4];
+ unsigned char SCSI_cmd_length;
+ unsigned char RESERVED[2]; /* must be zero */
+ unsigned char target_status;
+ unsigned char residual_data_count[3];
+ unsigned char residual_SG_segment_count;
+ unsigned char data_pointer[4];
+ unsigned char data_count[3];
+#if 0
+ /*
+ * No real point in transferring this to the
+ * SCB registers.
+ */
+ unsigned char RESERVED[6];
+#endif
+};
+
+/*
+ * NB. This table MUST be ordered shortest period first.
+ */
+static struct {
+ short period;
+ short rate;
+ char *english;
+} aic7xxx_synctab[] = {
+ 100, 0, "10.0",
+ 125, 1, "8.0",
+ 150, 2, "6.67",
+ 175, 3, "5.7",
+ 200, 4, "5.0",
+ 225, 5, "4.4",
+ 250, 6, "4.0",
+ 275, 7, "3.6"
+};
+
+static int aic7xxx_synctab_max =
+ sizeof(aic7xxx_synctab) / sizeof(aic7xxx_synctab[0]);
+
+enum aha_type {
+ T_NONE,
+ T_274X,
+ T_284X,
+ T_294X,
+ T_MAX
+};
+
+#ifdef AIC7XXX_DEBUG
+
+ extern int vsprintf(char *, const char *, va_list);
+
+ static
+ void debug(const char *fmt, ...)
+ {
+ va_list ap;
+ char buf[256];
+
+ va_start(ap, fmt);
+ vsprintf(buf, fmt, ap);
+ printk(buf);
+ va_end(ap);
+ }
+
+ static
+ void debug_config(enum aha_type type, struct aic7xxx_host_config *p)
+ {
+ int ioport2, ioport3;
+
+ static char *BRT[T_MAX][16] = {
+ { }, /* T_NONE */
+ {
+ "2", "???", "???", "12", /* T_274X */
+ "???", "???", "???", "28",
+ "???", "???", "???", "44",
+ "???", "???", "???", "60"
+ },
+ {
+ "2", "4", "8", "12", /* T_284X */
+ "16", "20", "24", "28",
+ "32", "36", "40", "44",
+ "48", "52", "56", "60"
+ },
+ {
+ "???", "???", "???", "???", /* T_294X */
+ "???", "???", "???", "???",
+ "???", "???", "???", "???",
+ "???", "???", "???", "???"
+ }
+ };
+ static int DFT[4] = {
+ 0, 50, 75, 100
+ };
+ static int SST[4] = {
+ 256, 128, 64, 32
+ };
+
+ ioport2 = inb(HA_HOSTCONF(p->base));
+ ioport3 = inb(HA_SCSICONF(p->base));
+
+ switch (type) {
+ case T_274X:
+ printk("AHA274X AT EISA SLOT %d:\n", p->base >> 12);
+ break;
+ case T_284X:
+ printk("AHA284X AT SLOT %d:\n", p->base >> 12);
+ break;
+ case T_294X:
+ printk("AHA294X (PCI-bus):\n");
+ break;
+ default:
+ panic("aic7xxx debug_config: internal error\n");
+ }
+
+ printk(" irq %d\n"
+ " bus release time %s bclks\n"
+ " data fifo threshold %d%%\n",
+ p->irq,
+ BRT[type][(ioport2 >> 2) & 0xf],
+ DFT[(ioport2 >> 6) & 0x3]);
+
+ printk(" SCSI CHANNEL A:\n"
+ " scsi id %d\n"
+ " scsi bus parity check %sabled\n"
+ " scsi selection timeout %d ms\n"
+ " scsi bus reset at power-on %sabled\n",
+ ioport3 & 0x7,
+ (ioport3 & 0x20) ? "en" : "dis",
+ SST[(ioport3 >> 3) & 0x3],
+ (ioport3 & 0x40) ? "en" : "dis");
+
+ if (type == T_274X) {
+ printk(" scsi bus termination %sabled\n",
+ (ioport3 & 0x80) ? "en" : "dis");
+ }
+ }
+
+ static
+ void debug_rate(int base, int rate)
+ {
+ int target = inb(O_SCSIID(base)) >> 4;
+
+ if (rate) {
+ printk("aic7xxx: target %d now synchronous at %sMb/s\n",
+ target,
+ aic7xxx_synctab[(rate >> 4) & 0x7].english);
+ } else {
+ printk("aic7xxx: target %d using asynchronous mode\n",
+ target);
+ }
+ }
+
+#else
+
+# define debug(fmt, args...)
+# define debug_config(x)
+# define debug_rate(x,y)
+
+#endif AIC7XXX_DEBUG
+
+/*
+ * XXX - these options apply unilaterally to _all_ 274x/284x/294x
+ * cards in the system. This should be fixed, but then,
+ * does anyone really have more than one in a machine?
+ */
+static int aic7xxx_extended = 0; /* extended translation on? */
+
+void aic7xxx_setup(char *s, int *dummy)
+{
+ int i;
+ char *p;
+
+ static struct {
+ char *name;
+ int *flag;
+ } options[] = {
+ "extended", &aic7xxx_extended,
+ NULL
+ };
+
+ for (p = strtok(s, ","); p; p = strtok(NULL, ",")) {
+ for (i = 0; options[i].name; i++)
+ if (!strcmp(options[i].name, p))
+ *(options[i].flag) = !0;
+ }
+}
+
+static
+void aic7xxx_getscb(int base, struct aic7xxx_scb *scb)
+{
+ /*
+ * This is almost identical to aic7xxx_putscb().
+ */
+ outb(0x80, O_SCBCNT(base)); /* SCBAUTO */
+
+ asm volatile("cld\n\t"
+ "rep\n\t"
+ "insb"
+ : /* no output */
+ :"D" (scb), "c" (sizeof(*scb)), "d" (O_SCBARRAY(base))
+ :"di", "cx", "dx");
+
+ outb(0, O_SCBCNT(base));
+}
+
+/*
+ * How much data should be transferred for this SCSI command? Stop
+ * at segment sg_last if it's a scatter-gather command so we can
+ * compute underflow easily.
+ */
+static
+unsigned aic7xxx_length(Scsi_Cmnd *cmd, int sg_last)
+{
+ int i, segments;
+ unsigned length;
+ struct scatterlist *sg;
+
+ segments = cmd->use_sg - sg_last;
+ sg = (struct scatterlist *)cmd->buffer;
+
+ if (cmd->use_sg) {
+ for (i = length = 0;
+ i < cmd->use_sg && i < segments;
+ i++)
+ {
+ length += sg[i].length;
+ }
+ } else
+ length = cmd->request_bufflen;
+
+ return(length);
+}
+
+static
+void aic7xxx_sg_check(Scsi_Cmnd *cmd)
+{
+ int i;
+ struct scatterlist *sg = (struct scatterlist *)cmd->buffer;
+
+ if (cmd->use_sg) {
+ for (i = 0; i < cmd->use_sg; i++)
+ if ((unsigned)sg[i].length > 0xffff)
+ panic("aic7xxx_sg_check: s/g segment > 64k\n");
+ }
+}
+
+static
+void aic7xxx_to_scsirate(unsigned char *rate,
+ unsigned char transfer,
+ unsigned char offset)
+{
+ int i;
+
+ transfer *= 4;
+
+ for (i = 0; i < aic7xxx_synctab_max-1; i++) {
+
+ if (transfer == aic7xxx_synctab[i].period) {
+ *rate = (aic7xxx_synctab[i].rate << 4) | (offset & 0xf);
+ return;
+ }
+
+ if (transfer > aic7xxx_synctab[i].period &&
+ transfer < aic7xxx_synctab[i+1].period)
+ {
+ *rate = (aic7xxx_synctab[i+1].rate << 4) |
+ (offset & 0xf);
+ return;
+ }
+ }
+ *rate = 0;
+}
+
+/*
+ * Pause the sequencer and wait for it to actually stop - this
+ * is important since the sequencer can disable pausing for critical
+ * sections.
+ */
+#define PAUSE_SEQUENCER(p) \
+ do { \
+ outb(0xe, O_HCNTRL(p->base)); /* IRQMS|PAUSE|INTEN */ \
+ \
+ while ((inb(O_HCNTRL(p->base)) & 0x4) == 0) \
+ ; \
+ } while (0)
+
+/*
+ * Unpause the sequencer. Unremarkable, yet done often enough to
+ * warrant an easy way to do it.
+ */
+#define UNPAUSE_SEQUENCER(p) \
+ outb(p->unpause, O_HCNTRL(p->base)) /* IRQMS|INTEN */
+
+/*
+ * See comments in aic7xxx_loadram() wrt this.
+ */
+#define RESTART_SEQUENCER(p) \
+ do { \
+ do { \
+ outb(0x2, O_SEQCTL(p->base)); \
+ \
+ } while (inb(O_SEQADDR(p->base)) != 0 && \
+ inb(O_SEQADDR(p->base) + 1) != 0); \
+ \
+ UNPAUSE_SEQUENCER(p); \
+ } while (0)
+
+/*
+ * Since we declared this using SA_INTERRUPT, interrupts should
+ * be disabled all through this function unless we say otherwise.
+ */
+static
+void aic7xxx_isr(int irq)
+{
+ int base, intstat;
+ struct aic7xxx_host *p;
+
+ p = (struct aic7xxx_host *)aic7xxx_boards[irq]->hostdata;
+ base = p->base;
+
+ /*
+ * Check the startup flag - if no commands have been queued,
+ * we probably have the interrupt type set wrong. Reverse
+ * the stored value and the active one in the host control
+ * register.
+ */
+ if (p->startup) {
+ p->unpause ^= 0x8;
+ outb(inb(O_HCNTRL(p->base)) ^ 0x8, O_HCNTRL(p->base));
+ return;
+ }
+
+ /*
+ * Handle all the interrupt sources - especially for SCSI
+ * interrupts, we won't get a second chance at them.
+ */
+ intstat = inb(O_INTSTAT(base));
+
+ if (intstat & 0x8) { /* BRKADRINT */
+
+ panic("aic7xxx_isr: brkadrint, error = 0x%x, seqaddr = 0x%x\n",
+ inb(O_ERROR(base)), inw(O_SEQADDR(base)));
+ }
+
+ if (intstat & 0x4) { /* SCSIINT */
+
+ int scbptr = inb(O_SCBPTR(base));
+ int status = inb(O_SSTAT1(base));
+ Scsi_Cmnd *cmd;
+
+ cmd = (Scsi_Cmnd *)p->SCB_array[scbptr];
+ if (!cmd) {
+ printk("aic7xxx_isr: no command for scb (scsiint)\n");
+ /*
+ * Turn off the interrupt and set status
+ * to zero, so that it falls through the
+ * reset of the SCSIINT code.
+ */
+ outb(status, O_CLRSINT1(base));
+ UNPAUSE_SEQUENCER(p);
+ outb(0x4, O_CLRINT(base)); /* undocumented */
+ status = 0;
+ }
+ p->SCB_array[scbptr] = NULL;
+
+ /*
+ * Only the SCSI Status 1 register has information
+ * about exceptional conditions that we'd have a
+ * SCSIINT about; anything in SSTAT0 will be handled
+ * by the sequencer. Note that there can be multiple
+ * bits set.
+ */
+ if (status & 0x80) { /* SELTO */
+ /*
+ * Hardware selection timer has expired. Turn
+ * off SCSI selection sequence.
+ */
+ outb(0, O_SCSISEQ(base));
+ cmd->result = DID_TIME_OUT << 16;
+
+ /*
+ * If there's an active message, it belongs to the
+ * command that is getting punted - remove it.
+ */
+ outb(0, HA_MSG_FLAGS(base));
+
+ /*
+ * Shut off the offending interrupt sources, reset
+ * the sequencer address to zero and unpause it,
+ * then call the high-level SCSI completion routine.
+ *
+ * WARNING! This is a magic sequence! After many
+ * hours of guesswork, turning off the SCSI interrupts
+ * in CLRSINT? does NOT clear the SCSIINT bit in
+ * INTSTAT. By writing to the (undocumented, unused
+ * according to the AIC-7770 manual) third bit of
+ * CLRINT, you can clear INTSTAT. But, if you do it
+ * while the sequencer is paused, you get a BRKADRINT
+ * with an Illegal Host Address status, so the
+ * sequencer has to be restarted first.
+ */
+ outb(0x80, O_CLRSINT1(base)); /* CLRSELTIMO */
+ RESTART_SEQUENCER(p);
+
+ outb(0x4, O_CLRINT(base)); /* undocumented */
+ cmd->scsi_done(cmd);
+ }
+
+ if (status & 0x4) { /* SCSIPERR */
+ /*
+ * A parity error has occurred during a data
+ * transfer phase. Flag it and continue.
+ */
+ printk("aic7xxx: parity error on target %d, lun %d\n",
+ cmd->target,
+ cmd->lun);
+ aic7xxx_parity(cmd) = DID_PARITY;
+
+ /*
+ * Clear interrupt and resume as above.
+ */
+ outb(0x4, O_CLRSINT1(base)); /* CLRSCSIPERR */
+ UNPAUSE_SEQUENCER(p);
+
+ outb(0x4, O_CLRINT(base)); /* undocumented */
+ }
+
+ if ((status & (0x8|0x4)) == 0 && status) {
+ /*
+ * We don't know what's going on. Turn off the
+ * interrupt source and try to continue.
+ */
+ printk("aic7xxx_isr: sstat1 = 0x%x\n", status);
+ outb(status, O_CLRSINT1(base));
+ UNPAUSE_SEQUENCER(p);
+ outb(0x4, O_CLRINT(base)); /* undocumented */
+ }
+ }
+
+ if (intstat & 0x2) { /* CMDCMPLT */
+
+ int complete, old_scbptr;
+ struct aic7xxx_scb scb;
+ unsigned actual;
+ Scsi_Cmnd *cmd;
+
+ /*
+ * The sequencer will continue running when it
+ * issues this interrupt. There may be >1 commands
+ * finished, so loop until we've processed them all.
+ */
+ do {
+ complete = inb(O_QOUTFIFO(base));
+
+ cmd = (Scsi_Cmnd *)p->SCB_array[complete];
+ if (!cmd) {
+ printk("aic7xxx warning: "
+ "no command for scb (cmdcmplt)\n");
+ continue;
+ }
+ p->SCB_array[complete] = NULL;
+
+ PAUSE_SEQUENCER(p);
+
+ /*
+ * After pausing the sequencer (and waiting
+ * for it to stop), save its SCB pointer, then
+ * write in our completed one and read the SCB
+ * registers. Afterwards, restore the saved
+ * pointer, unpause the sequencer and call the
+ * higher-level completion function - unpause
+ * first since we have no idea how long done()
+ * will take.
+ */
+ old_scbptr = inb(O_SCBPTR(base));
+ outb(complete, O_SCBPTR(base));
+
+ aic7xxx_getscb(base, &scb);
+ outb(old_scbptr, O_SCBPTR(base));
+
+ UNPAUSE_SEQUENCER(p);
+
+ cmd->result = scb.target_status |
+ (aic7xxx_parity(cmd) << 16);
+
+ /*
+ * Did we underflow? At this time, there's only
+ * one other driver that bothers to check for this,
+ * and cmd->underflow seems to be set rather half-
+ * heartedly in the higher-level SCSI code.
+ */
+ actual = aic7xxx_length(cmd,
+ scb.residual_SG_segment_count);
+
+ actual -= ((scb.residual_data_count[2] << 16) |
+ (scb.residual_data_count[1] << 8) |
+ (scb.residual_data_count[0]));
+
+ if (actual < cmd->underflow) {
+ printk("aic7xxx: target %d underflow - "
+ "wanted (at least) %u, got %u\n",
+ cmd->target, cmd->underflow, actual);
+
+ cmd->result = scb.target_status |
+ (DID_UNDERFLOW << 16);
+ }
+
+ cmd->scsi_done(cmd);
+
+ /*
+ * Clear interrupt status before checking
+ * the output queue again. This eliminates
+ * a race condition whereby a command could
+ * complete between the queue poll and the
+ * interrupt clearing, so notification of the
+ * command being complete never made it back
+ * up to the kernel.
+ */
+ outb(0x2, O_CLRINT(base)); /* CLRCMDINT */
+
+ } while (inb(O_QOUTCNT(base)));
+ }
+
+ if (intstat & 0x1) { /* SEQINT */
+
+ unsigned char transfer, offset, rate;
+
+ /*
+ * Although the sequencer is paused immediately on
+ * a SEQINT, an interrupt for a SCSIINT or a CMDCMPLT
+ * condition will have unpaused the sequencer before
+ * this point.
+ */
+ PAUSE_SEQUENCER(p);
+
+ switch (intstat & 0xf0) {
+ case 0x00:
+ panic("aic7xxx_isr: unknown scsi bus phase\n");
+ case 0x10:
+ debug("aic7xxx_isr warning: "
+ "issuing message reject, 1st byte 0x%x\n",
+ inb(HA_REJBYTE(base)));
+ break;
+ case 0x20:
+ panic("aic7xxx_isr: reconnecting target %d "
+ "didn't issue IDENTIFY message\n",
+ (inb(O_SELID(base)) >> 4) & 0xf);
+ case 0x30:
+ debug("aic7xxx_isr: sequencer couldn't find match "
+ "for reconnecting target %d - issuing ABORT\n",
+ (inb(O_SELID(base)) >> 4) & 0xf);
+ break;
+ case 0x40:
+ transfer = inb(HA_ARG_1(base));
+ offset = inb(HA_ARG_2(base));
+ aic7xxx_to_scsirate(&rate, transfer, offset);
+ outb(rate, HA_RETURN_1(base));
+ debug_rate(base, rate);
+ break;
+ default:
+ debug("aic7xxx_isr: seqint, "
+ "intstat = 0x%x, scsisigi = 0x%x\n",
+ intstat, inb(O_SCSISIGI(base)));
+ break;
+ }
+
+ outb(0x1, O_CLRINT(base)); /* CLRSEQINT */
+ UNPAUSE_SEQUENCER(p);
+ }
+}
+
+/*
+ * Probing for EISA boards: it looks like the first two bytes
+ * are a manufacturer code - three characters, five bits each:
+ *
+ * BYTE 0 BYTE 1 BYTE 2 BYTE 3
+ * ?1111122 22233333 PPPPPPPP RRRRRRRR
+ *
+ * The characters are baselined off ASCII '@', so add that value
+ * to each to get the real ASCII code for it. The next two bytes
+ * appear to be a product and revision number, probably vendor-
+ * specific. This is what is being searched for at each port,
+ * and what should probably correspond to the ID= field in the
+ * ECU's .cfg file for the card - if your card is not detected,
+ * make sure your signature is listed in the array.
+ *
+ * The fourth byte's lowest bit seems to be an enabled/disabled
+ * flag (rest of the bits are reserved?).
+ */
+
+static
+enum aha_type aic7xxx_probe(int slot, int s_base)
+{
+ int i;
+ unsigned char buf[4];
+
+ static struct {
+ int n;
+ unsigned char signature[sizeof(buf)];
+ enum aha_type type;
+ } S[] = {
+ 4, { 0x04, 0x90, 0x77, 0x71 }, T_274X, /* host adapter 274x */
+ 4, { 0x04, 0x90, 0x77, 0x70 }, T_274X, /* motherboard 274x */
+ 4, { 0x04, 0x90, 0x77, 0x56 }, T_284X, /* 284x, BIOS enabled */
+ };
+
+ for (i = 0; i < sizeof(buf); i++) {
+ /*
+ * The VL-bus cards need to be primed by
+ * writing before a signature check.
+ */
+ outb(0x80 + i, s_base);
+ buf[i] = inb(s_base + i);
+ }
+
+ for (i = 0; i < sizeof(S)/sizeof(S[0]); i++) {
+ if (!memcmp(buf, S[i].signature, S[i].n)) {
+ /*
+ * Signature match on enabled card?
+ */
+ if (inb(s_base + 4) & 1)
+ return(S[i].type);
+ printk("aic7xxx disabled at slot %d, ignored\n", slot);
+ }
+ }
+ return(T_NONE);
+}
+
+/*
+ * Return ' ' for plain 274x, 'T' for twin-channel, 'W' for
+ * wide channel, '?' for anything else.
+ */
+
+static
+char aic7xxx_type(int base)
+{
+ /*
+ * AIC-7770/7870s can be wired so that, on chip reset,
+ * the SCSI Block Control register indicates how many
+ * busses the chip is configured for. The two high bits
+ * set indicate a 294x.
+ */
+ switch (inb(O_SBLKCTL(base))) {
+ case 0:
+ case 0xc0:
+ return(' ');
+ case 2:
+ case 0xc2:
+ return('W');
+ case 8:
+ return('T');
+ default:
+ printk("aic7xxx has unknown bus configuration\n");
+ return('?');
+ }
+}
+
+static
+void aic7xxx_loadram(int base)
+{
+ static unsigned char seqprog[] = {
+ /*
+ * Each sequencer instruction is 29 bits
+ * long (fill in the excess with zeroes)
+ * and has to be loaded from least -> most
+ * significant byte, so this table has the
+ * byte ordering reversed.
+ */
+# include "aic7xxx_seq.h"
+ };
+
+ /*
+ * When the AIC-7770 is paused (as on chip reset), the
+ * sequencer address can be altered and a sequencer
+ * program can be loaded by writing it, byte by byte, to
+ * the sequencer RAM port - the Adaptec documentation
+ * recommends using REP OUTSB to do this, hence the inline
+ * assembly. Since the address autoincrements as we load
+ * the program, reset it back to zero afterward. Disable
+ * sequencer RAM parity error detection while loading, and
+ * make sure the LOADRAM bit is enabled for loading.
+ */
+ outb(0x83, O_SEQCTL(base)); /* PERRORDIS|SEQRESET|LOADRAM */
+
+ asm volatile("cld\n\t"
+ "rep\n\t"
+ "outsb"
+ : /* no output */
+ :"S" (seqprog), "c" (sizeof(seqprog)), "d" (O_SEQRAM(base))
+ :"si", "cx", "dx");
+
+ /*
+ * WARNING! This is a magic sequence! After extensive
+ * experimentation, it seems that you MUST turn off the
+ * LOADRAM bit before you play with SEQADDR again, else
+ * you will end up with parity errors being flagged on
+ * your sequencer program. (You would also think that
+ * turning off LOADRAM and setting SEQRESET to reset the
+ * address to zero would work, but you need to do it twice
+ * for it to take effect on the address. Timing problem?)
+ */
+ outb(0, O_SEQCTL(base));
+ do {
+ /*
+ * Actually, reset it until
+ * the address shows up as
+ * zero just to be safe..
+ */
+ outb(0x2, O_SEQCTL(base)); /* SEQRESET */
+
+ } while (inb(O_SEQADDR(base)) != 0 && inb(O_SEQADDR(base) + 1) != 0);
+}
+
+static
+void aha274x_config(struct aic7xxx_host_config *p, va_list ap)
+{
+ int base = va_arg(ap, int);
+
+ /*
+ * Give the AIC-7770 a reset - reading the 274x's registers
+ * returns zeroes unless you do. This forces a pause of the
+ * Sequencer.
+ */
+ outb(1, O_HCNTRL(base)); /* CHIPRST */
+
+ p->base = base;
+ p->irq = inb(HA_INTDEF(base)) & 0xf;
+ p->scsi_id = inb(HA_SCSICONF(base)) & 0x7;
+
+ /*
+ * This value for HCNTRL may be changed in the ISR if we
+ * catch a spurious interrupt right away.
+ */
+ p->unpause = 0xa;
+
+ /*
+ * XXX - these are values that I don't know how to query
+ * the hardware for. Apparently some revision E
+ * '7770s can have more SCBs, and I don't know how
+ * to get the "extended translation" flag from the
+ * EISA data area.
+ */
+ p->maxscb = 4;
+ p->extended = aic7xxx_extended;
+
+ /*
+ * A reminder until this can be detected automatically.
+ */
+ printk("aha274x: extended translation %sabled\n",
+ p->extended ? "en" : "dis");
+}
+
+static
+void aha284x_config(struct aic7xxx_host_config *p, va_list ap)
+{
+ int base = va_arg(ap, int);
+
+ /*
+ * Give the AIC-7770 a reset - this forces a pause of the
+ * Sequencer and returns everything to default values.
+ */
+ outb(1, O_HCNTRL(base)); /* CHIPRST */
+
+ p->base = base;
+ p->unpause = 0x2;
+ p->irq = inb(HA_INTDEF(base)) & 0xf;
+ p->scsi_id = inb(HA_SCSICONF(base)) & 0x7;
+
+ /*
+ * XXX - these are values that I don't know how to query
+ * the hardware for. Apparently some revision E
+ * '7770s can have more SCBs, and I don't know how
+ * to get the "extended translation" flag from the
+ * onboard memory.
+ */
+ p->maxscb = 4;
+ p->extended = aic7xxx_extended;
+
+ /*
+ * A reminder until this can be detected automatically.
+ */
+ printk("aha284x: extended translation %sabled\n",
+ p->extended ? "en" : "dis");
+}
+
+static
+void aha294x_config(struct aic7xxx_host_config *p, va_list ap)
+{
+ int error;
+ unsigned long io_port;
+ unsigned char bus, device_fn, irq;
+
+ bus = va_arg(ap, unsigned char);
+ device_fn = va_arg(ap, unsigned char);
+
+ /*
+ * Read esundry information from PCI BIOS.
+ */
+ error = pcibios_read_config_dword(bus,
+ device_fn,
+ PCI_BASE_ADDRESS_0,
+ &io_port);
+ if (error) {
+ panic("aha294x_config: error %s reading i/o port\n",
+ pcibios_strerror(error));
+ }
+
+ error = pcibios_read_config_byte(bus,
+ device_fn,
+ PCI_INTERRUPT_LINE,
+ &irq);
+ if (error) {
+ panic("aha294x_config: error %s reading irq\n",
+ pcibios_strerror(error));
+ }
+
+ /*
+ * Make the base I/O register look like EISA and VL-bus.
+ */
+ p->base = io_port - 0xc01;
+
+ /*
+ * Give the AIC-7870 a reset - this forces a pause of the
+ * Sequencer and returns everything to default values.
+ */
+ outb(1, O_HCNTRL(p->base)); /* CHIPRST */
+
+ p->irq = irq;
+ p->maxscb = 16;
+ p->unpause = 0xa;
+
+ /*
+ * XXX - these are values that I don't know how to query
+ * the hardware for, so for now the SCSI host ID is
+ * hardwired to 7, and the "extended translation"
+ * flag is taken from boot-time flags.
+ */
+ p->scsi_id = 7;
+ p->extended = aic7xxx_extended;
+
+ /*
+ * XXX - force data fifo threshold to 100%. Why does this
+ * need to be done?
+ */
+# define DFTHRESH 3
+
+ outb(DFTHRESH << 6, O_DSPCISTATUS(p->base));
+ outb(p->scsi_id | (DFTHRESH << 6), HA_SCSICONF(p->base));
+
+ /*
+ * A reminder until this can be detected automatically.
+ */
+ printk("aha294x: extended translation %sabled\n",
+ p->extended ? "en" : "dis");
+}
+
+static
+int aic7xxx_register(Scsi_Host_Template *template, enum aha_type type, ...)
+{
+ va_list ap;
+ int i, base;
+ struct Scsi_Host *host;
+ struct aic7xxx_host *p;
+ struct aic7xxx_host_config config;
+
+ va_start(ap, type);
+
+ switch (type) {
+ case T_274X:
+ aha274x_config(&config, ap);
+ break;
+ case T_284X:
+ aha284x_config(&config, ap);
+ break;
+ case T_294X:
+ aha294x_config(&config, ap);
+ break;
+ default:
+ panic("aic7xxx_register: internal error\n");
+ }
+ va_end(ap);
+
+ base = config.base;
+
+ /*
+ * The IRQ level in i/o port 4 maps directly onto the real
+ * IRQ number. If it's ok, register it with the kernel.
+ *
+ * NB. the Adaptec documentation says the IRQ number is only
+ * in the lower four bits; the ECU information shows the
+ * high bit being used as well. Which is correct?
+ */
+ if (config.irq < 9 || config.irq > 15) {
+ printk("aic7xxx uses unsupported IRQ level, ignoring\n");
+ return(0);
+ }
+
+ /*
+ * Lock out other contenders for our i/o space.
+ */
+ snarf_region(O_MINREG(base), O_MAXREG(base)-O_MINREG(base));
+
+ /*
+ * Any card-type-specific adjustments before we register
+ * the scsi host(s).
+ */
+ switch (aic7xxx_type(base)) {
+ case 'T':
+ printk("aic7xxx warning: ignoring channel B of 274x-twin\n");
+ break;
+ case ' ':
+ break;
+ default:
+ printk("aic7xxx is an unsupported type, ignoring\n");
+ return(0);
+ }
+
+ /*
+ * Before registry, make sure that the offsets of the
+ * struct scatterlist are what the sequencer will expect,
+ * otherwise disable scatter-gather altogether until someone
+ * can fix it. This is important since the sequencer will
+ * DMA elements of the SG array in while executing commands.
+ */
+ if (template->sg_tablesize != SG_NONE) {
+ struct scatterlist sg;
+
+ if (SG_STRUCT_CHECK(sg)) {
+ printk("aic7xxx warning: kernel scatter-gather "
+ "structures changed, disabling it\n");
+ template->sg_tablesize = SG_NONE;
+ }
+ }
+
+ /*
+ * Register each "host" and fill in the returned Scsi_Host
+ * structure as best we can. Some of the parameters aren't
+ * really relevant for bus types beyond ISA, and none of the
+ * high-level SCSI code looks at it anyway.. why are the fields
+ * there? Also save the pointer so that we can find the
+ * information when an IRQ is triggered.
+ */
+ host = scsi_register(template, sizeof(struct aic7xxx_host));
+ host->can_queue = config.maxscb;
+ host->this_id = config.scsi_id;
+ host->irq = config.irq;
+
+ aic7xxx_boards[config.irq] = host;
+
+ p = (struct aic7xxx_host *)host->hostdata;
+ for (i = 0; i < AIC7XXX_MAXSCB; i++)
+ p->SCB_array[i] = NULL;
+
+ p->base = config.base;
+ p->maxscb = config.maxscb;
+ p->extended = config.extended;
+
+ /*
+ * The interrupt trigger is different depending
+ * on whether the card is EISA or VL-bus - sometimes.
+ * The startup variable will be cleared once the first
+ * command is queued, and is checked in the isr to
+ * try and detect when the interrupt type is set
+ * incorrectly, triggering an interrupt immediately.
+ * This is now just set on a per-card-type basis.
+ */
+ p->unpause = config.unpause;
+ p->startup = !0;
+
+ /*
+ * Register IRQ with the kernel _after_ the host information
+ * is set up, in case we take an interrupt right away, due to
+ * the interrupt type being set wrong.
+ */
+ if (request_irq(config.irq, aic7xxx_isr, SA_INTERRUPT, "aic7xxx")) {
+ printk("aic7xxx couldn't register irq %d, ignoring\n",
+ config.irq);
+ return(0);
+ }
+
+ /*
+ * Print out debugging information before re-enabling
+ * the card - a lot of registers on it can't be read
+ * when the sequencer is active.
+ */
+#ifdef AIC7XXX_DEBUG
+ debug_config(type, &config);
+#endif
+
+ /*
+ * Load the sequencer program, then re-enable the board -
+ * resetting the AIC-7770 disables it, leaving the lights
+ * on with nobody home. On the PCI bus you *may* be home,
+ * but then your mailing address is dynamically assigned
+ * so no one can find you anyway :-)
+ */
+ aic7xxx_loadram(base);
+ if (type != T_294X)
+ outb(1, O_BCTL(base)); /* ENABLE */
+
+ /*
+ * Set the host adapter registers to indicate that synchronous
+ * negotiation should be attempted the first time the targets
+ * are communicated with. Also initialize the active message
+ * flag to indicate that there is no message.
+ */
+ outb(0xff, HA_NEEDSDTR(base));
+ outb(0, HA_MSG_FLAGS(base));
+
+ /*
+ * For reconnecting targets, the sequencer code needs to
+ * know how many SCBs it has to search through.
+ */
+ outb(config.maxscb, HA_SCBCOUNT(base));
+
+ /*
+ * Unpause the sequencer before returning and enable
+ * interrupts - we shouldn't get any until the first
+ * command is sent to us by the high-level SCSI code.
+ */
+ UNPAUSE_SEQUENCER(p);
+ return(1);
+}
+
+int aic7xxx_detect(Scsi_Host_Template *template)
+{
+ enum aha_type type;
+ int found = 0, slot, base;
+
+ /*
+ * EISA/VL-bus card signature probe.
+ */
+ for (slot = MINSLOT; slot <= MAXSLOT; slot++) {
+
+ base = SLOTBASE(slot);
+
+ if (check_region(O_MINREG(base),
+ O_MAXREG(base)-O_MINREG(base)))
+ {
+ /*
+ * Some other driver has staked a
+ * claim to this i/o region already.
+ */
+ continue;
+ }
+
+ type = aic7xxx_probe(slot, O_BIDx(base));
+
+ if (type != T_NONE) {
+ /*
+ * We "find" a 274x if we locate the card
+ * signature and we can set it up and register
+ * it with the kernel without incident.
+ */
+ found += aic7xxx_register(template, type, base);
+ }
+ }
+
+ /*
+ * PCI-bus probe.
+ */
+ if (pcibios_present()) {
+ int index = 0;
+ unsigned char bus, device_fn;
+
+ while (!pcibios_find_device(PCI_VENDOR_ID_ADAPTEC,
+ PCI_DEVICE_ID_ADAPTEC_2940,
+ index,
+ &bus,
+ &device_fn))
+ {
+ found += aic7xxx_register(template, T_294X,
+ bus, device_fn);
+ index += 1;
+ }
+ }
+
+ template->name = (char *)aic7xxx_info(NULL);
+ return(found);
+}
+
+const char *aic7xxx_info(struct Scsi_Host *notused)
+{
+ return("Adaptec AHA274x/284x/294x (EISA/VL-bus/PCI -> Fast SCSI) "
+ AIC7XXX_SEQ_VERSION "/"
+ AIC7XXX_H_VERSION "/"
+ "1.34");
+}
+
+static
+void aic7xxx_buildscb(struct aic7xxx_host *p,
+ Scsi_Cmnd *cmd,
+ struct aic7xxx_scb *scb)
+{
+ void *addr;
+ unsigned length;
+
+ memset(scb, 0, sizeof(*scb));
+
+ /*
+ * NB. channel selection (bit 3) is always zero.
+ */
+ scb->target_channel_lun = ((cmd->target << 4) & 0xf0) |
+ (cmd->lun & 0x7);
+
+ /*
+ * The interpretation of request_buffer and request_bufflen
+ * changes depending on whether or not use_sg is zero; a
+ * non-zero use_sg indicates the number of elements in the
+ * scatter-gather array.
+ *
+ * The AIC-7770 can't support transfers of any sort larger
+ * than 2^24 (three-byte count) without backflips. For what
+ * the kernel is doing, this shouldn't occur. I hope.
+ */
+ length = aic7xxx_length(cmd, 0);
+
+ /*
+ * The sequencer code cannot yet handle scatter-gather segments
+ * larger than 64k (two-byte length). The 1.1.x kernels, however,
+ * have a four-byte length field in the struct scatterlist, so
+ * make sure we don't exceed 64k on these kernels for now.
+ */
+ aic7xxx_sg_check(cmd);
+
+ if (length > 0xffffff) {
+ panic("aic7xxx_buildscb: can't transfer > 2^24 - 1 bytes\n");
+ }
+
+ /*
+ * XXX - this relies on the host data being stored in a
+ * little-endian format.
+ */
+ addr = cmd->cmnd;
+ scb->SCSI_cmd_length = cmd->cmd_len;
+ memcpy(scb->SCSI_cmd_pointer, &addr, sizeof(scb->SCSI_cmd_pointer));
+
+ if (cmd->use_sg) {
+#if 0
+ debug("aic7xxx_buildscb: SG used, %d segments, length %u\n",
+ cmd->use_sg,
+ length);
+#endif
+ scb->SG_segment_count = cmd->use_sg;
+ memcpy(scb->SG_list_pointer,
+ &cmd->request_buffer,
+ sizeof(scb->SG_list_pointer));
+ } else {
+ scb->SG_segment_count = 0;
+ memcpy(scb->data_pointer,
+ &cmd->request_buffer,
+ sizeof(scb->data_pointer));
+ memcpy(scb->data_count,
+ &cmd->request_bufflen,
+ sizeof(scb->data_count));
+ }
+}
+
+static
+void aic7xxx_putscb(int base, struct aic7xxx_scb *scb)
+{
+ /*
+ * By turning on the SCB auto increment, any reference
+ * to the SCB I/O space postincrements the SCB address
+ * we're looking at. So turn this on and dump the relevant
+ * portion of the SCB to the card.
+ */
+ outb(0x80, O_SCBCNT(base)); /* SCBAUTO */
+
+ asm volatile("cld\n\t"
+ "rep\n\t"
+ "outsb"
+ : /* no output */
+ :"S" (scb), "c" (sizeof(*scb)), "d" (O_SCBARRAY(base))
+ :"si", "cx", "dx");
+
+ outb(0, O_SCBCNT(base));
+}
+
+int aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
+{
+ long flags;
+ int empty, old_scbptr;
+ struct aic7xxx_host *p;
+ struct aic7xxx_scb scb;
+
+#if 0
+ debug("aic7xxx_queue: cmd 0x%x (size %u), target %d, lun %d\n",
+ cmd->cmnd[0],
+ cmd->cmd_len,
+ cmd->target,
+ cmd->lun);
+#endif
+
+ p = (struct aic7xxx_host *)cmd->host->hostdata;
+
+ /*
+ * Construct the SCB beforehand, so the sequencer is
+ * paused a minimal amount of time.
+ */
+ aic7xxx_buildscb(p, cmd, &scb);
+
+ /*
+ * Clear the startup flag - we can now legitimately
+ * expect interrupts.
+ */
+ p->startup = 0;
+
+ /*
+ * This is a critical section, since we don't want the
+ * interrupt routine mucking with the host data or the
+ * card. Since the kernel documentation is vague on
+ * whether or not we are in a cli/sti pair already, save
+ * the flags to be on the safe side.
+ */
+ save_flags(flags);
+ cli();
+
+ /*
+ * Find a free slot in the SCB array to load this command
+ * into. Since can_queue is set to the maximum number of
+ * SCBs for the card, we should always find one.
+ */
+ for (empty = 0; empty < p->maxscb; empty++)
+ if (!p->SCB_array[empty])
+ break;
+ if (empty == p->maxscb)
+ panic("aic7xxx_queue: couldn't find a free scb\n");
+
+ /*
+ * Pause the sequencer so we can play with its registers -
+ * wait for it to acknowledge the pause.
+ *
+ * XXX - should the interrupts be left on while doing this?
+ */
+ PAUSE_SEQUENCER(p);
+
+ /*
+ * Save the SCB pointer and put our own pointer in - this
+ * selects one of the four banks of SCB registers. Load
+ * the SCB, then write its pointer into the queue in FIFO
+ * and restore the saved SCB pointer.
+ */
+ old_scbptr = inb(O_SCBPTR(p->base));
+ outb(empty, O_SCBPTR(p->base));
+
+ aic7xxx_putscb(p->base, &scb);
+
+ outb(empty, O_QINFIFO(p->base));
+ outb(old_scbptr, O_SCBPTR(p->base));
+
+ /*
+ * Make sure the Scsi_Cmnd pointer is saved, the struct it
+ * points to is set up properly, and the parity error flag
+ * is reset, then unpause the sequencer and watch the fun
+ * begin.
+ */
+ cmd->scsi_done = fn;
+ p->SCB_array[empty] = cmd;
+ aic7xxx_parity(cmd) = DID_OK;
+
+ UNPAUSE_SEQUENCER(p);
+
+ restore_flags(flags);
+ return(0);
+}
+
+/* return values from aic7xxx_kill */
+
+enum k_state {
+ k_ok, /* scb found and message sent */
+ k_busy, /* message already present */
+ k_absent, /* couldn't locate scb */
+ k_disconnect, /* scb found, but disconnected */
+};
+
+/*
+ * This must be called with interrupts disabled - it's going to
+ * be messing around with the host data, and an interrupt being
+ * fielded in the middle could get ugly.
+ *
+ * Since so much of the abort and reset code is shared, this
+ * function performs more magic than it really should. If the
+ * command completes ok, then it will call scsi_done with the
+ * result code passed in. The unpause parameter controls whether
+ * or not the sequencer gets unpaused - the reset function, for
+ * instance, may want to do something more aggressive.
+ *
+ * Note that the command is checked for in our SCB_array first
+ * before the sequencer is paused, so if k_absent is returned,
+ * then the sequencer is NOT paused.
+ */
+
+static
+enum k_state aic7xxx_kill(Scsi_Cmnd *cmd, unsigned char message,
+ unsigned int result, int unpause)
+{
+ struct aic7xxx_host *p;
+ int i, scb, found, queued;
+ unsigned char scbsave[AIC7XXX_MAXSCB];
+
+ p = (struct aic7xxx_host *)cmd->host->hostdata;
+
+ /*
+ * If we can't find the command, assume it just completed
+ * and shrug it away.
+ */
+ for (scb = 0; scb < p->maxscb; scb++)
+ if (p->SCB_array[scb] == cmd)
+ break;
+
+ if (scb == p->maxscb)
+ return(k_absent);
+
+ PAUSE_SEQUENCER(p);
+
+ /*
+ * This is the best case, really. Check to see if the
+ * command is still in the sequencer's input queue. If
+ * so, simply remove it. Reload the queue afterward.
+ */
+ queued = inb(O_QINCNT(p->base));
+
+ for (i = found = 0; i < queued; i++) {
+ scbsave[i] = inb(O_QINFIFO(p->base));
+
+ if (scbsave[i] == scb) {
+ found = 1;
+ i -= 1;
+ }
+ }
+
+ queued -= found;
+ for (i = 0; i < queued; i++)
+ outb(scbsave[i], O_QINFIFO(p->base));
+
+ if (found)
+ goto complete;
+
+ /*
+ * Check the current SCB bank. If it's not the one belonging
+ * to the command we want to kill, assume that the command
+ * is disconnected. It's rather a pain to force a reconnect
+ * and send a message to the target, so we abdicate responsibility
+ * in this case.
+ */
+ if (inb(O_SCBPTR(p->base)) != scb) {
+ if (unpause)
+ UNPAUSE_SEQUENCER(p);
+ return(k_disconnect);
+ }
+
+ /*
+ * Presumably at this point our target command is active. Check
+ * to see if there's a message already in effect. If not, place
+ * our message in and assert ATN so the target goes into MESSAGE
+ * OUT phase.
+ */
+ if (inb(HA_MSG_FLAGS(p->base)) & 0x80) {
+ if (unpause)
+ UNPAUSE_SEQUENCER(p);
+ return(k_busy);
+ }
+
+ outb(0x80, HA_MSG_FLAGS(p->base)); /* active message */
+ outb(1, HA_MSG_LEN(p->base)); /* length = 1 */
+ outb(message, HA_MSG_START(p->base)); /* message body */
+
+ /*
+ * Assert ATN. Use the value of SCSISIGO saved by the
+ * sequencer code so we don't alter its contents radically
+ * in the middle of something critical.
+ */
+ outb(inb(HA_SIGSTATE(p->base)) | 0x10, O_SCSISIGO(p->base));
+
+ /*
+ * The command has been killed. Do the bookkeeping, unpause
+ * the sequencer, and notify the higher-level SCSI code.
+ */
+complete:
+ p->SCB_array[scb] = NULL;
+ if (unpause)
+ UNPAUSE_SEQUENCER(p);
+
+ cmd->result = result << 16;
+ cmd->scsi_done(cmd);
+ return(k_ok);
+}
+
+int aic7xxx_abort(Scsi_Cmnd *cmd)
+{
+ int rv;
+ long flags;
+
+ save_flags(flags);
+ cli();
+
+ switch (aic7xxx_kill(cmd, ABORT, DID_ABORT, !0)) {
+ case k_ok: rv = SCSI_ABORT_SUCCESS; break;
+ case k_busy: rv = SCSI_ABORT_BUSY; break;
+ case k_absent: rv = SCSI_ABORT_NOT_RUNNING; break;
+ case k_disconnect: rv = SCSI_ABORT_SNOOZE; break;
+ default:
+ panic("aic7xxx_do_abort: internal error\n");
+ }
+
+ restore_flags(flags);
+ return(rv);
+}
+
+/*
+ * Resetting the bus always succeeds - is has to, otherwise the
+ * kernel will panic! Try a surgical technique - sending a BUS
+ * DEVICE RESET message - on the offending target before pulling
+ * the SCSI bus reset line.
+ */
+
+int aic7xxx_reset(Scsi_Cmnd *cmd)
+{
+ int i;
+ long flags;
+ Scsi_Cmnd *reset;
+ struct aic7xxx_host *p;
+
+ p = (struct aic7xxx_host *)cmd->host->hostdata;
+ save_flags(flags);
+ cli();
+
+ switch (aic7xxx_kill(cmd, BUS_DEVICE_RESET, DID_RESET, 0)) {
+
+ case k_ok:
+ /*
+ * The RESET message was sent to the target
+ * with no problems. Flag that target as
+ * needing a SDTR negotiation on the next
+ * connection and restart the sequencer.
+ */
+ outb((1 << cmd->target), HA_NEEDSDTR(p->base));
+ UNPAUSE_SEQUENCER(p);
+ break;
+
+ case k_absent:
+ /*
+ * The sequencer will not be paused if aic7xxx_kill()
+ * couldn't find the command.
+ */
+ PAUSE_SEQUENCER(p);
+ /* falls through */
+
+ case k_busy:
+ case k_disconnect:
+ /*
+ * Do a hard reset of the SCSI bus. According to the
+ * SCSI-2 draft specification, reset has to be asserted
+ * for at least 25us. I'm invoking the kernel delay
+ * function for 30us since I'm not totally trusting of
+ * the busy loop timing.
+ *
+ * XXX - I'm not convinced this works. I tried resetting
+ * the bus before, trying to get the devices on the
+ * bus to revert to asynchronous transfer, and it
+ * never seemed to work.
+ */
+ debug("aic7xxx: attempting to reset scsi bus and card\n");
+
+ outb(1, O_SCSISEQ(p->base)); /* SCSIRSTO */
+ udelay(30);
+ outb(0, O_SCSISEQ(p->base)); /* !SCSIRSTO */
+
+ outb(0xff, HA_NEEDSDTR(p->base));
+ UNPAUSE_SEQUENCER(p);
+
+ /*
+ * Locate the command and return a "reset" status
+ * for it. This is not completely correct and will
+ * probably return to haunt me later.
+ */
+ for (i = 0; i < p->maxscb; i++) {
+ if (cmd == p->SCB_array[i]) {
+ reset = (Scsi_Cmnd *)p->SCB_array[i];
+ p->SCB_array[i] = NULL;
+ reset->result = DID_RESET << 16;
+ reset->scsi_done(reset);
+ break;
+ }
+ }
+ break;
+
+ default:
+ panic("aic7xxx_reset: internal error\n");
+ }
+
+ restore_flags(flags);
+ return(SCSI_RESET_SUCCESS);
+}
+
+int aic7xxx_biosparam(Disk *disk, int devno, int geom[])
+{
+ int heads, sectors, cylinders;
+ struct aic7xxx_host *p;
+
+ p = (struct aic7xxx_host *)disk->device->host->hostdata;
+
+ /*
+ * XXX - if I could portably find the card's configuration
+ * information, then this could be autodetected instead
+ * of left to a boot-time switch.
+ */
+ heads = 64;
+ sectors = 32;
+ cylinders = disk->capacity / (heads * sectors);
+
+ if (p->extended && cylinders > 1024) {
+ heads = 255;
+ sectors = 63;
+ cylinders = disk->capacity / (255 * 63);
+ }
+
+ geom[0] = heads;
+ geom[1] = sectors;
+ geom[2] = cylinders;
+
+ return(0);
+}
+
--- /dev/null
+/* @(#)aic7xxx.h 1.14 94/11/30 jda */
+
+/*
+ * Adaptec 274x/284x/294x device driver for Linux.
+ * Copyright (c) 1994 The University of Calgary Department of Computer Science.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef aic7xxx_h
+#define aic7xxx_h
+
+#define AIC7XXX_H_VERSION "1.14"
+
+/*
+ * Scsi_Host_Template (see hosts.h) for 274x - some fields
+ * to do with card config are filled in after the card is
+ * detected.
+ */
+#define AIC7XXX { \
+ NULL, \
+ NULL, \
+ NULL, \
+ aic7xxx_detect, \
+ NULL, \
+ aic7xxx_info, \
+ NULL, \
+ aic7xxx_queue, \
+ aic7xxx_abort, \
+ aic7xxx_reset, \
+ NULL, \
+ aic7xxx_biosparam, \
+ -1, /* max simultaneous cmds */\
+ -1, /* scsi id of host adapter */\
+ SG_ALL, /* max scatter-gather cmds */\
+ 1, /* cmds per lun (linked cmds) */\
+ 0, /* number of 274x's present */\
+ 0, /* no memory DMA restrictions */\
+ DISABLE_CLUSTERING \
+}
+
+extern int aic7xxx_queue(Scsi_Cmnd *, void (*)(Scsi_Cmnd *));
+extern int aic7xxx_biosparam(Disk *, int, int[]);
+extern int aic7xxx_detect(Scsi_Host_Template *);
+extern int aic7xxx_command(Scsi_Cmnd *);
+extern int aic7xxx_abort(Scsi_Cmnd *);
+extern int aic7xxx_reset(Scsi_Cmnd *);
+
+extern const char *aic7xxx_info(struct Scsi_Host *);
+
+#endif
--- /dev/null
+# @(#)aic7xxx.seq 1.32 94/11/29 jda
+#
+# Adaptec 274x/284x/294x device driver for Linux.
+# Copyright (c) 1994 The University of Calgary Department of Computer Science.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+VERSION AIC7XXX_SEQ_VERSION 1.32
+
+SCBMASK = 0x1f
+
+SCSISEQ = 0x00
+SXFRCTL0 = 0x01
+SXFRCTL1 = 0x02
+SCSISIGI = 0x03
+SCSISIGO = 0x03
+SCSIRATE = 0x04
+SCSIID = 0x05
+SCSIDATL = 0x06
+STCNT = 0x08
+STCNT+0 = 0x08
+STCNT+1 = 0x09
+STCNT+2 = 0x0a
+SSTAT0 = 0x0b
+CLRSINT1 = 0x0c
+SSTAT1 = 0x0c
+SIMODE1 = 0x11
+SCSIBUSL = 0x12
+SHADDR = 0x14
+SELID = 0x19
+SBLKCTL = 0x1f
+SEQCTL = 0x60
+A = 0x64 # == ACCUM
+SINDEX = 0x65
+DINDEX = 0x66
+ALLZEROS = 0x6a
+NONE = 0x6a
+SINDIR = 0x6c
+DINDIR = 0x6d
+FUNCTION1 = 0x6e
+HADDR = 0x88
+HCNT = 0x8c
+HCNT+0 = 0x8c
+HCNT+1 = 0x8d
+HCNT+2 = 0x8e
+SCBPTR = 0x90
+INTSTAT = 0x91
+DFCNTRL = 0x93
+DFSTATUS = 0x94
+DFDAT = 0x99
+QINFIFO = 0x9b
+QINCNT = 0x9c
+QOUTFIFO = 0x9d
+
+SCSICONF = 0x5a
+
+# The two reserved bytes at SCBARRAY+1[23] are expected to be set to
+# zero, and the reserved bit in SCBARRAY+0 is used as an internal flag
+# to indicate whether or not to reload scatter-gather parameters after
+# a disconnect.
+#
+SCBARRAY+0 = 0xa0
+SCBARRAY+1 = 0xa1
+SCBARRAY+2 = 0xa2
+SCBARRAY+3 = 0xa3
+SCBARRAY+7 = 0xa7
+SCBARRAY+11 = 0xab
+SCBARRAY+14 = 0xae
+SCBARRAY+15 = 0xaf
+SCBARRAY+16 = 0xb0
+SCBARRAY+17 = 0xb1
+SCBARRAY+18 = 0xb2
+SCBARRAY+19 = 0xb3
+SCBARRAY+20 = 0xb4
+SCBARRAY+21 = 0xb5
+SCBARRAY+22 = 0xb6
+SCBARRAY+23 = 0xb7
+SCBARRAY+24 = 0xb8
+SCBARRAY+25 = 0xb9
+
+SIGNAL_0 = 0x01 # unknown scsi bus phase
+SIGNAL_1 = 0x11 # message reject
+SIGNAL_2 = 0x21 # no IDENTIFY after reconnect
+SIGNAL_3 = 0x31 # no cmd match for reconnect
+SIGNAL_4 = 0x41 # SDTR -> SCSIRATE conversion
+
+# The host adapter card (at least the BIOS) uses 20-2f for SCSI
+# device information, 32-33 and 5a-5f as well. Since we don't support
+# wide or twin-bus SCSI, 28-2f can be reclaimed. As it turns out, the
+# BIOS trashes 20-27 anyway, writing the synchronous negotiation results
+# on top of the BIOS values, so we re-use those for our per-target
+# scratchspace (actually a value that can be copied directly into
+# SCSIRATE). This implies, since we can't get the BIOS config values,
+# that all targets will be negotiated with for synchronous transfer.
+# NEEDSDTR has one bit per target indicating if an SDTR message is
+# needed for that device - this will be set initially, as well as
+# after a bus reset condition.
+#
+# The high bit of DROPATN is set if ATN should be dropped before the ACK
+# when outb is called. REJBYTE contains the first byte of a MESSAGE IN
+# message, so the driver can report an intelligible error if a message is
+# rejected.
+#
+# RESELECT's high bit is true if we are currently handling a reselect;
+# its next-highest bit is true ONLY IF we've seen an IDENTIFY message
+# from the reselecting target. If we haven't had IDENTIFY, then we have
+# no idea what the lun is, and we can't select the right SCB register
+# bank, so force a kernel panic if the target attempts a data in/out or
+# command phase instead of corrupting something.
+#
+# Note that SG_NEXT occupies four bytes.
+#
+SYNCNEG = 0x20
+DISC_DSB_A = 0x32
+
+DROPATN = 0x30
+REJBYTE = 0x31
+RESELECT = 0x34
+
+MSG_FLAGS = 0x35
+MSG_LEN = 0x36
+MSG_START+0 = 0x37
+MSG_START+1 = 0x38
+MSG_START+2 = 0x39
+MSG_START+3 = 0x3a
+MSG_START+4 = 0x3b
+MSG_START+5 = 0x3c
+-MSG_START+0 = 0xc9 # 2's complement of MSG_START+0
+
+ARG_1 = 0x4c # sdtr conversion args & return
+ARG_2 = 0x4d
+RETURN_1 = 0x4c
+
+SIGSTATE = 0x4e # value written to SCSISIGO
+NEEDSDTR = 0x4f # send SDTR message, 1 bit/trgt
+
+SG_SIZEOF = 12 # sizeof(struct scatterlist)
+SG_NOLOAD = 0x50 # load SG pointer/length?
+SG_COUNT = 0x51 # working value of SG count
+SG_NEXT = 0x52 # working value of SG pointer
+SG_NEXT+0 = 0x52
+SG_NEXT+1 = 0x53
+SG_NEXT+2 = 0x54
+SG_NEXT+3 = 0x55
+
+SCBCOUNT = 0x56 # the actual number of SCBs
+
+# Poll QINCNT for work - the lower bits contain
+# the number of entries in the Queue In FIFO.
+#
+start:
+ test SCSISIGI,0x4 jnz reselect # BSYI
+ test QINCNT,SCBMASK jz start
+
+# We have at least one queued SCB now. Set the SCB pointer
+# from the FIFO so we see the right bank of SCB registers,
+# then set SCSI options and set the initiator and target
+# SCSI IDs.
+#
+ mov SCBPTR,QINFIFO
+ mov SCBARRAY+1 call initialize
+ clr SG_NOLOAD
+ clr RESELECT
+
+# As soon as we get a successful selection, the target should go
+# into the message out phase since we have ATN asserted. Prepare
+# the message to send, locking out the device driver. If the device
+# driver hasn't beaten us with an ABORT or RESET message, then tack
+# on a SDTR negotiation if required.
+#
+# Messages are stored in scratch RAM starting with a flag byte (high bit
+# set means active message), one length byte, and then the message itself.
+#
+ mov SCBARRAY+1 call disconnect # disconnect ok?
+
+ and SINDEX,0x7,SCBARRAY+1 # lun
+ or SINDEX,A # return value from disconnect
+ or SINDEX,0x80 call mk_mesg # IDENTIFY message
+
+ mov A,SINDEX
+ cmp MSG_START+0,A jne !message # did driver beat us?
+ mvi MSG_START+1 call mk_sdtr # build SDTR message if needed
+
+!message:
+
+# Enable selection phase as an initiator, and do automatic ATN
+# after the selection.
+#
+ mvi SCSISEQ,0x48 # ENSELO|ENAUTOATNO
+
+# Wait for successful arbitration. The AIC-7770 documentation says
+# that SELINGO indicates successful arbitration, and that it should
+# be used to look for SELDO. However, if the sequencer is paused at
+# just the right time - a parallel fsck(8) on two drives did it for
+# me - then SELINGO can flip back to false before we've seen it. This
+# makes the sequencer sit in the arbitration loop forever. This is
+# Not Good.
+#
+# Therefore, I've added a check in the arbitration loop for SELDO
+# too. This could arguably be made a critical section by disabling
+# pauses, but I don't want to make a potentially infinite loop a CS.
+# I suppose you could fold it into the select loop, too, but since
+# I've been hunting this bug for four days it's kinda like a trophy.
+#
+arbitrate:
+ test SSTAT0,0x40 jnz *select # SELDO
+ test SSTAT0,0x10 jz arbitrate # SELINGO
+
+# Wait for a successful selection. If the hardware selection
+# timer goes off, then the driver gets the interrupt, so we don't
+# need to worry about it.
+#
+select:
+ test SSTAT0,0x40 jz select # SELDO
+ jmp *select
+
+# Reselection is being initiated by a target - we've seen the BSY
+# line driven active, and we didn't do it! Enable the reselection
+# hardware, and wait for it to finish. Make a note that we've been
+# reselected, but haven't seen an IDENTIFY message from the target
+# yet.
+#
+reselect:
+ mvi SCSISEQ,0x10 # ENRSELI
+
+reselect1:
+ test SSTAT0,0x20 jz reselect1 # SELDI
+ mov SELID call initialize
+
+ mvi RESELECT,0x80 # reselected, no IDENTIFY
+
+# After the [re]selection, make sure that the [re]selection enable
+# bit is off. This chip is flaky enough without extra things
+# turned on. Also clear the BUSFREE bit in SSTAT1 since we'll be
+# using it shortly.
+#
+*select:
+ clr SCSISEQ
+ mvi CLRSINT1,0x8 # CLRBUSFREE
+
+# Main loop for information transfer phases. If BSY is false, then
+# we have a bus free condition, expected or not. Otherwise, wait
+# for the target to assert REQ before checking MSG, C/D and I/O
+# for the bus phase.
+#
+# We can't simply look at the values of SCSISIGI here (if we want
+# to do synchronous data transfer), because the target won't assert
+# REQ if it's already sent us some data that we haven't acknowledged
+# yet.
+#
+ITloop:
+ test SSTAT1,0x8 jnz p_busfree # BUSFREE
+ test SSTAT1,0x1 jz ITloop # REQINIT
+
+ and A,0xe0,SCSISIGI # CDI|IOI|MSGI
+
+ cmp ALLZEROS,A je p_dataout
+ cmp A,0x40 je p_datain
+ cmp A,0x80 je p_command
+ cmp A,0xc0 je p_status
+ cmp A,0xa0 je p_mesgout
+ cmp A,0xe0 je p_mesgin
+
+ mvi INTSTAT,SIGNAL_0 # unknown - signal driver
+
+p_dataout:
+ mvi 0 call scsisig # !CDO|!IOO|!MSGO
+ call assert
+ call sg_load
+
+ mvi A,3
+ mvi DINDEX,HCNT
+ mvi SCBARRAY+23 call bcopy
+
+ mvi A,3
+ mvi DINDEX,STCNT
+ mvi SCBARRAY+23 call bcopy
+
+ mvi A,4
+ mvi DINDEX,HADDR
+ mvi SCBARRAY+19 call bcopy
+
+ mvi 0x3d call dma # SCSIEN|SDMAEN|HDMAEN|
+ # DIRECTION|FIFORESET
+
+# After a DMA finishes, save the final transfer pointer and count
+# back into the SCB, in case a device disconnects in the middle of
+# a transfer. Use SHADDR and STCNT instead of HADDR and HCNT, since
+# it's a reflection of how many bytes were transferred on the SCSI
+# (as opposed to the host) bus.
+#
+ mvi A,3
+ mvi DINDEX,SCBARRAY+23
+ mvi STCNT call bcopy
+
+ mvi A,4
+ mvi DINDEX,SCBARRAY+19
+ mvi SHADDR call bcopy
+
+ call sg_advance
+ mov SCBARRAY+18,SG_COUNT # residual S/G count
+
+ jmp ITloop
+
+p_datain:
+ mvi 0x40 call scsisig # !CDO|IOO|!MSGO
+ call assert
+ call sg_load
+
+ mvi A,3
+ mvi DINDEX,HCNT
+ mvi SCBARRAY+23 call bcopy
+
+ mvi A,3
+ mvi DINDEX,STCNT
+ mvi SCBARRAY+23 call bcopy
+
+ mvi A,4
+ mvi DINDEX,HADDR
+ mvi SCBARRAY+19 call bcopy
+
+ mvi 0x39 call dma # SCSIEN|SDMAEN|HDMAEN|
+ # !DIRECTION|FIFORESET
+ mvi A,3
+ mvi DINDEX,SCBARRAY+23
+ mvi STCNT call bcopy
+
+ mvi A,4
+ mvi DINDEX,SCBARRAY+19
+ mvi SHADDR call bcopy
+
+ call sg_advance
+ mov SCBARRAY+18,SG_COUNT # residual S/G count
+
+ jmp ITloop
+
+# Command phase. Set up the DMA registers and let 'er rip - the
+# two bytes after the SCB SCSI_cmd_length are zeroed by the driver,
+# so we can copy those three bytes directly into HCNT.
+#
+p_command:
+ mvi 0x80 call scsisig # CDO|!IOO|!MSGO
+ call assert
+
+ mvi A,3
+ mvi DINDEX,HCNT
+ mvi SCBARRAY+11 call bcopy
+
+ mvi A,3
+ mvi DINDEX,STCNT
+ mvi SCBARRAY+11 call bcopy
+
+ mvi A,4
+ mvi DINDEX,HADDR
+ mvi SCBARRAY+7 call bcopy
+
+ mvi 0x3d call dma # SCSIEN|SDMAEN|HDMAEN|
+ # DIRECTION|FIFORESET
+ jmp ITloop
+
+# Status phase. Wait for the data byte to appear, then read it
+# and store it into the SCB.
+#
+p_status:
+ mvi 0xc0 call scsisig # CDO|IOO|!MSGO
+
+ mvi SCBARRAY+14 call inb
+ jmp ITloop
+
+# Message out phase. If there is no active message, but the target
+# took us into this phase anyway, build a no-op message and send it.
+#
+p_mesgout:
+ mvi 0xa0 call scsisig # CDO|!IOO|MSGO
+ mvi 0x8 call mk_mesg # build NOP message
+
+# Set up automatic PIO transfer from MSG_START. Bit 3 in
+# SXFRCTL0 (SPIOEN) is already on.
+#
+ mvi SINDEX,MSG_START+0
+ mov DINDEX,MSG_LEN
+ clr A
+
+# When target asks for a byte, drop ATN if it's the last one in
+# the message. Otherwise, keep going until the message is exhausted.
+# (We can't use outb for this since it wants the input in SINDEX.)
+#
+# Keep an eye out for a phase change, in case the target issues
+# a MESSAGE REJECT.
+#
+p_mesgout2:
+ test SSTAT0,0x2 jz p_mesgout2 # SPIORDY
+ test SSTAT1,0x10 jnz p_mesgout6 # PHASEMIS
+
+ cmp DINDEX,1 jne p_mesgout3 # last byte?
+ mvi CLRSINT1,0x40 # CLRATNO - drop ATN
+
+# Write a byte to the SCSI bus. The AIC-7770 refuses to automatically
+# send ACKs in automatic PIO or DMA mode unless you make sure that the
+# "expected" bus phase in SCSISIGO matches the actual bus phase. This
+# behaviour is completely undocumented and caused me several days of
+# grief.
+#
+# After plugging in different drives to test with and using a longer
+# SCSI cable, I found that I/O in Automatic PIO mode ceased to function,
+# especially when transferring >1 byte. It seems to be much more stable
+# if STCNT is set to one before the transfer, and SDONE (in SSTAT0) is
+# polled for transfer completion - for both output _and_ input. The
+# only theory I have is that SPIORDY doesn't drop right away when SCSIDATL
+# is accessed (like the documentation says it does), and that on a longer
+# cable run, the sequencer code was fast enough to loop back and see
+# an SPIORDY that hadn't dropped yet.
+#
+p_mesgout3:
+ call one_stcnt
+ mov SCSIDATL,SINDIR
+
+p_mesgout4:
+ test SSTAT0,0x4 jz p_mesgout4 # SDONE
+ dec DINDEX
+ inc A
+ cmp MSG_LEN,A jne p_mesgout2
+
+# If the next bus phase after ATN drops is a message out, it means
+# that the target is requesting that the last message(s) be resent.
+#
+p_mesgout5:
+ test SSTAT1,0x8 jnz p_mesgout6 # BUSFREE
+ test SSTAT1,0x1 jz p_mesgout5 # REQINIT
+
+ and A,0xe0,SCSISIGI # CDI|IOI|MSGI
+ cmp A,0xa0 jne p_mesgout6
+ mvi 0x10 call scsisig # ATNO - re-assert ATN
+
+ jmp ITloop
+
+p_mesgout6:
+ mvi CLRSINT1,0x40 # CLRATNO - in case of PHASEMIS
+ clr MSG_FLAGS # no active msg
+ jmp ITloop
+
+# Message in phase. Bytes are read using Automatic PIO mode, but not
+# using inb. This alleviates a race condition, namely that if ATN had
+# to be asserted under Automatic PIO mode, it had to beat the SCSI
+# circuitry sending an ACK to the target. This showed up under heavy
+# loads and really confused things, since ABORT commands wouldn't be
+# seen by the drive after an IDENTIFY message in until it had changed
+# to a data I/O phase.
+#
+p_mesgin:
+ mvi 0xe0 call scsisig # CDO|IOO|MSGO
+ mvi A call inb_first # read the 1st message byte
+ mvi REJBYTE,A # save it for the driver
+
+ cmp ALLZEROS,A jne p_mesgin1
+
+# We got a "command complete" message, so put the SCB pointer
+# into the Queue Out, and trigger a completion interrupt.
+#
+ mov QOUTFIFO,SCBPTR
+ mvi INTSTAT,0x2 # CMDCMPLT
+ jmp p_mesgin_done
+
+# Is it an extended message? We only support the synchronous data
+# transfer request message, which will probably be in response to
+# an SDTR message out from us. If it's not an SDTR, reject it -
+# apparently this can be done after any message in byte, according
+# to the SCSI-2 spec.
+#
+# XXX - we should really reject this if we didn't initiate the SDTR
+# negotiation; this may cause problems with unusual devices.
+#
+p_mesgin1:
+ cmp A,1 jne p_mesgin2 # extended message code?
+
+ mvi A call inb_next
+ cmp A,3 jne p_mesginN # extended mesg length = 3
+ mvi A call inb_next
+ cmp A,1 jne p_mesginN # SDTR code
+
+ mvi ARG_1 call inb_next # xfer period
+ mvi ARG_2 call inb_next # REQ/ACK offset
+ mvi INTSTAT,SIGNAL_4 # call driver to convert
+
+ call ndx_sdtr # index sync config for target
+ mov DINDEX,SINDEX
+ mov DINDIR,RETURN_1 # save returned value
+
+ not A # turn off "need sdtr" flag
+ and NEEDSDTR,A
+
+# Even though the SCSI-2 specification says that a device responding
+# to our SDTR message should honor our parameters for transmitting
+# to us, it doesn't seem to work too well in real life. In particular,
+# a lot of CD-ROM and tape units don't function: try using the SDTR
+# parameters the device sent us for both transmitting and receiving.
+#
+ mov SCSIRATE,RETURN_1
+ jmp p_mesgin_done
+
+# Is it a disconnect message? Set a flag in the SCB to remind us
+# and await the bus going free.
+#
+p_mesgin2:
+ cmp A,4 jne p_mesgin3 # disconnect code?
+
+ or SCBARRAY+0,0x4 # set "disconnected" bit
+ jmp p_mesgin_done
+
+# Save data pointers message? Copy working values into the SCB,
+# usually in preparation for a disconnect.
+#
+p_mesgin3:
+ cmp A,2 jne p_mesgin4 # save data pointers code?
+
+ call sg_ram2scb
+ jmp p_mesgin_done
+
+# Restore pointers message? Data pointers are recopied from the
+# SCB anyway at the start of any DMA operation, so the only thing
+# to copy is the scatter-gather values.
+#
+p_mesgin4:
+ cmp A,3 jne p_mesgin5 # restore pointers code?
+
+ call sg_scb2ram
+ jmp p_mesgin_done
+
+# Identify message? For a reconnecting target, this tells us the lun
+# that the reconnection is for - find the correct SCB and switch to it,
+# clearing the "disconnected" bit so we don't "find" it by accident later.
+#
+p_mesgin5:
+ test A,0x80 jz p_mesgin6 # identify message?
+
+ test A,0x78 jnz p_mesginN # !DiscPriv|!LUNTAR|!Reserved
+
+ mov A call findSCB # switch to correct SCB
+
+# If a active message is present after calling findSCB, then either it
+# or the driver is trying to abort the command. Either way, something
+# untoward has happened and we should just leave it alone.
+#
+ test MSG_FLAGS,0x80 jnz p_mesgin_done
+
+ xor SCBARRAY+0,0x4 # clear disconnect bit in SCB
+ mvi RESELECT,0xc0 # make note of IDENTIFY
+
+ call sg_scb2ram # implied restore pointers
+ # required on reselect
+ jmp p_mesgin_done
+
+# Message reject? If we have an outstanding SDTR negotiation, assume
+# that it's a response from the target selecting asynchronous transfer,
+# otherwise just ignore it since we have no clue what it pertains to.
+#
+# XXX - I don't have a device that responds this way. Does this code
+# actually work?
+#
+p_mesgin6:
+ cmp A,7 jne p_mesgin7 # message reject code?
+
+ and FUNCTION1,0x70,SCSIID # outstanding SDTR message?
+ mov A,FUNCTION1
+ test NEEDSDTR,A jz p_mesgin_done # no - ignore rejection
+
+ call ndx_sdtr # note use of asynch xfer
+ mov DINDEX,SINDEX
+ clr DINDIR
+
+ not A # turn off "active sdtr" flag
+ and NEEDSDTR,A
+
+ clr SCSIRATE # select asynch xfer
+ jmp p_mesgin_done
+
+# [ ADD MORE MESSAGE HANDLING HERE ]
+#
+p_mesgin7:
+
+# We have no idea what this message in is, and there's no way
+# to pass it up to the kernel, so we issue a message reject and
+# hope for the best. Since we're now using manual PIO mode to
+# read in the message, there should no longer be a race condition
+# present when we assert ATN. In any case, rejection should be a
+# rare occurrence - signal the driver when it happens.
+#
+p_mesginN:
+ or SINDEX,0x10,SIGSTATE # turn on ATNO
+ call scsisig
+ mvi INTSTAT,SIGNAL_1 # let driver know
+
+ mvi 0x7 call mk_mesg # MESSAGE REJECT message
+
+p_mesgin_done:
+ call inb_last # ack & turn auto PIO back on
+ jmp ITloop
+
+# Bus free phase. It might be useful to interrupt the device
+# driver if we aren't expecting this. For now, make sure that
+# ATN isn't being asserted and look for a new command.
+#
+p_busfree:
+ mvi CLRSINT1,0x40 # CLRATNO
+ clr SIGSTATE
+ jmp start
+
+# Bcopy: number of bytes to transfer should be in A, DINDEX should
+# contain the destination address, and SINDEX should contain the
+# source address. All input parameters are trashed on return.
+#
+bcopy:
+ mov DINDIR,SINDIR
+ dec A
+ cmp ALLZEROS,A jne bcopy
+ ret
+
+# Locking the driver out, build a one-byte message passed in SINDEX
+# if there is no active message already. SINDEX is returned intact.
+#
+mk_mesg:
+ mvi SEQCTL,0x40 # PAUSEDIS
+ test MSG_FLAGS,0x80 jnz mk_mesg1 # active message?
+
+ mvi MSG_FLAGS,0x80 # if not, there is now
+ mvi MSG_LEN,1 # length = 1
+ mov MSG_START+0,SINDEX # 1-byte message
+
+mk_mesg1:
+ clr SEQCTL # !PAUSEDIS
+ ret
+
+# Input byte in Automatic PIO mode. The address to store the byte
+# in should be in SINDEX. DINDEX will be used by this routine.
+#
+inb:
+ test SSTAT0,0x2 jz inb # SPIORDY
+ mov DINDEX,SINDEX
+ call one_stcnt # xfer one byte
+ mov DINDIR,SCSIDATL
+inb1:
+ test SSTAT0,0x4 jz inb1 # SDONE - wait to "finish"
+ ret
+
+# Carefully read data in Automatic PIO mode. I first tried this using
+# Manual PIO mode, but it gave me continual underrun errors, probably
+# indicating that I did something wrong, but I feel more secure leaving
+# Automatic PIO on all the time.
+#
+# According to Adaptec's documentation, an ACK is not sent on input from
+# the target until SCSIDATL is read from. So we wait until SCSIDATL is
+# latched (the usual way), then read the data byte directly off the bus
+# using SCSIBUSL. When we have pulled the ATN line, or we just want to
+# acknowledge the byte, then we do a dummy read from SCISDATL. The SCSI
+# spec guarantees that the target will hold the data byte on the bus until
+# we send our ACK.
+#
+# The assumption here is that these are called in a particular sequence,
+# and that REQ is already set when inb_first is called. inb_{first,next}
+# use the same calling convention as inb.
+#
+inb_first:
+ mov DINDEX,SINDEX
+ mov DINDIR,SCSIBUSL ret # read byte directly from bus
+
+inb_next:
+ mov DINDEX,SINDEX # save SINDEX
+
+ call one_stcnt # xfer one byte
+ mov NONE,SCSIDATL # dummy read from latch to ACK
+inb_next1:
+ test SSTAT0,0x4 jz inb_next1 # SDONE
+inb_next2:
+ test SSTAT0,0x2 jz inb_next2 # SPIORDY - wait for next byte
+ mov DINDIR,SCSIBUSL ret # read byte directly from bus
+
+inb_last:
+ call one_stcnt # ACK with dummy read
+ mov NONE,SCSIDATL
+inb_last1:
+ test SSTAT0,0x4 jz inb_last1 # wait for completion
+ ret
+
+# Output byte in Automatic PIO mode. The byte to output should be
+# in SINDEX. If DROPATN's high bit is set, then ATN will be dropped
+# before the byte is output.
+#
+outb:
+ test SSTAT0,0x2 jz outb # SPIORDY
+ call one_stcnt # xfer one byte
+
+ test DROPATN,0x80 jz outb1
+ mvi CLRSINT1,0x40 # CLRATNO
+ clr DROPATN
+outb1:
+ mov SCSIDATL,SINDEX
+outb2:
+ test SSTAT0,0x4 jz outb2 # SDONE
+ ret
+
+# Write the value "1" into the STCNT registers, for Automatic PIO
+# transfers.
+#
+one_stcnt:
+ clr STCNT+2
+ clr STCNT+1
+ mvi STCNT+0,1 ret
+
+# DMA data transfer. HADDR and HCNT must be loaded first, and
+# SINDEX should contain the value to load DFCNTRL with - 0x3d for
+# host->scsi, or 0x39 for scsi->host. The SCSI channel is cleared
+# during initialization.
+#
+dma:
+ mov DFCNTRL,SINDEX
+dma1:
+dma2:
+ test SSTAT0,0x1 jnz dma3 # DMADONE
+ test SSTAT1,0x10 jz dma1 # PHASEMIS, ie. underrun
+
+# We will be "done" DMAing when the transfer count goes to zero, or
+# the target changes the phase (in light of this, it makes sense that
+# the DMA circuitry doesn't ACK when PHASEMIS is active). If we are
+# doing a SCSI->Host transfer, the data FIFO should be flushed auto-
+# magically on STCNT=0 or a phase change, so just wait for FIFO empty
+# status.
+#
+dma3:
+ test SINDEX,0x4 jnz dma5 # DIRECTION
+dma4:
+ test DFSTATUS,0x1 jz dma4 # !FIFOEMP
+
+# Now shut the DMA enables off, and copy STCNT (ie. the underrun
+# amount, if any) to the SCB registers; SG_COUNT will get copied to
+# the SCB's residual S/G count field after sg_advance is called. Make
+# sure that the DMA enables are actually off first lest we get an ILLSADDR.
+#
+dma5:
+ clr DFCNTRL # disable DMA
+dma6:
+ test DFCNTRL,0x38 jnz dma6 # SCSIENACK|SDMAENACK|HDMAENACK
+
+ mvi A,3
+ mvi DINDEX,SCBARRAY+15
+ mvi STCNT call bcopy
+
+ ret
+
+# Common SCSI initialization for selection and reselection. Expects
+# the target SCSI ID to be in the upper four bits of SINDEX, and A's
+# contents are stomped on return.
+#
+initialize:
+ clr SBLKCTL # channel A, !wide
+ and SCSIID,0xf0,SINDEX # target ID
+ and A,0x7,SCSICONF # SCSI_ID_A[210]
+ or SCSIID,A
+
+# Esundry initialization.
+#
+ clr DROPATN
+ clr SIGSTATE
+
+# Turn on Automatic PIO mode now, before we expect to see an REQ
+# from the target. It shouldn't hurt anything to leave it on. Set
+# CLRCHN here before the target has entered a data transfer mode -
+# with synchronous SCSI, if you do it later, you blow away some
+# data in the SCSI FIFO that the target has already sent to you.
+#
+# DFON is a 7870 bit enabling digital filtering of REQ and ACK signals.
+#
+ mvi SXFRCTL0,0x8a # DFON|SPIOEN|CLRCHN
+
+# Set SCSI bus parity checking and the selection timeout value,
+# and enable the hardware selection timer. Set the SELTO interrupt
+# to signal the driver.
+#
+# STPWEN is 7870-specific, enabling an external termination power source.
+#
+ and A,0x38,SCSICONF # PARITY_ENB_A|SEL_TIM_A[10]
+ or SXFRCTL1,0x5,A # ENSTIMER|STPWEN
+ mvi SIMODE1,0x84 # ENSELTIMO|ENSCSIPERR
+
+# Initialize scatter-gather pointers by setting up the working copy
+# in scratch RAM.
+#
+ call sg_scb2ram
+
+# Initialize SCSIRATE with the appropriate value for this target.
+#
+ call ndx_sdtr
+ mov SCSIRATE,SINDIR
+ ret
+
+# Assert that if we've been reselected, then we've seen an IDENTIFY
+# message.
+#
+assert:
+ test RESELECT,0x80 jz assert1 # reselected?
+ test RESELECT,0x40 jnz assert1 # seen IDENTIFY?
+
+ mvi INTSTAT,SIGNAL_2 # no - cause a kernel panic
+
+assert1:
+ ret
+
+# Find out if disconnection is ok from the information the BIOS has left
+# us. The target ID should be in the upper four bits of SINDEX; A will
+# contain either 0x40 (disconnection ok) or 0x00 (disconnection not ok)
+# on exit.
+#
+# This is the only place the target ID is limited to three bits, so we
+# can use the FUNCTION1 register.
+#
+disconnect:
+ and FUNCTION1,0x70,SINDEX # strip off extra just in case
+ mov A,FUNCTION1
+ test DISC_DSB_A,A jz disconnect1 # bit nonzero if DISabled
+
+ clr A ret
+disconnect1:
+ mvi A,0x40 ret
+
+# Locate the SCB matching the target ID in SELID and the lun in the lower
+# three bits of SINDEX, and switch the SCB to it. Have the kernel print
+# a warning message if it can't be found, and generate an ABORT message
+# to the target.
+#
+findSCB:
+ and A,0x7,SINDEX # lun in lower three bits
+ or A,A,SELID # can I do this?
+ and A,0xf7 # only channel A implemented
+ mov DINDEX,A # save in DINDEX for later
+
+ clr SINDEX
+
+findSCB1:
+ mov A,DINDEX # reload A after 1st iteration
+ mov SCBPTR,SINDEX # switch to new SCB
+ cmp SCBARRAY+1,A jne findSCB2 # target ID/channel/lun match?
+ test SCBARRAY+0,0x4 jz findSCB2 # should be disconnected
+
+ ret
+
+findSCB2:
+ inc SINDEX
+ mov A,SCBCOUNT
+ cmp SINDEX,A jne findSCB1
+
+ mvi INTSTAT,SIGNAL_3 # not found - signal kernel
+ mvi 0x6 call mk_mesg # ABORT message
+
+ or SINDEX,0x10,SIGSTATE # assert ATNO
+ call scsisig
+ ret
+
+# Make a working copy of the scatter-gather parameters in the SCB.
+#
+sg_scb2ram:
+ mov SG_COUNT,SCBARRAY+2
+
+ mvi A,4
+ mvi DINDEX,SG_NEXT
+ mvi SCBARRAY+3 call bcopy
+
+ mvi SG_NOLOAD,0x80
+ test SCBARRAY+0,0x10 jnz sg_scb2ram1 # don't reload s/g?
+ clr SG_NOLOAD
+
+sg_scb2ram1:
+ ret
+
+# Copying RAM values back to SCB, for Save Data Pointers message.
+#
+sg_ram2scb:
+ mov SCBARRAY+2,SG_COUNT
+
+ mvi A,4
+ mvi DINDEX,SCBARRAY+3
+ mvi SG_NEXT call bcopy
+
+ and SCBARRAY+0,0xef,SCBARRAY+0
+ test SG_NOLOAD,0x80 jz sg_ram2scb1 # reload s/g?
+ or SCBARRAY+0,0x10
+
+sg_ram2scb1:
+ ret
+
+# Load a struct scatter if needed and set up the data address and
+# length. If the working value of the SG count is nonzero, then
+# we need to load a new set of values.
+#
+# This, like the above DMA, assumes a little-endian host data storage.
+#
+sg_load:
+ test SG_COUNT,0xff jz sg_load3 # SG being used?
+ test SG_NOLOAD,0x80 jnz sg_load3 # don't reload s/g?
+
+ clr HCNT+2
+ clr HCNT+1
+ mvi HCNT+0,SG_SIZEOF
+
+ mvi A,4
+ mvi DINDEX,HADDR
+ mvi SG_NEXT call bcopy
+
+ mvi DFCNTRL,0xd # HDMAEN|DIRECTION|FIFORESET
+
+# Wait for DMA from host memory to data FIFO to complete, then disable
+# DMA and wait for it to acknowledge that it's off.
+#
+sg_load1:
+ test DFSTATUS,0x8 jz sg_load1 # HDONE
+
+ clr DFCNTRL # disable DMA
+sg_load2:
+ test DFCNTRL,0x8 jnz sg_load2 # HDMAENACK
+
+# Copy data from FIFO into SCB data pointer and data count. This assumes
+# that the struct scatterlist has this structure (this and sizeof(struct
+# scatterlist) == 12 are asserted in aic7xxx.c):
+#
+# struct scatterlist {
+# char *address; /* four bytes, little-endian order */
+# ... /* four bytes, ignored */
+# unsigned short length; /* two bytes, little-endian order */
+# }
+#
+ mov SCBARRAY+19,DFDAT # new data address
+ mov SCBARRAY+20,DFDAT
+ mov SCBARRAY+21,DFDAT
+ mov SCBARRAY+22,DFDAT
+
+ mov NONE,DFDAT # throw away four bytes
+ mov NONE,DFDAT
+ mov NONE,DFDAT
+ mov NONE,DFDAT
+
+ mov SCBARRAY+23,DFDAT
+ mov SCBARRAY+24,DFDAT
+ clr SCBARRAY+25
+
+sg_load3:
+ ret
+
+# Advance the scatter-gather pointers only IF NEEDED. If SG is enabled,
+# and the SCSI transfer count is zero (note that this should be called
+# right after a DMA finishes), then move the working copies of the SG
+# pointer/length along. If the SCSI transfer count is not zero, then
+# presumably the target is disconnecting - do not reload the SG values
+# next time.
+#
+sg_advance:
+ test SG_COUNT,0xff jz sg_advance2 # s/g enabled?
+
+ test STCNT+0,0xff jnz sg_advance1 # SCSI transfer count nonzero?
+ test STCNT+1,0xff jnz sg_advance1
+ test STCNT+2,0xff jnz sg_advance1
+
+ clr SG_NOLOAD # reload s/g next time
+ dec SG_COUNT # one less segment to go
+
+ clr A # add sizeof(struct scatter)
+ add SG_NEXT+0,SG_SIZEOF,SG_NEXT+0
+ adc SG_NEXT+1,A,SG_NEXT+1
+ adc SG_NEXT+2,A,SG_NEXT+2
+ adc SG_NEXT+3,A,SG_NEXT+3
+
+ ret
+
+sg_advance1:
+ mvi SG_NOLOAD,0x80 # don't reload s/g next time
+sg_advance2:
+ ret
+
+# Add the array base SYNCNEG to the target offset (the target address
+# is in SCSIID), and return the result in SINDEX. The accumulator
+# contains the 3->8 decoding of the target ID on return.
+#
+ndx_sdtr:
+ shr A,SCSIID,4
+ and A,0x7
+ add SINDEX,SYNCNEG,A
+
+ and FUNCTION1,0x70,SCSIID # 3-bit target address decode
+ mov A,FUNCTION1 ret
+
+# If we need to negotiate transfer parameters, build the SDTR message
+# starting at the address passed in SINDEX. DINDEX is modified on return.
+#
+mk_sdtr:
+ mov DINDEX,SINDEX # save SINDEX
+
+ call ndx_sdtr
+ test NEEDSDTR,A jnz mk_sdtr1 # do we need negotiation?
+ ret
+
+mk_sdtr1:
+ mvi DINDIR,1 # extended message
+ mvi DINDIR,3 # extended message length = 3
+ mvi DINDIR,1 # SDTR code
+ mvi DINDIR,25 # REQ/ACK transfer period
+ mvi DINDIR,15 # REQ/ACK offset
+
+ add MSG_LEN,-MSG_START+0,DINDEX # update message length
+ ret
+
+# Set SCSI bus control signal state. This also saves the last-written
+# value into a location where the higher-level driver can read it - if
+# it has to send an ABORT or RESET message, then it needs to know this
+# so it can assert ATN without upsetting SCSISIGO. The new value is
+# expected in SINDEX. Change the actual state last to avoid contention
+# from the driver.
+#
+scsisig:
+ mov SIGSTATE,SINDEX
+ mov SCSISIGO,SINDEX ret
/* fdomain.c -- Future Domain TMC-16x0 SCSI driver
* Created: Sun May 3 18:53:19 1992 by faith@cs.unc.edu
- * Revised: Mon Jun 5 09:21:54 1995 by faith@cs.unc.edu
+ * Revised: Fri Jun 23 17:07:09 1995 by r.faith@ieee.org
* Author: Rickard E. Faith, faith@cs.unc.edu
* Copyright 1992, 1993, 1994, 1995 Rickard E. Faith
*
- * $Id: fdomain.c,v 5.28 1995/06/05 13:21:57 faith Exp $
+ * $Id: fdomain.c,v 5.31 1995/06/23 21:07:16 faith Exp $
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
(rsimpson@ewrcsdra.demon.co.uk) for more Quantum signatures and detective
work on the Quantum RAM layout.
+ Special thanks to James T. McKinley (mckinley@msupa.pa.msu.edu) for
+ providing patches for proper PCI BIOS32-mediated detection of the TMC-3260
+ card (a PCI bus card with the 36C70 chip). Please send James PCI-related
+ bug reports.
+
All of the alpha testers deserve much thanks.
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/ioport.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
-#define VERSION "$Revision: 5.28 $"
+#define VERSION "$Revision: 5.31 $"
/* START OF USER DEFINABLE OPTIONS */
printk( " at 0x%x using scsi id %d\n",
(unsigned)bios_base, shpnt->this_id );
+ /* If this driver works for later FD PCI
+ boards, we will have to modify banner
+ for additional PCI cards, but for now if
+ it's PCI it's a TMC-3260 - JTM */
printk( "scsi%d <fdomain>: %s chip at 0x%x irq ",
shpnt->host_no,
chip == tmc1800 ? "TMC-1800"
: (chip == tmc18c50 ? "TMC-18C50"
- : (chip == tmc18c30 ? "TMC-18C30" : "Unknown")),
+ : (chip == tmc18c30 ?
+ (PCI_bus ? "TMC-36C70 (PCI bus)" : "TMC-18C30")
+ : "Unknown")),
port_base );
if (interrupt_level) printk( "%d", interrupt_level );
else printk( "<none>" );
- if (PCI_bus) printk( " (PCI bus)" );
printk( "\n" );
}
static int fdomain_is_valid_port( int port )
{
- int options;
-
#if DEBUG_DETECT
printk( " (%x%x),",
inb( port + MSB_ID_Code ), inb( port + LSB_ID_Code ) );
we'll use the other method.) */
outb( 0x80, port + IO_Control );
- if (inb( port + Configuration2 ) & 0x80 == 0x80) {
+ if ((inb( port + Configuration2 ) & 0x80) == 0x80) {
outb( 0x00, port + IO_Control );
- if (inb( port + Configuration2 ) & 0x80 == 0x00) {
+ if ((inb( port + Configuration2 ) & 0x80) == 0x00) {
chip = tmc18c30;
FIFO_Size = 0x800; /* 2k FIFO */
}
/* If that failed, we are an 18c50. */
}
- /* We have a valid MCA ID for a TMC-1660/TMC-1680 Future Domain board.
- Now, check to be sure the bios_base matches these ports. If someone
- was unlucky enough to have purchased more than one Future Domain
- board, then they will have to modify this code, as we only detect one
- board here. [The one with the lowest bios_base.] */
-
- options = inb( port + Configuration1 );
-
-#if DEBUG_DETECT
- printk( " Options = %x\n", options );
-#endif
-
- /* Check for board with lowest bios_base --
- this isn't valid for the 18c30 or for
- boards on the PCI bus, so just assume we
- have the right board. */
-
- if (chip != tmc18c30
- && !PCI_bus
- && addresses[ (options & 0xc0) >> 6 ] != bios_base) return 0;
-
- /* Get the IRQ from the options. */
-
- interrupt_level = ints[ (options & 0x0e) >> 1 ];
-
return 1;
}
return 0;
}
-int fdomain_16x0_detect( Scsi_Host_Template *tpnt )
+/* fdomain_get_irq assumes that we have a valid MCA ID for a
+ TMC-1660/TMC-1680 Future Domain board. Now, check to be sure the
+ bios_base matches these ports. If someone was unlucky enough to have
+ purchased more than one Future Domain board, then they will have to
+ modify this code, as we only detect one board here. [The one with the
+ lowest bios_base.]
+
+ Note that this routine is only used for systems without a PCI BIOS32
+ (e.g., ISA bus). For PCI bus systems, this routine will likely fail
+ unless one of the IRQs listed in the ints array is used by the board.
+ Sometimes it is possible to use the computer's BIOS setup screen to
+ configure a PCI system so that one of these IRQs will be used by the
+ Future Domain card. */
+
+static int fdomain_get_irq( int base )
{
- int i, j;
- int flag = 0;
- int retcode;
- struct Scsi_Host *shpnt;
-#if DO_DETECT
- const int buflen = 255;
- Scsi_Cmnd SCinit;
- unsigned char do_inquiry[] = { INQUIRY, 0, 0, 0, buflen, 0 };
- unsigned char do_request_sense[] = { REQUEST_SENSE, 0, 0, 0, buflen, 0 };
- unsigned char do_read_capacity[] = { READ_CAPACITY,
- 0, 0, 0, 0, 0, 0, 0, 0, 0 };
- unsigned char buf[buflen];
-#endif
+ int options = inb( base + Configuration1 );
#if DEBUG_DETECT
- printk( "fdomain_16x0_detect()," );
+ printk( " Options = %x\n", options );
#endif
+
+ /* Check for board with lowest bios_base --
+ this isn't valid for the 18c30 or for
+ boards on the PCI bus, so just assume we
+ have the right board. */
- for (i = 0; !bios_base && i < ADDRESS_COUNT; i++) {
-#if DEBUG_DETECT
- printk( " %x(%x),", (unsigned)addresses[i], (unsigned)bios_base );
-#endif
- for (j = 0; !bios_base && j < SIGNATURE_COUNT; j++) {
- if (!memcmp( ((char *)addresses[i] + signatures[j].sig_offset),
- signatures[j].signature, signatures[j].sig_length )) {
- bios_major = signatures[j].major_bios_version;
- bios_minor = signatures[j].minor_bios_version;
- PCI_bus = (signatures[j].flag == 1);
- Quantum = (signatures[j].flag > 1) ? signatures[j].flag : 0;
- bios_base = addresses[i];
- }
- }
- }
+ if (chip != tmc18c30
+ && !PCI_bus
+ && addresses[ (options & 0xc0) >> 6 ] != bios_base) return 0;
- if (!bios_base) {
-#if DEBUG_DETECT
- printk( " FAILED: NO BIOS\n" );
-#endif
- return 0;
- }
+ return ints[ (options & 0x0e) >> 1 ];
+}
+
+static int fdomain_isa_detect( int *irq, int *iobase )
+{
+ int i;
+ int base;
+ int flag = 0;
if (bios_major == 2) {
/* The TMC-1660/TMC-1680 has a RAM area just after the BIOS ROM.
switch (Quantum) {
case 2: /* ISA_200S */
case 3: /* ISA_250MG */
- port_base = *((char *)bios_base + 0x1fa2)
+ base = *((char *)bios_base + 0x1fa2)
+ (*((char *)bios_base + 0x1fa3) << 8);
break;
case 4: /* ISA_200S (another one) */
- port_base = *((char *)bios_base + 0x1fa3)
+ base = *((char *)bios_base + 0x1fa3)
+ (*((char *)bios_base + 0x1fa4) << 8);
break;
default:
- port_base = *((char *)bios_base + 0x1fcc)
+ base = *((char *)bios_base + 0x1fcc)
+ (*((char *)bios_base + 0x1fcd) << 8);
break;
}
#if DEBUG_DETECT
- printk( " %x,", port_base );
+ printk( " %x,", base );
#endif
for (flag = 0, i = 0; !flag && i < PORT_COUNT; i++) {
- if (port_base == ports[i])
+ if (base == ports[i])
++flag;
}
- if (flag)
- flag = fdomain_is_valid_port( port_base );
- }
-
- if (!flag) { /* Cannot get port base from BIOS RAM */
+ if (flag && fdomain_is_valid_port( base )) {
+ *irq = fdomain_get_irq( base );
+ *iobase = base;
+ return 1;
+ }
/* This is a bad sign. It usually means that someone patched the
BIOS signature list (the signatures variable) to contain a BIOS
- signature for a board *OTHER THAN* the TMC-1660/TMC-1680. It
- also means that we don't have a Version 2.0 BIOS :-)
- */
+ signature for a board *OTHER THAN* the TMC-1660/TMC-1680. */
#if DEBUG_DETECT
- if (bios_major != 2) printk( " RAM FAILED, " );
+ printk( " RAM FAILED, " );
#endif
+ }
- /* Anyway, the alternative to finding the address in the RAM is to
- just search through every possible port address for one that is
- attached to the Future Domain card. Don't panic, though, about
- reading all these random port addresses -- there are rumors that
- the Future Domain BIOS does something very similar.
+ /* Anyway, the alternative to finding the address in the RAM is to just
+ search through every possible port address for one that is attached
+ to the Future Domain card. Don't panic, though, about reading all
+ these random port addresses -- there are rumors that the Future
+ Domain BIOS does something very similar.
- Do not, however, check ports which the kernel knows are being used
- by another driver. */
+ Do not, however, check ports which the kernel knows are being used by
+ another driver. */
- if (!PCI_bus) {
- for (i = 0; !flag && i < PORT_COUNT; i++) {
- port_base = ports[i];
- if (check_region( port_base, 0x10 )) {
+ for (i = 0; i < PORT_COUNT; i++) {
+ base = ports[i];
+ if (check_region( base, 0x10 )) {
#if DEBUG_DETECT
- printk( " (%x inuse),", port_base );
+ printk( " (%x inuse),", base );
#endif
- continue;
- }
+ continue;
+ }
+#if DEBUG_DETECT
+ printk( " %x,", base );
+#endif
+ if ((flag = fdomain_is_valid_port( base ))) break;
+ }
+
+ if (!flag) return 0; /* iobase not found */
+
+ *irq = fdomain_get_irq( base );
+ *iobase = base;
+
+ return 1; /* success */
+}
+
+static int fdomain_pci_nobios_detect( int *irq, int *iobase )
+{
+ int i;
+ int flag = 0;
+
+ /* The proper way of doing this is to use ask the PCI bus for the device
+ IRQ and interrupt level. But we can't do that if PCI BIOS32 support
+ isn't compiled into the kernel, or if a PCI BIOS32 isn't present.
+
+ Instead, we scan down a bunch of addresses (Future Domain tech
+ support says we will probably find the address before we get to
+ 0xf800). This works fine on some systems -- other systems may have
+ to scan more addresses. If you have to modify this section for your
+ installation, please send mail to faith@cs.unc.edu. */
+
+ for (i = 0xfff8; i > 0xe000; i -= 8) {
+ if (check_region( i, 0x10 )) {
#if DEBUG_DETECT
- printk( " %x,", port_base );
+ printk( " (%x inuse)," , i );
#endif
- flag = fdomain_is_valid_port( port_base );
+ continue;
+ }
+ if ((flag = fdomain_is_valid_port( i ))) break;
+ }
+
+ if (!flag) return 0; /* iobase not found */
+
+ *irq = fdomain_get_irq( i );
+ *iobase = i;
+
+ return 1; /* success */
+}
+
+/* PCI detection function: int fdomain_36c70_detect(int* irq, int* iobase)
+ This function gets the Interrupt Level and I/O base address from the PCI
+ configuration registers. The I/O base address is masked with 0xfff8
+ since on my card the address read from the PCI config registers is off
+ by one from the actual I/O base address necessary for accessing the
+ status and control registers on the card (PCI config register gives
+ 0xf801, actual address is 0xf800). This is likely a bug in the FD
+ config code that writes to the PCI registers, however using a mask
+ should be safe since I think the scan done by the card to determine the
+ I/O base is done in increments of 8 (i.e., 0xf800, 0xf808, ...), at
+ least the old scan code we used to use to get the I/O base did... Also,
+ the device ID from the PCI config registers is 0x0 and should be 0x60e9
+ as it is in the status registers (offset 5 from I/O base). If this is
+ changed in future hardware/BIOS changes it will need to be fixed in this
+ detection function. Comments, bug reports, etc... on this function
+ should be sent to mckinley@msupa.pa.msu.edu - James T. McKinley. */
+
+#ifdef PCI_CONFIG
+static int fdomain_36c70_detect( int *irq, int *iobase )
+{
+ int error;
+ unsigned char pci_bus, pci_dev_fn; /* PCI bus & device function */
+ unsigned char pci_irq; /* PCI interrupt line */
+ unsigned long pci_base; /* PCI I/O base address */
+ unsigned short pci_vendor, pci_device; /* PCI vendor & device IDs */
+
+ /* If the PCI BIOS doesn't exist, use the old-style detection routines.
+ Otherwise, get the I/O base address and interrupt from the PCI config
+ registers. */
+
+ if (!pcibios_present()) return fdomain_pci_detect( irq, iobase );
+
+#if DEBUG_DETECT
+ /* Tell how to print a list of the known PCI devices from bios32 and
+ list vendor and device IDs being used if in debug mode. */
+
+ printk( "\nINFO: cat /proc/pci to see list of PCI devices from bios32\n" );
+ printk( "\nTMC-3260 detect:"
+ " Using PCI Vendor ID: 0x%x, PCI Device ID: 0x%x\n",
+ PCI_VENDOR_ID_FD,
+ PCI_DEVICE_ID_FD_36C70 );
+#endif
+
+ /* We will have to change this if more than 1 PCI bus is present and the
+ FD scsi host is not on the first bus (i.e., a PCI to PCI bridge,
+ which is not supported by bios32 right now anyway). This should
+ probably be done by a call to pcibios_find_device but I can't get it
+ to work... Also the device ID reported from the PCI config registers
+ does not match the device ID quoted in the tech manual or available
+ from offset 5 from the I/O base address. It should be 0x60E9, but it
+ is 0x0 if read from the PCI config registers. I guess the FD folks
+ neglected to write it to the PCI registers... This loop is necessary
+ to get the device function (at least until someone can get
+ pcibios_find_device to work, I cannot but 53c7,8xx.c uses it...). */
+
+ pci_bus = 0;
+
+ for (pci_dev_fn = 0x0; pci_dev_fn < 0xff; pci_dev_fn++) {
+ pcibios_read_config_word( pci_bus,
+ pci_dev_fn,
+ PCI_VENDOR_ID,
+ &pci_vendor );
+
+ if (pci_vendor == PCI_VENDOR_ID_FD) {
+ pcibios_read_config_word( pci_bus,
+ pci_dev_fn,
+ PCI_DEVICE_ID,
+ &pci_device );
+
+ if (pci_device == PCI_DEVICE_ID_FD_36C70) {
+ /* Break out once we have the correct device. If other FD
+ PCI devices are added to this driver we will need to add
+ an or of the other PCI_DEVICE_ID_FD_XXXXX's here. */
+ break;
+ } else {
+ /* If we can't find an FD scsi card we give up. */
+ return 0;
}
- } else {
+ }
+ }
+
+#if DEBUG_DETECT
+ printk( "Future Domain 36C70 : at PCI bus %u, device %u, function %u\n",
+ pci_bus,
+ (pci_dev_fn & 0xf8) >> 3,
+ pci_dev_fn & 7 );
+#endif
+
+ /* We now have the appropriate device function for the FD board so we
+ just read the PCI config info from the registers. */
+
+ if ((error = pcibios_read_config_dword( pci_bus,
+ pci_dev_fn,
+ PCI_BASE_ADDRESS_0,
+ &pci_base ))
+ || (error = pcibios_read_config_byte( pci_bus,
+ pci_dev_fn,
+ PCI_INTERRUPT_LINE,
+ &pci_irq ))) {
+ printk ( "PCI ERROR: Future Domain 36C70 not initializing"
+ " due to error reading configuration space\n" );
+ return 0;
+ } else {
+#if DEBUG_DETECT
+ printk( "TMC-3260 PCI: IRQ = %u, I/O base = 0x%lx\n",
+ pci_irq, pci_base );
+#endif
- /* The proper way of doing this is to use ask the PCI bus for the
- device IRQ and interrupt level.
+ /* Now we have the I/O base address and interrupt from the PCI
+ configuration registers. Unfortunately it seems that the I/O base
+ address is off by one on my card so I mask it with 0xfff8. This
+ must be some kind of goof in the FD code that does the autoconfig
+ and writes to the PCI registers (or maybe I just don't understand
+ something). If they fix it in later versions of the card or BIOS
+ we may have to adjust the address based on the signature or
+ something... */
- Until the Linux kernel supports this sort of PCI bus query, we
- scan down a bunch of addresses (Future Domain tech support says
- we will probably find the address before we get to 0xf800).
- This works fine on some systems -- other systems may have to
- scan more addresses. If you have to modify this section for
- your installation, please send mail to faith@cs.unc.edu. */
+ *irq = pci_irq;
+ *iobase = (pci_base & 0xfff8);
- for (i = 0xfff8; !flag && i > 0xe000; i -= 8) {
- port_base = i;
- if (check_region( port_base, 0x10 )) {
#if DEBUG_DETECT
- printk( " (%x inuse)," , port_base );
+ printk( "TMC-3260 fix: Masking I/O base address with 0xff00.\n" );
+ printk( "TMC-3260: IRQ = %d, I/O base = 0x%x\n", *irq, *iobase );
#endif
- continue;
- }
- flag = fdomain_is_valid_port( port_base );
+
+ if (!fdomain_is_valid_port( *iobase )) return 0;
+ return 1;
+ }
+ return 0;
+}
+#endif
+
+int fdomain_16x0_detect( Scsi_Host_Template *tpnt )
+{
+ int i, j;
+ int flag = 0;
+ int retcode;
+ struct Scsi_Host *shpnt;
+#if DO_DETECT
+ const int buflen = 255;
+ Scsi_Cmnd SCinit;
+ unsigned char do_inquiry[] = { INQUIRY, 0, 0, 0, buflen, 0 };
+ unsigned char do_request_sense[] = { REQUEST_SENSE, 0, 0, 0, buflen, 0 };
+ unsigned char do_read_capacity[] = { READ_CAPACITY,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ unsigned char buf[buflen];
+#endif
+
+#if DEBUG_DETECT
+ printk( "fdomain_16x0_detect()," );
+#endif
+
+ for (i = 0; !bios_base && i < ADDRESS_COUNT; i++) {
+#if DEBUG_DETECT
+ printk( " %x(%x),", (unsigned)addresses[i], (unsigned)bios_base );
+#endif
+ for (j = 0; !bios_base && j < SIGNATURE_COUNT; j++) {
+ if (!memcmp( ((char *)addresses[i] + signatures[j].sig_offset),
+ signatures[j].signature, signatures[j].sig_length )) {
+ bios_major = signatures[j].major_bios_version;
+ bios_minor = signatures[j].minor_bios_version;
+ PCI_bus = (signatures[j].flag == 1);
+ Quantum = (signatures[j].flag > 1) ? signatures[j].flag : 0;
+ bios_base = addresses[i];
}
}
}
+ if (!bios_base) {
+#if DEBUG_DETECT
+ printk( " FAILED: NO BIOS\n" );
+#endif
+ return 0;
+ }
+
+ if (!PCI_bus) {
+ flag = fdomain_isa_detect( &interrupt_level, &port_base );
+ } else {
+#ifdef PCI_CONFIG
+ flag = fdomain_pci_bios_detect( &interrupt_level, &port_base );
+#else
+ flag = fdomain_pci_nobios_detect( &interrupt_level, &port_base );
+#endif
+ }
+
if (!flag) {
#if DEBUG_DETECT
printk( " FAILED: NO PORT\n" );
+#endif
+#ifdef PCI_CONFIG
+ printk( "\nTMC-3260 36C70 PCI scsi chip detection failed.\n" );
+ printk( "Send mail to mckinley@msupa.pa.msu.edu.\n" );
#endif
return 0; /* Cannot find valid set of ports */
}
#include "aha1740.h"
#endif
-#ifdef CONFIG_SCSI_AHA274X
-#include "aha274x.h"
+#ifdef CONFIG_SCSI_AIC7XXX
+#include "aic7xxx.h"
#endif
#ifdef CONFIG_SCSI_BUSLOGIC
#ifdef CONFIG_SCSI_AHA1740
AHA1740,
#endif
-#ifdef CONFIG_SCSI_AHA274X
- AHA274X,
+#ifdef CONFIG_SCSI_AIC7XXX
+ AIC7XXX,
#endif
#ifdef CONFIG_SCSI_FUTURE_DOMAIN
FDOMAIN_16X0,
@label_patches = ();
+@external_patches = ();
+
@absolute = sort @absolute;
foreach $i (@absolute) {
open (OUTPUT, ">$output") || die "$0 : can't open $output for writing\n";
open (OUTPUTU, ">$outputu") || die "$0 : can't open $outputu for writing\n";
-print OUTPUT "unsigned long ".$prefix."SCRIPT[] = {\n";
+print OUTPUT "u32 ".$prefix."SCRIPT[] = {\n";
$instructions = 0;
for ($i = 0; $i < $#code; ) {
if ($list_in_array) {
printf OUTPUT "\t0x%08x,", $code[$i];
printf STDERR "Address $i = %x\n", $code[$i] if ($debug);
if ($code[$i + 1] =~ /\s*($identifier)(.*)$/) {
- printf OUTPUT "((unsigned long)&%s)%s,", $1, $2
+ push (@external_patches, $i+1, $1);
+ printf OUTPUT "0%s,", $2
} else {
printf OUTPUT "0x%08x,",$code[$i+1];
}
if (($code[$i] & 0xff_00_00_00) == 0xc0_00_00_00) {
if ($code[$i + 2] =~ /$identifier/) {
- printf OUTPUT "(unsigned long)&%s,\n",$code[$i+2];
+ push (@external_patches, $i+2, $code[$i+2]);
+ printf OUTPUT "0,\n";
} else {
printf OUTPUT "0x%08x,\n",$code[$i+2];
}
}
printf OUTPUTU "#undef A_$i\n";
- printf OUTPUT "unsigned long A_".$i."_used\[\] = {\n";
+ printf OUTPUT "u32 A_".$i."_used\[\] = {\n";
printf STDERR "$i is used $symbol_references{$i}\n" if ($debug);
foreach $j (split (/\s+/,$symbol_references{$i})) {
$j =~ /(ABS|REL),(.*),(.*)/;
# NCR assembler outputs label patches in the form of indices into
# the code.
#
-printf OUTPUT "unsigned long ".$prefix."LABELPATCHES[] = {\n";
+printf OUTPUT "u32 ".$prefix."LABELPATCHES[] = {\n";
for $patch (sort {$a <=> $b} @label_patches) {
printf OUTPUT "\t0x%08x,\n", $patch;
}
printf OUTPUT "};\n\n";
-printf OUTPUT "unsigned long ".$prefix."INSTRUCTIONS\t= 0x%08x;\n",
+$num_external_patches = 0;
+printf OUTPUT "struct {\n\tu32\toffset;\n\tvoid\t\t*address;\n".
+ "} ".$prefix."EXTERNAL_PATCHES[] = {\n";
+while ($ident = pop(@external_patches)) {
+ $off = pop(@external_patches);
+ printf OUTPUT "\t{0x%08x, &%s},\n", $off, $ident;
+ ++$num_external_patches;
+}
+printf OUTPUT "};\n\n";
+
+printf OUTPUT "u32 ".$prefix."INSTRUCTIONS\t= %d;\n",
$instructions;
-printf OUTPUT "unsigned long ".$prefix."PATCHES\t= 0x%08x;\n",
+printf OUTPUT "u32 ".$prefix."PATCHES\t= %d;\n",
$#label_patches+1;
+printf OUTPUT "u32 ".$prefix."EXTERNAL_PATCHES_LEN\t= %d;\n",
+ $num_external_patches;
close OUTPUT;
close OUTPUTU;
save_flags(flags);
cli();
- if(dma_malloc_freelist[page] & (mask << sector) != (mask<<sector))
+ if((dma_malloc_freelist[page] & (mask << sector)) != (mask<<sector))
panic("Trying to free unused memory");
dma_free_sectors += nbits;
unsigned int length;
};
-#define ISA_DMA_THRESHOLD (0x00ffffff)
+#ifdef __alpha__
+# define ISA_DMA_THRESHOLD (~0UL)
+#else
+# define ISA_DMA_THRESHOLD (0x00ffffff)
+#endif
#define CONTIGUOUS_BUFFERS(X,Y) ((X->b_data+X->b_size) == Y->b_data)
const char * string;
if ((temp = host->hostt->present) && buffer) {
- len = get_fs_long ((unsigned long *) buffer);
+ len = get_user ((unsigned int *) buffer);
if(host->hostt->info)
string = host->hostt->info(host);
else
if (!buffer)
return -EINVAL;
- inlen = get_fs_long((unsigned long *) buffer);
- outlen = get_fs_long( ((unsigned long *) buffer) + 1);
+ inlen = get_user((unsigned int *) buffer);
+ outlen = get_user( ((unsigned int *) buffer) + 1);
cmd_in = (char *) ( ((int *)buffer) + 2);
- opcode = get_fs_byte(cmd_in);
+ opcode = get_user(cmd_in);
needed = buf_needed = (inlen > outlen ? inlen : outlen);
if(buf_needed){
switch (cmd) {
case SCSI_IOCTL_GET_IDLUN:
verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
- put_fs_long(dev->id + (dev->lun << 8) +
- (dev->host->host_no << 16), (unsigned long *) arg);
+ put_user(dev->id + (dev->lun << 8) +
+ (dev->host->host_no << 16), (unsigned int *) arg);
return 0;
case SCSI_IOCTL_TAGGED_ENABLE:
if(!suser()) return -EACCES;
host->hostt->bios_param(&rscsi_disks[MINOR(dev) >> 4],
dev,
&diskinfo[0]);
- put_fs_byte(diskinfo[0],
- (char *) &loc->heads);
- put_fs_byte(diskinfo[1],
- (char *) &loc->sectors);
- put_fs_word(diskinfo[2],
- (short *) &loc->cylinders);
- put_fs_long(sd[MINOR(inode->i_rdev)].start_sect,
- (long *) &loc->start);
+ put_user(diskinfo[0], &loc->heads);
+ put_user(diskinfo[1], &loc->sectors);
+ put_user(diskinfo[2], &loc->cylinders);
+ put_user(sd[MINOR(inode->i_rdev)].start_sect, &loc->start);
return 0;
case BLKGETSIZE: /* Return device size */
if (!arg) return -EINVAL;
error = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
if (error)
return error;
- put_fs_long(sd[MINOR(inode->i_rdev)].nr_sects,
- (long *) arg);
+ put_user(sd[MINOR(inode->i_rdev)].nr_sects,
+ (long *) arg);
return 0;
case BLKRASET:
if(!suser()) return -EACCES;
switch(cmd_in)
{
case SG_SET_TIMEOUT:
- scsi_generics[dev].timeout=get_fs_long((int *) arg);
+ scsi_generics[dev].timeout=get_user((int *) arg);
return 0;
case SG_GET_TIMEOUT:
return scsi_generics[dev].timeout;
/* now issue command */
SCpnt->request.dev=dev;
SCpnt->sense_buffer[0]=0;
- opcode = get_fs_byte(buf);
+ opcode = get_user(buf);
size=COMMAND_SIZE(opcode);
if (opcode >= 0xc0 && device->header.twelve_byte) size = 12;
SCpnt->cmd_len = size;
return result;
case CDROMEJECT:
- if (scsi_CDs[target].device -> access_count == 1)
- sr_ioctl (inode, NULL, SCSI_IOCTL_DOORUNLOCK, 0);
+ if (scsi_CDs[target].device -> access_count != 1)
+ return -EBUSY;
+ sr_ioctl (inode, NULL, SCSI_IOCTL_DOORUNLOCK, 0);
sr_cmd[0] = START_STOP;
sr_cmd[1] = ((scsi_CDs[target].device -> lun) << 5) | 1;
sr_cmd[2] = sr_cmd[3] = sr_cmd[5] = 0;
static void padzero(unsigned long elf_bss)
{
- unsigned long fpnt, nbyte;
+ unsigned long nbyte;
+ char * fpnt;
nbyte = elf_bss & (PAGE_SIZE-1);
if (nbyte) {
nbyte = PAGE_SIZE - nbyte;
verify_area(VERIFY_WRITE, (void *) elf_bss, nbyte);
- fpnt = elf_bss;
+ fpnt = (char *) elf_bss;
do {
- put_fs_byte(0, fpnt++);
+ put_user(0, fpnt++);
} while (--nbyte);
}
}
sp -= argc+1;
argv = sp;
if (!ibcs) {
- put_fs_long((unsigned long)envp,--sp);
- put_fs_long((unsigned long)argv,--sp);
+ put_user(envp,--sp);
+ put_user(argv,--sp);
}
/* The constant numbers (0-9) that we are writing here are
if(exec) { /* Put this here for an ELF program interpreter */
struct elf_phdr * eppnt;
eppnt = (struct elf_phdr *) exec->e_phoff;
- put_fs_long(3,dlinfo++); put_fs_long(load_addr + exec->e_phoff,dlinfo++);
- put_fs_long(4,dlinfo++); put_fs_long(sizeof(struct elf_phdr),dlinfo++);
- put_fs_long(5,dlinfo++); put_fs_long(exec->e_phnum,dlinfo++);
- put_fs_long(9,dlinfo++); put_fs_long((unsigned long) exec->e_entry,dlinfo++);
- put_fs_long(7,dlinfo++); put_fs_long(interp_load_addr,dlinfo++);
- put_fs_long(8,dlinfo++); put_fs_long(0,dlinfo++);
- put_fs_long(6,dlinfo++); put_fs_long(PAGE_SIZE,dlinfo++);
- put_fs_long(0,dlinfo++); put_fs_long(0,dlinfo++);
+ put_user(3,dlinfo++); put_user(load_addr + exec->e_phoff,dlinfo++);
+ put_user(4,dlinfo++); put_user(sizeof(struct elf_phdr),dlinfo++);
+ put_user(5,dlinfo++); put_user(exec->e_phnum,dlinfo++);
+ put_user(9,dlinfo++); put_user((unsigned long) exec->e_entry,dlinfo++);
+ put_user(7,dlinfo++); put_user(interp_load_addr,dlinfo++);
+ put_user(8,dlinfo++); put_user(0,dlinfo++);
+ put_user(6,dlinfo++); put_user(PAGE_SIZE,dlinfo++);
+ put_user(0,dlinfo++); put_user(0,dlinfo++);
}
- put_fs_long((unsigned long)argc,--sp);
+ put_user((unsigned long)argc,--sp);
current->mm->arg_start = (unsigned long) p;
while (argc-->0) {
- put_fs_long((unsigned long) p,argv++);
- while (get_fs_byte(p++)) /* nothing */ ;
+ put_user(p,argv++);
+ while (get_user(p++)) /* nothing */ ;
}
- put_fs_long(0,argv);
+ put_user(0,argv);
current->mm->arg_end = current->mm->env_start = (unsigned long) p;
while (envc-->0) {
- put_fs_long((unsigned long) p,envp++);
- while (get_fs_byte(p++)) /* nothing */ ;
+ put_user(p,envp++);
+ while (get_user(p++)) /* nothing */ ;
}
- put_fs_long(0,envp);
+ put_user(0,envp);
current->mm->env_end = (unsigned long) p;
return sp;
}
if(k > elf_bss) elf_bss = k;
SYS(close)(fd);
- if (error != elf_phdata->p_vaddr & 0xfffff000) {
+ if (error != (elf_phdata->p_vaddr & 0xfffff000)) {
kfree(elf_phdata);
MOD_DEC_USE_COUNT;
return error;
brelse(*bhe);
buf += chars;
} else {
- while (chars-->0)
- put_fs_byte(0,buf++);
+ while (chars-- > 0)
+ put_user(0,buf++);
}
offset = 0;
if (++bhe == &buflist[NBUF])
* Priority tells the routine how hard to try to shrink the
* buffers: 3 means "don't bother too much", while a value
* of 0 means "we'd better get some free pages now".
+ *
+ * "limit" is meant to limit the shrink-action only to pages
+ * that are in the 0 - limit address range, for DMA re-allocations.
+ * We ignore that right now.
*/
-int shrink_buffers(unsigned int priority)
+int shrink_buffers(unsigned int priority, unsigned long limit)
{
if (priority < 2) {
sync_buffers(0,0);
current->mm->arg_start = (unsigned long) p;
while (argc-->0) {
put_user(p,argv++);
- while (get_fs_byte(p++)) /* nothing */ ;
+ while (get_user(p++)) /* nothing */ ;
}
put_user(NULL,argv);
current->mm->arg_end = current->mm->env_start = (unsigned long) p;
while (envc-->0) {
put_user(p,envp++);
- while (get_fs_byte(p++)) /* nothing */ ;
+ while (get_user(p++)) /* nothing */ ;
}
put_user(NULL,envp);
current->mm->env_end = (unsigned long) p;
error = verify_area(VERIFY_READ, tmp, sizeof(char *));
if (error)
return error;
- while ((p = (char *) get_user(tmp++)) != NULL) {
+ while ((p = get_user(tmp++)) != NULL) {
i++;
error = verify_area(VERIFY_READ, p, 1);
if (error)
while (argc-- > 0) {
if (from_kmem == 1)
set_fs(new_fs);
- if (!(tmp = (char *)get_user(argv+argc)))
+ if (!(tmp = get_user(argv+argc)))
panic("VFS: argc is wrong");
if (from_kmem == 1)
set_fs(old_fs);
len=0; /* remember zero-padding */
do {
len++;
- } while (get_fs_byte(tmp++));
+ } while (get_user(tmp++));
if (p < len) { /* this shouldn't happen - 128kB */
set_fs(old_fs);
return 0;
set_fs(new_fs);
}
- *(pag + offset) = get_fs_byte(tmp);
+ *(pag + offset) = get_user(tmp);
}
}
if (from_kmem==2)
buf += chars;
} else {
while (chars-->0)
- put_fs_byte(0,buf++);
+ put_user(0,buf++);
}
offset = 0;
if (++bhe == &buflist[NBUF])
i = 0;
while (i<buflen && (c = bh->b_data[i])) {
i++;
- put_fs_byte(c,buffer++);
+ put_user(c,buffer++);
}
brelse(bh);
return i;
buf += chars;
} else {
while (chars-- > 0)
- put_fs_byte (0, buf++);
+ put_user (0, buf++);
}
offset = 0;
if (++bhe == &buflist[NBUF])
i = 0;
while (i < buflen && (c = link[i])) {
i++;
- put_fs_byte (c, buffer++);
+ put_user (c, buffer++);
}
iput (inode);
if (bh)
unsigned c = *in++;
if (c == '\r' && (len == 0 || *in == '\n'));
else
- put_fs_byte(c, out++);
+ put_user(c, out++);
}
return out - start;
if(mode == ISOFS_FILE_TEXT_M) outchar = 0x0a;
if(mode == ISOFS_FILE_TEXT) outchar = ' ';
}
- put_fs_byte(outchar, outbuf++);
+ put_user(outchar, outbuf++);
buffer++;
}
}
buf += chars;
} else {
while (chars-->0)
- put_fs_byte(0,buf++);
+ put_user(0,buf++);
}
offset = 0;
if (++bhe == &buflist[NBUF])
while (i<buflen && (c = pnt[i])) {
i++;
- put_fs_byte(c,buffer++);
+ put_user(c,buffer++);
}
kfree(pnt);
return i;
buf += chars;
} else {
while (chars-->0)
- put_fs_byte(0,buf++);
+ put_user(0,buf++);
}
offset = 0;
if (++bhe == &buflist[NBUF])
i = 0;
while (i<buflen && (c = bh->b_data[i])) {
i++;
- put_fs_byte(c,buffer++);
+ put_user(c,buffer++);
}
brelse(bh);
return i;
return -ENOENT;
bh = NULL;
while ((ino = msdos_get_entry(inode,&filp->f_pos,&bh,&de)) > -1) {
- if (!IS_FREE(de->name) && !(de->attr & ATTR_VOLUME)) {
+ if (!IS_FREE(de->name)
+ && !(de->attr & (ATTR_VOLUME|ATTR_SYS|ATTR_HIDDEN))) {
char bufname[12];
char *ptname = bufname;
for (i = last = 0; i < 8; i++) {
filp->f_pos = inode->i_size;
break;
}else if (ch != '\r'){
- put_fs_byte(ch,buf++);
+ put_user(ch,buf++);
}
}
}
carry = 0;
}
for (size = 0; size < count && left; size++) {
- if ((ch = get_fs_byte(buf++)) == '\n') {
+ if ((ch = get_user(buf++)) == '\n') {
*to++ = '\r';
left--;
}
error = -ENAMETOOLONG;
i = PAGE_SIZE;
}
- c = get_fs_byte(filename++);
+ c = get_user(filename++);
if (!c)
return -ENOENT;
if(!(page = __get_free_page(GFP_KERNEL)))
*result = tmp = (char *) page;
while (--i) {
*(tmp++) = c;
- c = get_fs_byte(filename++);
+ c = get_user(filename++);
if (!c) {
*tmp = '\0';
return 0;
iput(inode);
if (! error) {
memcpy_tofs(buffer, res, len);
- put_fs_byte('\0', buffer + len);
+ put_user('\0', buffer + len);
error = len;
}
kfree(mem);
iput(inode);
return error;
}
- actime = get_fs_long((unsigned long *) ×->actime);
- modtime = get_fs_long((unsigned long *) ×->modtime);
+ actime = get_user(×->actime);
+ modtime = get_user(×->modtime);
newattrs.ia_ctime = CURRENT_TIME;
flags = ATTR_ATIME_SET | ATTR_MTIME_SET;
} else {
case FIONREAD:
error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
if (!error)
- put_fs_long(PIPE_SIZE(*pino),(int *) arg);
+ put_user(PIPE_SIZE(*pino),(int *) arg);
return error;
default:
return -EINVAL;
}
while (p < 2*PAGE_SIZE && count > 0) {
- put_fs_byte(0,buf);
+ put_user(0,buf);
buf++;
p++;
count--;
read = 0;
while (p < sizeof(unsigned long) && count > 0) {
- put_fs_byte(*((char *)(&sample_step)+p),buf);
+ put_user(*((char *)(&sample_step)+p),buf);
buf++; p++; count--; read++;
}
pnt = (char *)prof_buffer + p - sizeof(unsigned long);
return;
}
ino &= 0x0000ffff;
- if (p->dumpable) {
+ if (p->dumpable && p->uid == p->euid && p->gid == p->egid) {
inode->i_uid = p->uid;
inode->i_gid = p->gid;
}
buflen = i;
i = 0;
while (i < buflen)
- put_fs_byte(buf[i++],buffer++);
+ put_user(buf[i++],buffer++);
return i;
}
if ((err = verify_area(VERIFY_WRITE, result, sizeof(loff_t))))
return err;
offset = (loff_t) (((unsigned long long) offset_high << 32) | offset_low);
-/* there is no fs specific llseek handler */
+
+ /* if there is a fs-specific handler, we can't just ignore it.. */
+ /* accept llseek() only for the signed long subset of long long */
+ if (file->f_op && file->f_op->lseek) {
+ if (offset != (long) offset)
+ return -EINVAL;
+ return file->f_op->lseek(file->f_inode,file,offset,origin);
+ }
+
switch (origin) {
case 0:
tmp = offset;
}
if (tmp < 0)
return -EINVAL;
- file->f_pos = tmp;
- file->f_reada = 0;
- file->f_version = ++event;
+ if (tmp != file->f_pos) {
+ file->f_pos = tmp;
+ file->f_reada = 0;
+ file->f_version = ++event;
+ }
memcpy_tofs(result, &file->f_pos, sizeof(loff_t));
return 0;
}
int count;
};
-static int fillonedir(void * __buf, char * name, int namlen, off_t offset, ino_t ino)
+static int fillonedir(void * __buf, const char * name, int namlen, off_t offset, ino_t ino)
{
struct readdir_callback * buf = (struct readdir_callback *) __buf;
struct old_linux_dirent * dirent;
int error;
};
-static int filldir(void * __buf, char * name, int namlen, off_t offset, ino_t ino)
+static int filldir(void * __buf, const char * name, int namlen, off_t offset, ino_t ino)
{
struct linux_dirent * dirent;
struct getdents_callback * buf = (struct getdents_callback *) __buf;
i = verify_area(VERIFY_WRITE, tvp, sizeof(*tvp));
if (i)
return i;
- timeout = ROUND_UP(get_fs_long((unsigned long *)&tvp->tv_usec),(1000000/HZ));
- timeout += get_fs_long((unsigned long *)&tvp->tv_sec) * HZ;
+ timeout = ROUND_UP(get_user(&tvp->tv_usec),(1000000/HZ));
+ timeout += get_user(&tvp->tv_sec) * (unsigned long) HZ;
if (timeout)
timeout += jiffies + 1;
}
if ((long) timeout < 0)
timeout = 0;
if (tvp && !(current->personality & STICKY_TIMEOUTS)) {
- put_fs_long(timeout/HZ, (unsigned long *) &tvp->tv_sec);
+ put_user(timeout/HZ, &tvp->tv_sec);
timeout %= HZ;
timeout *= (1000000/HZ);
- put_fs_long(timeout, (unsigned long *) &tvp->tv_usec);
+ put_user(timeout, &tvp->tv_usec);
}
if (i < 0)
return i;
buf += chars;
} else {
while (chars-- > 0)
- put_fs_byte(0,buf++);
+ put_user(0,buf++);
}
offset = 0;
if (++bhe == &buflist[NBUF])
i = 0;
while (i<buflen && (c = bh_data[i])) {
i++;
- put_fs_byte(c,buffer++);
+ put_user(c,buffer++);
}
brelse(bh);
return i;
{
return -EISDIR;
}
-#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))
-#define ROUND_UP(x) (((x)+3) & ~3)
+
+struct UMSDOS_DIR_ONCE {
+ void *dirbuf;
+ filldir_t filldir;
+ int count;
+ int stop;
+};
+
+/*
+ Record a single entry the first call.
+ Return -EINVAL the next one.
+*/
+static int umsdos_dir_once(
+ void * buf,
+ char * name,
+ int name_len,
+ off_t offset,
+ ino_t ino)
+{
+ int ret = -EINVAL;
+ struct UMSDOS_DIR_ONCE *d = (struct UMSDOS_DIR_ONCE *)buf;
+ if (d->count == 0){
+ char zname[100];
+ memcpy (zname,name,name_len);
+ zname[name_len] = '\0';
+ PRINTK (("dir_once :%s: offset %ld\n",zname,offset));
+ ret = d->filldir (d->dirbuf,name,name_len,offset,ino);
+ d->stop = ret < 0;
+ d->count = 1;
+ }
+ return ret;
+}
/*
Read count directory entries from directory filp
Return a negative value from linux/errno.h.
- Return > 0 if success (The amount of byte written in
- dirent round_up to a word size (32 bits).
+ Return > 0 if success (The amount of byte written by filldir).
This function is used by the normal readdir VFS entry point and by
some function who try to find out info on a file from a pure MSDOS
static int umsdos_readdir_x(
struct inode *dir, /* Point to a description of the super block */
struct file *filp, /* Point to a directory which is read */
- struct dirent *dirent, /* Will hold count directory entry */
- int dirent_in_fs, /* dirent point in user's space ? */
- int count,
+ void *dirbuf, /* Will hold count directory entry */
+ /* but filled by the filldir function */
+ int internal_read, /* Called for internal purpose */
struct umsdos_dirent *u_entry, /* Optional umsdos entry */
int follow_hlink,
- off_t *pt_f_pos) /* will hold the offset of the entry in EMD */
+ filldir_t filldir)
{
int ret = 0;
umsdos_startlookup(dir);
if (filp->f_pos == UMSDOS_SPECIAL_DIRFPOS
&& dir == pseudo_root
- && dirent_in_fs){
+ && !internal_read){
/*
We don't need to simulate this pseudo directory
when umsdos_readdir_x is called for internal operation
linux root), it simulate a directory /DOS which points to
the real root of the file system.
*/
- put_fs_long(dir->i_sb->s_mounted->i_ino,&dirent->d_ino);
- memcpy_tofs (dirent->d_name,"DOS",3);
- put_fs_byte(0,dirent->d_name+3);
- put_fs_word (3,&dirent->d_reclen);
- if (u_entry != NULL) u_entry->flags = 0;
- ret = ROUND_UP(NAME_OFFSET(dirent) + 3 + 1);
- filp->f_pos++;
+ if (filldir (dirbuf,"DOS",3,UMSDOS_SPECIAL_DIRFPOS
+ ,dir->i_sb->s_mounted->i_ino) == 0){
+ filp->f_pos++;
+ }
}else if (filp->f_pos < 2
|| (dir != dir->i_sb->s_mounted && filp->f_pos == 32)){
/* #Specification: readdir / . and ..
EMD, we are back at offset 64. So we set the offset
to UMSDOS_SPECIAL_DIRFPOS(3) as soon as we have read the
.. entry from msdos.
+
+ Now (linux 1.3), umsdos_readdir can read more than one
+ entry even if we limit (umsdos_dir_once) to only one:
+ It skips over hidden file. So we switch to
+ UMSDOS_SPECIAL_DIRFPOS as soon as we have read successfully
+ the .. entry.
*/
- ret = msdos_readdir(dir,filp,dirent,count);
- if (filp->f_pos == 64) filp->f_pos = UMSDOS_SPECIAL_DIRFPOS;
+ int last_f_pos = filp->f_pos;
+ struct UMSDOS_DIR_ONCE bufk;
+ bufk.dirbuf = dirbuf;
+ bufk.filldir = filldir;
+ bufk.count = 0;
+ ret = msdos_readdir(dir,filp,&bufk,umsdos_dir_once);
+ if (last_f_pos > 0 && filp->f_pos > last_f_pos) filp->f_pos = UMSDOS_SPECIAL_DIRFPOS;
if (u_entry != NULL) u_entry->flags = 0;
}else{
struct inode *emd_dir = umsdos_emd_dir_lookup(dir,0);
if (emd_dir != NULL){
if (filp->f_pos <= UMSDOS_SPECIAL_DIRFPOS+1) filp->f_pos = 0;
- PRINTK (("f_pos %ld i_size %d\n",filp->f_pos,emd_dir->i_size));
+ PRINTK (("f_pos %lu i_size %ld\n",filp->f_pos,emd_dir->i_size));
ret = 0;
while (filp->f_pos < emd_dir->i_size){
struct umsdos_dirent entry;
int lret;
umsdos_parse (entry.name,entry.name_len,&info);
info.f_pos = cur_f_pos;
- *pt_f_pos = cur_f_pos;
umsdos_manglename (&info);
lret = umsdos_real_lookup (dir,info.fake.fname
,info.fake.len,&inode);
infinite recursion /DOS/linux/DOS/linux while
walking the file system.
*/
- if (inode != pseudo_root){
- PRINTK (("Trouve ino %d ",inode->i_ino));
- if (dirent_in_fs){
- put_fs_long(inode->i_ino,&dirent->d_ino);
- memcpy_tofs (dirent->d_name,entry.name
- ,entry.name_len);
- put_fs_byte(0,dirent->d_name+entry.name_len);
- put_fs_word (entry.name_len
- ,&dirent->d_reclen);
- /* In this case, the caller only needs */
- /* flags */
- if (u_entry != NULL){
- u_entry->flags = entry.flags;
- }
- }else{
- dirent->d_ino = inode->i_ino;
- memcpy (dirent->d_name,entry.name
- ,entry.name_len);
- dirent->d_name[entry.name_len] = '\0';
- dirent->d_reclen = entry.name_len;
- if (u_entry != NULL) *u_entry = entry;
+ if (inode != pseudo_root
+ && (internal_read
+ || !(entry.flags & UMSDOS_HIDDEN))){
+ if (filldir (dirbuf
+ ,entry.name,entry.name_len
+ ,cur_f_pos, inode->i_ino) < 0){
+ filp->f_pos = cur_f_pos;
}
- ret = ROUND_UP(NAME_OFFSET(dirent) + entry.name_len + 1);
+ PRINTK (("Trouve ino %ld ",inode->i_ino));
+ if (u_entry != NULL) *u_entry = entry;
iput (inode);
break;
}
}
}
}
+ /*
+ If the fillbuf has failed, f_pos is back to 0.
+ To avoid getting back into the . and .. state
+ (see comments at the beginning), we put back
+ the special offset.
+ */
+ if (filp->f_pos == 0) filp->f_pos = UMSDOS_SPECIAL_DIRFPOS;
iput(emd_dir);
}
}
umsdos_endlookup(dir);
- PRINTK (("read dir %p pos %d ret %d\n",dir,filp->f_pos,ret));
+ PRINTK (("read dir %p pos %lu ret %d\n",dir,filp->f_pos,ret));
return ret;
}
/*
static int UMSDOS_readdir(
struct inode *dir, /* Point to a description of the super block */
struct file *filp, /* Point to a directory which is read */
- struct dirent *dirent, /* Will hold count directory entry */
- int count)
+ void *dirbuf, /* Will hold directory entries */
+ filldir_t filldir)
{
- int ret = -ENOENT;
- while (1){
+ int ret = 0;
+ int count = 0;
+ struct UMSDOS_DIR_ONCE bufk;
+ bufk.dirbuf = dirbuf;
+ bufk.filldir = filldir;
+ bufk.stop = 0;
+ PRINTK (("UMSDOS_readdir in\n"));
+ while (ret == 0 && bufk.stop == 0){
struct umsdos_dirent entry;
- off_t f_pos;
- ret = umsdos_readdir_x (dir,filp,dirent,1,count,&entry,1,&f_pos);
- if (ret <= 0 || !(entry.flags & UMSDOS_HIDDEN)) break;
+ bufk.count = 0;
+ ret = umsdos_readdir_x (dir,filp,&bufk,0,&entry,1,umsdos_dir_once);
+ if (bufk.count == 0) break;
+ count += bufk.count;
}
- return ret;
+ PRINTK (("UMSDOS_readdir out %d count %d pos %lu\n",ret,count
+ ,filp->f_pos));
+ return count == 0 ? -ENOENT : ret;
}
/*
Complete the inode content with info from the EMD file
if (inode->u.umsdos_i.i_emd_owner==0) printk ("emd_owner still 0 ???\n");
}
}
+struct UMSDOS_DIRENT_K{
+ off_t f_pos; /* will hold the offset of the entry in EMD */
+ ino_t ino;
+};
+
+/*
+ Just to record the offset of one entry.
+*/
+static int umsdos_filldir_k(
+ void * buf,
+ char * name,
+ int name_len,
+ off_t offset,
+ ino_t ino)
+{
+ struct UMSDOS_DIRENT_K *d = (struct UMSDOS_DIRENT_K *)buf;
+ d->f_pos = offset;
+ d->ino = ino;
+ return 0;
+}
+
+struct UMSDOS_DIR_SEARCH{
+ struct umsdos_dirent *entry;
+ int found;
+ ino_t search_ino;
+};
+
+static int umsdos_dir_search (
+ void * buf,
+ char * name,
+ int name_len,
+ off_t offset,
+ ino_t ino)
+{
+ int ret = 0;
+ struct UMSDOS_DIR_SEARCH *d = (struct UMSDOS_DIR_SEARCH *)buf;
+ if (d->search_ino == ino){
+ d->found = 1;
+ memcpy (d->entry->name,name,name_len);
+ d->entry->name[name_len] = '\0';
+ d->entry->name_len = name_len;
+ ret = 1; /* So msdos_readdir will terminate */
+ }
+ return ret;
+}
+
+
/*
Locate entry of an inode in a directory.
Return 0 or a negative error code.
iput (emddir);
if (emddir == NULL){
/* This is a DOS directory */
+ struct UMSDOS_DIR_SEARCH bufk;
struct file filp;
filp.f_reada = 1;
filp.f_pos = 0;
- while (1){
- struct dirent dirent;
- if (umsdos_readdir_kmem (dir,&filp,&dirent,1) <= 0){
- printk ("UMSDOS: can't locate inode %ld in DOS directory???\n"
- ,inode->i_ino);
- }else if (dirent.d_ino == inode->i_ino){
- ret = 0;
- memcpy (entry->name,dirent.d_name,dirent.d_reclen);
- entry->name[dirent.d_reclen] = '\0';
- entry->name_len = dirent.d_reclen;
- inode->u.umsdos_i.i_dir_owner = dir->i_ino;
- inode->u.umsdos_i.i_emd_owner = 0;
- umsdos_setup_dir_inode(inode);
- break;
- }
+ bufk.entry = entry;
+ bufk.search_ino = inode->i_ino;
+ msdos_readdir (dir,&filp,&bufk,umsdos_dir_search);
+ if (bufk.found){
+ ret = 0;
+ inode->u.umsdos_i.i_dir_owner = dir->i_ino;
+ inode->u.umsdos_i.i_emd_owner = 0;
+ umsdos_setup_dir_inode(inode);
}
}else{
/* skip . and .. see umsdos_readdir_x() */
filp.f_reada = 1;
filp.f_pos = UMSDOS_SPECIAL_DIRFPOS;
while (1){
- struct dirent dirent;
- off_t f_pos;
- if (umsdos_readdir_x(dir,&filp,&dirent
- ,0,1,entry,0,&f_pos) <= 0){
+ struct UMSDOS_DIRENT_K bufk;
+ if (umsdos_readdir_x(dir,&filp,&bufk
+ ,1,entry,0,umsdos_filldir_k) < 0){
printk ("UMSDOS: can't locate inode %ld in EMD file???\n"
,inode->i_ino);
break;
- }else if (dirent.d_ino == inode->i_ino){
+ }else if (bufk.ino == inode->i_ino){
ret = 0;
- umsdos_lookup_patch (dir,inode,entry,f_pos);
+ umsdos_lookup_patch (dir,inode,entry,bufk.f_pos);
break;
}
}
int ret;
umsdos_patch_inode (dir,NULL,0);
ret = umsdos_real_lookup (dir,"..",2,result);
- PRINTK (("result %d %x ",ret,*result));
+ PRINTK (("result %d %p ",ret,*result));
if (ret == 0){
struct inode *adir = *result;
ret = umsdos_inode2entry (adir,dir,entry);
struct umsdos_info info;
ret = umsdos_parse (name,len,&info);
if (ret == 0) ret = umsdos_findentry (dir,&info,0);
- PRINTK (("lookup %s pos %d ret %d len %d ",info.fake.fname,info.f_pos,ret
+ PRINTK (("lookup %s pos %lu ret %d len %d ",info.fake.fname,info.f_pos,ret
,info.fake.len));
if (ret == 0){
/* #Specification: umsdos / lookup
umsdos_delentry (dir,&info,S_ISDIR(info.entry.mode));
}else{
umsdos_lookup_patch (dir,inode,&info.entry,info.f_pos);
- PRINTK (("lookup ino %d flags %d\n",inode->i_ino
+ PRINTK (("lookup ino %ld flags %d\n",inode->i_ino
,info.entry.flags));
if (info.entry.flags & UMSDOS_HLINK){
ret = umsdos_hlink2inode (inode,result);
#define PRINTK(x)
#define Printk(x) printk x
-int umsdos_readdir_kmem(
- struct inode *inode,
- struct file *filp,
- struct dirent *dirent,
- int count)
-{
- int ret;
- int old_fs = get_fs();
- set_fs (KERNEL_DS);
- ret = msdos_readdir(inode,filp,dirent,count);
- set_fs (old_fs);
- return ret;
-}
/*
Read a file into kernel space memory
*/
filp->f_flags = 0;
sizeread = umsdos_file_read_kmem (emd_dir,filp,buf,count);
if (sizeread != count){
- printk ("UMSDOS: problem with EMD file. Can't read\n");
+ printk ("UMSDOS: problem with EMD file. Can't read pos = %ld (%d != %d)\n"
+ ,filp->f_pos,sizeread,count);
ret = -EIO;
}
return ret;
#define PRINTK(x)
#define Printk(x) printk x
+struct UMSDOS_DIR_ONCE {
+ struct dirent *ent;
+ int count;
+};
+
+/*
+ Record a single entry the first call.
+ Return -EINVAL the next one.
+*/
+static int umsdos_ioctl_fill(
+ void * buf,
+ char * name,
+ int name_len,
+ off_t offset,
+ ino_t ino)
+{
+ int ret = -EINVAL;
+ struct UMSDOS_DIR_ONCE *d = (struct UMSDOS_DIR_ONCE *)buf;
+ if (d->count == 0){
+ memcpy_tofs (d->ent->d_name,name,name_len);
+ put_user ('\0',d->ent->d_name+name_len);
+ put_user (name_len,&d->ent->d_reclen);
+ put_user (ino,&d->ent->d_ino);
+ put_user (offset,&d->ent->d_off);
+ d->count = 1;
+ ret = 0;
+ }
+ return ret;
+}
+
+
/*
Perform special function on a directory
*/
Return > 0 if success.
*/
- ret = msdos_readdir(dir,filp,&idata->dos_dirent,1);
+ struct UMSDOS_DIR_ONCE bufk;
+ bufk.count = 0;
+ bufk.ent = &idata->dos_dirent;
+ msdos_readdir(dir,filp,&bufk,umsdos_ioctl_fill);
+ ret = bufk.count == 1 ? 1 : 0;
}else if (cmd == UMSDOS_READDIR_EMD){
/* #Specification: ioctl / UMSDOS_READDIR_EMD
One entry is read from the EMD at the current
extern struct inode *pseudo_root;
-static int UMSDOS_rreaddir (
- struct inode *dir,
- struct file *filp,
- struct dirent *dirent,
- int count)
+struct RDIR_FILLDIR {
+ void *dirbuf;
+ filldir_t filldir;
+ int real_root;
+};
+
+static int rdir_filldir(
+ void * buf,
+ char * name,
+ int name_len,
+ off_t offset,
+ ino_t ino)
{
int ret = 0;
- while (1){
- int len = -1;
- ret = msdos_readdir(dir,filp,dirent,count);
- if (ret > 0) len = get_fs_word(&dirent->d_reclen);
- if (len == 5
- && pseudo_root != NULL
- && dir->i_sb->s_mounted == pseudo_root->i_sb->s_mounted){
- /*
- In pseudo root mode, we must eliminate logically
- the directory linux from the real root.
- */
- char name[5];
- memcpy_fromfs (name,dirent->d_name,5);
- if (memcmp(name,UMSDOS_PSDROOT_NAME,UMSDOS_PSDROOT_LEN)!=0) break;
- }else{
- if (pseudo_root != NULL
- && len == 2
- && dir == dir->i_sb->s_mounted
- && dir == pseudo_root->i_sb->s_mounted){
- char name[2];
- memcpy_fromfs (name,dirent->d_name,2);
- if (name[0] == '.' && name[1] == '.'){
- put_fs_long (pseudo_root->i_ino,&dirent->d_ino);
- }
+ struct RDIR_FILLDIR *d = (struct RDIR_FILLDIR*) buf;
+ if (d->real_root){
+ /* real root of a pseudo_rooted partition */
+ if (name_len != UMSDOS_PSDROOT_LEN
+ || memcmp(name,UMSDOS_PSDROOT_NAME,UMSDOS_PSDROOT_LEN)!=0){
+ /* So it is not the /linux directory */
+ if (name_len == 2
+ && name[0] == '.'
+ && name[1] == '.'){
+ /* Make sure the .. entry points back to the pseudo_root */
+ ino = pseudo_root->i_ino;
}
- break;
+ ret = d->filldir (d->dirbuf,name,name_len,offset,ino);
}
+ }else{
+ /* Any DOS directory */
+ ret = d->filldir (d->dirbuf,name,name_len,offset,ino);
}
return ret;
}
+
+static int UMSDOS_rreaddir (
+ struct inode *dir,
+ struct file *filp,
+ void *dirbuf,
+ filldir_t filldir)
+{
+ struct RDIR_FILLDIR bufk;
+ bufk.filldir = filldir;
+ bufk.dirbuf = dirbuf;
+ bufk.real_root = pseudo_root != NULL
+ && dir == dir->i_sb->s_mounted
+ && dir == pseudo_root->i_sb->s_mounted;
+ return msdos_readdir(dir,filp,&bufk,rdir_filldir);
+}
+
/*
Lookup into a non promoted directory.
If the result is a directory, make sure we find out if it is
buf += chars;
} else {
while (chars-->0)
- put_fs_byte(0,buf++);
+ put_user(0,buf++);
}
offset = 0;
if (++bhe == &buflist[NBUF])
if (!bh)
return 0;
for (i=0; i < buflen && (c=bh->b_data[i]); i++)
- put_fs_byte(c, buffer++);
+ put_user(c, buffer++);
if (i < buflen-1)
- put_fs_byte((char)0, buffer);
+ put_user('\0', buffer);
brelse(bh);
return i;
}
#define MAX_DMA_CHANNELS 8
/* The maximum address that we can perform a DMA transfer to on this platform */
-#define MAX_DMA_ADDRESS 0x1000000
+#define MAX_DMA_ADDRESS (~0UL)
/* 8237 DMA controllers */
#define IO_DMA1_BASE 0x00 /* 8 bit slave DMA, channels 0..3 */
--- /dev/null
+#ifndef __ALPHA_PAL_H
+#define __ALPHA_PAL_H
+
+/*
+ * Common PAL-code
+ */
+#define PAL_halt 0
+#define PAL_cflush 1
+#define PAL_draina 2
+#define PAL_cobratt 9
+#define PAL_bpt 128
+#define PAL_bugchk 129
+#define PAL_chmk 131
+#define PAL_callsys 131
+#define PAL_imb 134
+#define PAL_rduniq 158
+#define PAL_wruniq 159
+#define PAL_gentrap 170
+#define PAL_nphalt 190
+
+/*
+ * VMS specific PAL-code
+ */
+#define PAL_swppal 10
+#define PAL_mfpr_vptb 41
+
+/*
+ * OSF specific PAL-code
+ */
+#define PAL_rdmces 16
+#define PAL_wrmces 17
+#define PAL_wrfen 43
+#define PAL_wrvptptr 45
+#define PAL_jtopal 46
+#define PAL_swpctx 48
+#define PAL_wrval 49
+#define PAL_rdval 50
+#define PAL_tbi 51
+#define PAL_wrent 52
+#define PAL_swpipl 53
+#define PAL_rdps 54
+#define PAL_wrkgp 55
+#define PAL_wrusp 56
+#define PAL_wrperfmon 57
+#define PAL_rdusp 58
+#define PAL_whami 60
+#define PAL_rtsys 61
+#define PAL_rti 63
+
+#endif /* __ALPHA_PAL_H */
* pointer type..
*/
#define put_user(x,ptr) __put_user((unsigned long)(x),(ptr),sizeof(*(ptr)))
-#define get_user(ptr) __get_user((ptr),sizeof(*(ptr)))
+#define get_user(ptr) ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr))))
/*
* This is a silly but good way to make sure that
}
/* I should make this use unaligned transfers etc.. */
-static inline unsigned long __get_user(void * y, int size)
+static inline unsigned long __get_user(const void * y, int size)
{
switch (size) {
case 1:
#ifndef __ALPHA_SYSTEM_H
#define __ALPHA_SYSTEM_H
+#include <asm/pal.h> /* for backwards compatibility... */
+
/*
* System defines.. Note that this is included both from .c and .S
* files, so it does only defines, not any C code.
#define START_ADDR 0xfffffc0000310000
#define START_SIZE (2*1024*1024)
-/*
- * Common PAL-code
- */
-#define PAL_halt 0
-#define PAL_cflush 1
-#define PAL_draina 2
-#define PAL_cobratt 9
-#define PAL_bpt 128
-#define PAL_bugchk 129
-#define PAL_chmk 131
-#define PAL_callsys 131
-#define PAL_imb 134
-#define PAL_rduniq 158
-#define PAL_wruniq 159
-#define PAL_gentrap 170
-#define PAL_nphalt 190
-
-/*
- * VMS specific PAL-code
- */
-#define PAL_swppal 10
-#define PAL_mfpr_vptb 41
-
-/*
- * OSF specific PAL-code
- */
-#define PAL_rdmces 16
-#define PAL_wrmces 17
-#define PAL_wrfen 43
-#define PAL_wrvptptr 45
-#define PAL_jtopal 46
-#define PAL_swpctx 48
-#define PAL_wrval 49
-#define PAL_rdval 50
-#define PAL_tbi 51
-#define PAL_wrent 52
-#define PAL_swpipl 53
-#define PAL_rdps 54
-#define PAL_wrkgp 55
-#define PAL_wrusp 56
-#define PAL_wrperfmon 57
-#define PAL_rdusp 58
-#define PAL_whami 60
-#define PAL_rtsys 61
-#define PAL_rti 63
-
#ifndef __ASSEMBLY__
extern void wrent(void *, unsigned long);
extern void wrkgp(unsigned long);
extern void wrusp(unsigned long);
extern unsigned long rdusp(void);
+extern unsigned long rdmces (void);
+extern void wrmces (unsigned long);
#define halt() __asm__ __volatile__(".long 0");
#define TIOCSWINSZ _IOW('t', 103, struct winsize)
#define TIOCGWINSZ _IOR('t', 104, struct winsize)
+#define TIOCSTART _IO('t', 110) /* start output, like ^Q */
+#define TIOCSTOP _IO('t', 111) /* stop output, like ^S */
#define TIOCOUTQ _IOR('t', 115, int) /* output queue size */
#define TIOCGLTC _IOR('t', 116, struct ltchars)
#define VSTOP 13
#define VLNEXT 14
#define VDISCARD 15
-#define VTIME 17
#define VMIN 16
+#define VTIME 17
/*
* ..and the same for c_cc in the termio structure..
#define _VSWTC 7
#ifdef __KERNEL__
-/* intr=^C quit=^| erase=del kill=^U
- eof=^D vtime=\0 vmin=\1 sxtc=\0
- start=^Q stop=^S susp=^Z eol=\0
- reprint=^R discard=^U werase=^W lnext=^V
- eol2=\0
+/* eof=^D eol=\0 eol2=\0 erase=del
+ werase=^W kill=^U reprint=^R sxtc=\0
+ intr=^C quit=^\ susp=^Z <OSF/1 VDSUSP>
+ start=^Q stop=^S lnext=^V discard=^U
+ vmin=\1 vtime=\0
*/
-#define INIT_C_CC "\004\000\000\177\027\025\022\000\003\034\032\000\021\023\026\025\000\001"
+#define INIT_C_CC "\004\000\000\177\027\025\022\000\003\034\032\000\021\023\026\025\001\000"
#endif
/* c_iflag bits */
__asm__ __volatile__("btsl %2,%1\n\tsbbl %0,%0"
:"=r" (oldbit),"=m" (ADDR)
- :"r" (nr));
+ :"ir" (nr));
return oldbit;
}
__asm__ __volatile__("btrl %2,%1\n\tsbbl %0,%0"
:"=r" (oldbit),"=m" (ADDR)
- :"r" (nr));
+ :"ir" (nr));
return oldbit;
}
__asm__ __volatile__("btcl %2,%1\n\tsbbl %0,%0"
:"=r" (oldbit),"=m" (ADDR)
- :"r" (nr));
+ :"ir" (nr));
return oldbit;
}
__asm__ __volatile__("btl %2,%1\n\tsbbl %0,%0"
:"=r" (oldbit)
- :"m" (ADDR),"r" (nr));
+ :"m" (ADDR),"ir" (nr));
return oldbit;
}
__asm__("
cld
movl $-1,%%eax
+ xorl %%edx,%%edx
repe; scasl
je 1f
+ xorl -4(%%edi),%%eax
subl $4,%%edi
- movl (%%edi),%%eax
- notl %%eax
bsfl %%eax,%%edx
- jmp 2f
-1: xorl %%edx,%%edx
-2: subl %%ebx,%%edi
+1: subl %%ebx,%%edi
shll $3,%%edi
addl %%edi,%%edx"
:"=d" (res)
:"c" ((size + 31) >> 5), "D" (addr), "b" (addr)
- :"ax", "bx", "cx", "di");
+ :"ax", "cx", "di");
return res;
}
#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.
+ */
+extern inline unsigned long readb(unsigned long addr)
+{ return *(unsigned char *) addr; }
+
+extern inline unsigned long readw(unsigned long addr)
+{ return *(unsigned short *) addr; }
+
+extern inline unsigned long readl(unsigned long addr)
+{ return *(unsigned int *) addr; }
+
+extern inline void writeb(unsigned char b, unsigned long addr)
+{ *(unsigned char *) addr = b; }
+
+extern inline void writew(unsigned short b, unsigned long addr)
+{ *(unsigned short *) addr = b; }
+
+extern inline void writel(unsigned int b, unsigned long addr)
+{ *(unsigned int *) addr = b; }
+
+#define memset_io(a,b,c) memset((void *)(a),(b),(c))
+#define memcpy_fromio(a,b,c) memcpy((a),(void *)(b),(c))
+#define memcpy_toio(a,b,c) memcpy((void *)(a),(b),(c))
+
/*
* Talk about misusing macros..
*/
* pointer type..
*/
#define put_user(x,ptr) __put_user((unsigned long)(x),(ptr),sizeof(*(ptr)))
-#define get_user(ptr) __get_user((ptr),sizeof(*(ptr)))
+#define get_user(ptr) ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr))))
/*
* This is a silly but good way to make sure that
static inline void __generic_memcpy_tofs(void * to, const void * from, unsigned long n)
{
-__asm__("cld\n\t"
- "push %%es\n\t"
- "push %%fs\n\t"
- "pop %%es\n\t"
- "testb $1,%%cl\n\t"
- "je 1f\n\t"
- "movsb\n"
- "1:\ttestb $2,%%cl\n\t"
- "je 2f\n\t"
- "movsw\n"
- "2:\tshrl $2,%%ecx\n\t"
- "rep ; movsl\n\t"
- "pop %%es"
- : /* no outputs */
- :"c" (n),"D" ((long) to),"S" ((long) from)
+ __asm__ volatile
+ (" cld
+ push %%es
+ movw %%fs,%%cx
+ movw %%cx,%%es
+ cmpl $3,%0
+ jbe 1f
+ movl %%edi,%%ecx
+ negl %%ecx
+ andl $3,%%ecx
+ subl %%ecx,%0
+ rep; movsb
+ movl %0,%%ecx
+ shrl $2,%%ecx
+ rep; movsl
+ andl $3,%0
+ 1: movl %0,%%ecx
+ rep; movsb
+ pop %%es"
+ :"=abd" (n)
+ :"0" (n),"D" ((long) to),"S" ((long) from)
:"cx","di","si");
}
static inline void __generic_memcpy_fromfs(void * to, const void * from, unsigned long n)
{
-__asm__("cld\n\t"
- "testb $1,%%cl\n\t"
- "je 1f\n\t"
- "fs ; movsb\n"
- "1:\ttestb $2,%%cl\n\t"
- "je 2f\n\t"
- "fs ; movsw\n"
- "2:\tshrl $2,%%ecx\n\t"
- "rep ; fs ; movsl"
- : /* no outputs */
- :"c" (n),"D" ((long) to),"S" ((long) from)
- :"cx","di","si","memory");
+ __asm__ volatile
+ (" cld
+ cmpl $3,%0
+ jbe 1f
+ movl %%edi,%%ecx
+ negl %%ecx
+ andl $3,%%ecx
+ subl %%ecx,%0
+ fs; rep; movsb
+ movl %0,%%ecx
+ shrl $2,%%ecx
+ fs; rep; movsl
+ andl $3,%0
+ 1: movl %0,%%ecx
+ fs; rep; movsb"
+ :"=abd" (n)
+ :"0" (n),"D" ((long) to),"S" ((long) from)
+ :"cx","di","si", "memory");
}
static inline void __constant_memcpy_fromfs(void * to, const void * from, unsigned long n)
__asm__ __volatile__(
"cld\n\t"
"rep ; movsl\n\t"
- "testb $2,%%dl\n\t"
+ "testb $2,%b1\n\t"
"je 1f\n\t"
"movsw\n"
- "1:\ttestb $1,%%dl\n\t"
+ "1:\ttestb $1,%b1\n\t"
"je 2f\n\t"
"movsb\n"
"2:"
: /* no output */
- :"c" (n/4), "d" (n),"D" ((long) to),"S" ((long) from)
+ :"c" (n/4), "q" (n),"D" ((long) to),"S" ((long) from)
: "cx","di","si","memory");
return (to);
}
return dest;
}
-extern inline int memcmp(const void * cs,const void * ct,size_t count)
-{
-register int __res;
-__asm__ __volatile__(
- "cld\n\t"
- "repe\n\t"
- "cmpsb\n\t"
- "je 1f\n\t"
- "sbbl %%eax,%%eax\n\t"
- "orb $1,%%al\n"
- "1:"
- :"=a" (__res):"0" (0),"S" (cs),"D" (ct),"c" (count)
- :"si","di","cx");
-return __res;
-}
+#define memcmp __builtin_memcmp
extern inline void * memchr(const void * cs,int c,size_t count)
{
__asm__ __volatile__(
"cld\n\t"
"rep ; stosl\n\t"
- "testb $2,%%dl\n\t"
+ "testb $2,%b1\n\t"
"je 1f\n\t"
"stosw\n"
- "1:\ttestb $1,%%dl\n\t"
+ "1:\ttestb $1,%b1\n\t"
"je 2f\n\t"
"stosb\n"
"2:"
: /* no output */
- :"a" (c), "d" (count), "c" (count/4), "D" ((long) s)
+ :"a" (c), "q" (count), "c" (count/4), "D" ((long) s)
:"cx","di","memory");
return (s);
}
typedef __signed__ short __s16;
typedef unsigned short __u16;
-typedef __signed__ long __s32;
-typedef unsigned long __u32;
+typedef __signed__ int __s32;
+typedef unsigned int __u32;
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
typedef __signed__ long long __s64;
typedef signed short s16;
typedef unsigned short u16;
-typedef signed long s32;
-typedef unsigned long u32;
+typedef signed int s32;
+typedef unsigned int u32;
typedef signed long long s64;
typedef unsigned long long u64;
#define _SPARC_ASI_H
/* asi.h: Address Space Identifier values for the sparc.
-
- Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
-*/
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ *
+ * Pioneer work for sun4m: Paul Hatchman (paul@sfe.com.au)
+ * Joint edition for sun4c+sun4m: Pete A. Zaitcev <zaitcev@ipmce.su>
+ */
/* These are sun4c, beware on other architectures. Although things should
* be similar under regular sun4's.
*/
#include <linux/config.h>
-#ifdef CONFIG_SUN4M
-#include "asi4m.h"
-#else
#define ASI_NULL1 0x0
#define ASI_NULL2 0x1
#define ASI_PTE 0x4
#define ASI_HWFLUSHSEG 0x5 /* These are to initiate hw flushes of the cache */
#define ASI_HWFLUSHPAGE 0x6
+#define ASI_REGMAP 0x6 /* Top level segmaps on Sun4's with MUTANT MMU */
#define ASI_HWFLUSHCONTEXT 0x7
#define ASI_FLUSHPG 0xd
#define ASI_FLUSHCTX 0xe
+/* The following are now not so SS5 specific any more, it is pretty
+ * much a complete generic sun4m/V8 ASI assignment listing now.
+ *
+ * -- davem@caip.rutgers.edu
+ */
+
+/* SPARCstation-5: only 6 bits are decoded. */
+/* wo = Write Only, rw = Read Write; */
+/* ss = Single Size, as = All Sizes; */
+#define ASI_M_RES00 0x00 /* Don't touch... */
+#define ASI_M_UNA01 0x01 /* Same here... */
+#define ASI_M_MXCC 0x02 /* Access to TI VIKING MXCC registers */
+#define ASI_M_FLUSH_PROBE 0x03 /* Reference MMU Flush/Probe; rw, ss */
+#define ASI_M_MMUREGS 0x04 /* MMU Registers; rw, ss */
+#define ASI_M_TLBDIAG 0x05 /* MMU TLB only Diagnostics */
+#define ASI_M_DIAGS 0x06 /* Reference MMU Diagnostics */
+#define ASI_M_IODIAG 0x07 /* MMU I/O TLB only Diagnostics */
+#define ASI_M_USERTXT 0x08 /* Same as ASI_USERTXT; rw, as */
+#define ASI_M_KERNELTXT 0x09 /* Same as ASI_KERNELTXT; rw, as */
+#define ASI_M_USERDATA 0x0A /* Same as ASI_USERDATA; rw, as */
+#define ASI_M_KERNELDATA 0x0B /* Same as ASI_KERNELDATA; rw, as */
+#define ASI_M_TXTC_TAG 0x0C /* Instruction Cache Tag; rw, ss */
+#define ASI_M_TXTC_DATA 0x0D /* Instruction Cache Data; rw, ss */
+#define ASI_M_DATAC_TAG 0x0E /* Data Cache Tag; rw, ss */
+#define ASI_M_DATAC_DATA 0x0F /* Data Cache Data; rw, ss */
+
+/* The following cache flushing ASIs work only with the 'sta'
+ * instruction results are unpredictable for 'swap' and 'ldstuba' etc.
+ * So don't do it.
+ */
+
+/* These ASI flushes affect external caches too. */
+#define ASI_M_FLUSH_PAGE 0x10 /* Flush I&D Cache Line (page); wo, ss */
+#define ASI_M_FLUSH_SEG 0x11 /* Flush I&D Cache Line (seg); wo, ss */
+#define ASI_M_FLUSH_REGION 0x12 /* Flush I&D Cache Line (region); wo, ss */
+#define ASI_M_FLUSH_CTX 0x13 /* Flush I&D Cache Line (context); wo, ss */
+#define ASI_M_FLUSH_USER 0x14 /* Flush I&D Cache Line (user); wo, ss */
+
+/* Block-copy operations are available on certain V8 cpus */
+#define ASI_M_BCOPY 0x17 /* Block copy */
+
+/* These affect only the ICACHE and are Ross HyperSparc specific. */
+#define ASI_M_IFLUSH_PAGE 0x18 /* Flush I Cache Line (page); wo, ss */
+#define ASI_M_IFLUSH_SEG 0x19 /* Flush I Cache Line (seg); wo, ss */
+#define ASI_M_IFLUSH_REGION 0x1A /* Flush I Cache Line (region); wo, ss */
+#define ASI_M_IFLUSH_CTX 0x1B /* Flush I Cache Line (context); wo, ss */
+#define ASI_M_IFLUSH_USER 0x1C /* Flush I Cache Line (user); wo, ss */
+
+/* Block-fill operations are available on certain V8 cpus */
+#define ASI_M_BFILL 0x1F
+
+/* This allows direct access to main memory, actually 0x20 to 0x2f are
+ * the available ASI's for physical ram pass-through, but I don't have
+ * any idea what the other ones do....
+ */
+
+#define ASI_M_BYPASS 0x20 /* Reference MMU bypass; rw, as */
+#define ASI_M_FBMEM 0x29 /* Graphics card frame buffer access */
+#define ASI_M_VMEUS 0x2A /* VME user 16-bit access */
+#define ASI_M_VMEPS 0x2B /* VME priv 16-bit access */
+#define ASI_M_VMEUT 0x2C /* VME user 32-bit access */
+#define ASI_M_VMEPT 0x2D /* VME priv 32-bit access */
+#define ASI_M_SBUS 0x2E /* Direct SBus access */
+#define ASI_M_CTL 0x2F /* Control Space (ECC and MXCC are here) */
+
+
+/* This is ROSS HyperSparc only. */
+#define ASI_M_FLUSH_IWHOLE 0x31 /* Flush entire ICACHE; wo, ss */
+
+#define ASI_M_DCDR 0x39 /* Data Cache Diagnostics Registerl rw, ss */
+
+/* Sparc V9 TI UltraSparc ASI's */
+
+/* ASIs 0x0-0x7f are Supervisor Only. 0x80-0xff are for anyone. */
+
+/* You will notice that there are a lot of places where if a normal
+ * ASI is available on the V9, there is also a little-endian version.
+ */
+
+#define ASI_V9_RESV0 0x00 /* Don't touch... */
+#define ASI_V9_RESV1 0x01 /* Not here */
+#define ASI_V9_RESV2 0x02 /* Or here */
+#define ASI_V9_RESV3 0x03 /* nor here. */
+#define ASI_V9_NUCLEUS 0x04 /* Impl-dep extra virtual access context */
+#define ASI_V9_NUCLEUSL 0x0C /* Nucleus context, little-endian */
+#define ASI_V9_USER_PRIM 0x10 /* User primary address space */
+#define ASI_V9_USER_SEC 0x11 /* User secondary address space */
+
+#define ASI_V9_MMUPASS 0x14 /* OBMEM (external cache, no data cache) */
+#define ASI_V9_IOPASS 0x15 /* Like MMUPASS but for I/O areas (uncached) */
+#define ASI_V9_USER_PRIML 0x18 /* User primary address space, little-endian. */
+#define ASI_V9_USER_SECL 0x19 /* User secondary address space, little-endian. */
+#define ASI_V9_MMUPASSL 0x1C /* OBMEM little-endian */
+#define ASI_V9_IOPASSL 0x1D /* Like IOPASS but little-endian */
+#define ASI_V9_ATOMICQ 0x24 /* Atomic 128-bit load address space */
+#define ASI_V9_ATOMICQL 0x2C /* Atomic 128-bit load little-endian */
+#define ASI_V9_LSTORECTL 0x45 /* ld/st control unit */
+#define ASI_V9_DCACHE_ENT 0x46 /* Data cache entries */
+#define ASI_V9_DCACHE_TAG 0x47 /* Data cache tags */
+#define ASI_V9_IRQDISPS 0x48 /* IRQ dispatch status registers */
+#define ASI_V9_IRQRECVS 0x49 /* IRQ receive status registers */
+#define ASI_V9_MMUREGS 0x4A /* Spitfire MMU control register */
+#define ASI_V9_ESTATE 0x4B /* Error state enable register */
+#define ASI_V9_ASYNC_FSR 0x4C /* Asynchronous Fault Status reg */
+#define ASI_V9_ASYNC_FAR 0x4D /* Asynchronous Fault Address reg */
+
+#define ASI_V9_ECACHE_DIAG 0x4E /* External Cache diagnostics */
+
+#define ASI_V9_TXTMMU 0x50 /* MMU for program text */
+#define ASI_V9_TXTMMU_D1 0x51 /* XXX */
+#define ASI_V9_TXTMMU_D2 0x52 /* XXX */
+#define ASI_V9_TXTMMU_TDI 0x54 /* Text MMU TLB data in */
+#define ASI_V9_TXTMMU_TDA 0x55 /* Text MMU TLB data access */
+#define ASI_V9_TXTMMU_TTR 0x56 /* Text MMU TLB tag read */
+#define ASI_V9_TXTMMU_TDM 0x57 /* Text MMU TLB de-map */
+
+#define ASI_V9_DATAMMU 0x58 /* MMU for program data */
+#define ASI_V9_DATAMMU_D1 0x59 /* XXX */
+#define ASI_V9_DATAMMU_D2 0x5A /* XXX */
+#define ASI_V9_DATAMMU_DD 0x5B /* XXX */
+#define ASI_V9_DATAMMU_TDI 0x5C /* Data MMU TLB data in */
+#define ASI_V9_DATAMMU_TDA 0x5D /* Data MMU TLB data access */
+#define ASI_V9_DATAMMU_TTR 0x5E /* Data MMU TLB tag read */
+#define ASI_V9_DATAMMU_TDM 0x5F /* Data MMU TLB de-map */
+
+#define ASI_V9_ICACHE_D 0x66 /* Instruction cache data */
+#define ASI_V9_ICACHE_T 0x67 /* Instruction cache tags */
+#define ASI_V9_ICACHE_DEC 0x6E /* Instruction cache decode */
+#define ASI_V9_ICACHE_NXT 0x6F /* Instruction cache next ent */
+
+#define ASI_V9_HUH1 0x70 /* XXX */
+#define ASI_V9_HUH2 0x71 /* XXX */
+
+#define ASI_V9_ECACHE_ACC 0x76 /* External cache registers */
+
+#define ASI_V9_INTR_DISP 0x77 /* Interrupt dispatch registers */
+#define ASI_V9_HUH1L 0x78 /* XXX */
+#define ASI_V9_HUH2L 0x79 /* XXX */
+#define ASI_V9_INTR_RECV 0x7f /* Interrupt Receive registers */
+
+#define ASI_V9_PRIMARY 0x80 /* Primary address space */
+#define ASI_V9_SECONDARY 0x81 /* Secondary address space */
+#define ASI_V9_PRIMARY_NF 0x82 /* Primary address space -- No Fault */
+#define ASI_V9_SECONDARY_NF 0x83 /* Secondary address space -- No Fault */
+
+#define ASI_V9_PRIMARYL 0x80 /* Primary address space, little-endian */
+#define ASI_V9_SECONDARYL 0x81 /* Secondary address space, little-endian */
+#define ASI_V9_PRIMARY_NFL 0x82 /* Primary address space, No Fault, l-endian */
+#define ASI_V9_SECONDARY_NFL 0x83 /* Secondary address space, No Fault, l-endian */
+
+#define ASI_V9_XXX1 0xC0 /* XXX */
+#define ASI_V9_XXX2 0xC1 /* XXX */
+#define ASI_V9_XXX3 0xC2 /* XXX */
+#define ASI_V9_XXX4 0xC3 /* XXX */
+#define ASI_V9_XXX5 0xC4 /* XXX */
+#define ASI_V9_XXX6 0xC5 /* XXX */
+#define ASI_V9_XXX7 0xC8 /* XXX */
+#define ASI_V9_XXX8 0xC9 /* XXX */
+#define ASI_V9_XXX9 0xCA /* XXX */
+#define ASI_V9_XXX10 0xCB /* XXX */
+#define ASI_V9_XXX11 0xCC /* XXX */
+#define ASI_V9_XXX12 0xCD /* XXX */
+
+#define ASI_V9_XXX13 0xD0 /* XXX */
+#define ASI_V9_XXX14 0xD1 /* XXX */
+#define ASI_V9_XXX15 0xD2 /* XXX */
+#define ASI_V9_XXX16 0xD3 /* XXX */
+#define ASI_V9_XXX17 0xD8 /* XXX */
+#define ASI_V9_XXX18 0xD9 /* XXX */
+#define ASI_V9_XXX19 0xDA /* XXX */
+#define ASI_V9_XXX20 0xDB /* XXX */
+
+#define ASI_V9_XXX21 0xE0 /* XXX */
+#define ASI_V9_XXX22 0xE1 /* XXX */
+#define ASI_V9_XXX23 0xF0 /* XXX */
+#define ASI_V9_XXX24 0xF1 /* XXX */
+#define ASI_V9_XXX25 0xF8 /* XXX */
+#define ASI_V9_XXX26 0xF9 /* XXX */
+
+#ifndef __ASSEMBLY__
+
+/* Better to do these inline with gcc __asm__ statements. */
+
+/* The following allow you to access physical memory directly without
+ * translation by the SRMMU. The only other way to do this is to
+ * turn off the SRMMU completely, and well... thats not good.
+ *
+ * TODO: For non-MBus SRMMU units we have to perform the following
+ * using this sequence.
+ * 1) Turn off traps
+ * 2) Turn on AC bit in SRMMU control register
+ * 3) Do our direct physical memory access
+ * 4) Restore old SRMMU control register value
+ * 5) Restore old %psr value
+ */
+
+extern __inline__ unsigned int
+ldb_sun4m_bypass(unsigned int addr)
+{
+ unsigned int retval;
+
+ __asm__("lduba [%2] %1, %0\n\t" :
+ "=r" (retval) :
+ "i" (ASI_M_BYPASS), "r" (addr));
+
+ return retval;
+}
+
+extern __inline__ unsigned int
+ldw_sun4m_bypass(unsigned int addr)
+{
+ unsigned int retval;
+
+ __asm__("lda [%2] %1, %0\n\t" :
+ "=r" (retval) :
+ "i" (ASI_M_BYPASS), "r" (addr));
+
+ return retval;
+}
+
+extern __inline__ void
+stb_sun4m_bypass(unsigned char value, unsigned int addr)
+{
+ __asm__("stba %0, [%2] %1\n\t" : :
+ "r" (value), "i" (ASI_M_BYPASS), "r" (addr) :
+ "memory");
+}
+
+extern __inline__ void
+stw_sun4m_bypass(unsigned int value, unsigned int addr)
+{
+ __asm__("sta %0, [%2] %1\n\t" : :
+ "r" (value), "i" (ASI_M_BYPASS), "r" (addr) :
+ "memory");
+}
+
+#endif /* !(__ASSEMBLY__) */
+
-#endif /* CONFIG_SUN4M */
#endif /* _SPARC_ASI_H */
--- /dev/null
+/* auxio.h: Definitons and code for the Auxiliary I/O register.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+#ifndef _SPARC_AUXIO_H
+#define _SPARC_AUXIO_H
+
+/* This defines the register as I know it on the Sun4c, it may be
+ * different or not exist at all on sun4m's.
+ */
+
+#define AUXIO_IOADDR 0xf7400000 /* Physical address is IO space */
+
+/* This register is an unsigned char in IO space. It does two things.
+ * First, it is used to control the front panel LED light on machines
+ * that have it (good for testing entry points to trap handlers and irq's)
+ * Secondly, it controls various floppy drive parameters on machines that
+ * have a drive.
+ */
+
+#define AUXIO_ORMEIN 0xf0 /* All writes must set these bits. */
+#define AUXIO_FLPY_DENS 0x20 /* Floppy density, high if set. */
+#define AUXIO_FLPY_DCHG 0x10 /* A disk change occurred. */
+#define AUXIO_FLPY_DSEL 0x08 /* Drive select, 0 'a drive' 1 'b drive'. */
+#define AUXIO_FLPY_TCNT 0x04 /* Floppy terminal count... ??? */
+#define AUXIO_FLPY_EJCT 0x02 /* Eject floppy disk. */
+#define AUXIO_LED 0x01 /* On if set, off if unset. */
+
+#define AUXREG ((volatile unsigned char *)(AUXIO_VADDR + 3))
+
+#define TURN_ON_LED *AUXREG = AUXIO_ORMEIN | AUXIO_FLPY_EJCT | AUXIO_LED
+#define TURN_OFF_LED *AUXREG = AUXIO_ORMEIN | AUXIO_FLPY_EJCT
+#define FLIP_LED *AUXREG = (*AUXREG | AUXIO_ORMEIN) ^ AUXIO_LED
+
+#endif /* !(_SPARC_AUXIO_H) */
#ifndef _SPARC_BITOPS_H
#define _SPARC_BITOPS_H
+#include <linux/kernel.h>
+#include <asm/system.h>
+
/*
- * Copyright 1994, David S. Miller (davem@caip.rutgers.edu).
+ * Copyright 1995, David S. Miller (davem@caip.rutgers.edu).
*/
* for sun4m (ie. SMP) no doubt.
*/
-extern __inline__ unsigned int set_bit(unsigned int nr, void *addr)
+/* These routines now do things in little endian byte order. */
+
+/* Our unsigned long accesses on the Sparc look like this:
+ * Big Endian:
+ * byte 0 byte 1 byte 2 byte 3
+ * 0000 0000 0000 0000 0000 0000 0000 0000
+ * 31 24 23 16 15 8 7 0
+ *
+ * We want to set the bits in a little-endian fashion:
+ * Little Endian:
+ * byte 3 byte 2 byte 1 byte 0
+ * 0000 0000 0000 0000 0000 0000 0000 0000
+ * 31 24 23 16 15 8 7 0
+ */
+
+/* #define LITTLE_ENDIAN_BITOPS */
+
+extern __inline__ unsigned int set_bit(unsigned int nr, void *vaddr)
{
- register unsigned long retval, tmp, mask, psr;
-
- __asm__ __volatile__("or %%g0, 0x1, %3\n\t" /* produce the mask */
- "sll %3, %4, %3\n\t"
- "rd %%psr, %5\n\t" /* read the psr */
- "wr %5, 0x20, %%psr\n\t" /* traps disabled */
- "ld [%1], %2\n\t" /* critical section */
- "and %3, %2, %0\n\t"
- "or %3, %2, %2\n\t"
- "st %2, [%1]\n\t"
- "wr %5, 0x0, %%psr\n\t" : /* re-enable traps */
- "=r" (retval) :
- "r" (addr), "r" (tmp=0), "r" (mask=0),
- "r" (nr), "r" (psr=0));
-
- return retval; /* confuse gcc :-) */
+
+#ifdef LITTLE_ENDIAN_BITOPS
+
+
+ int retval;
+ unsigned char *addr = (unsigned char *)vaddr;
+ unsigned char mask;
+#ifndef TEST_BITOPS
+ unsigned long flags;
+#endif
+
+ addr += nr >> 3;
+ mask = 1 << (nr & 0x7);
+
+#ifndef TEST_BITOPS
+ save_flags(flags);
+ cli();
+#endif
+
+ retval = (mask & *addr) != 0;
+ *addr |= mask;
+
+#ifndef TEST_BITOPS
+ restore_flags(flags);
+#endif
+
+ return retval;
+
+#else /* BIG ENDIAN BITOPS */
+
+
+ int retval;
+ unsigned long *addr = vaddr;
+ unsigned long mask;
+#ifndef TEST_BITOPS
+ unsigned long flags;
+#endif
+
+ addr += nr>>5;
+ mask = 1 << (nr&31);
+
+#ifndef TEST_BITOPS
+ save_flags(flags);
+ cli();
+#endif
+
+ retval = (mask & *addr) != 0;
+ *addr |= mask;
+
+#ifndef TEST_BITOPS
+ restore_flags(flags);
+#endif
+
+ return retval;
+
+
+#endif
}
-extern __inline__ unsigned int clear_bit(unsigned int nr, void *addr)
+extern __inline__ unsigned int clear_bit(unsigned int nr, void *vaddr)
{
- register unsigned long retval, tmp, mask, psr;
-
- __asm__ __volatile__("or %%g0, 0x1, %3\n\t"
- "sll %3, %4, %3\n\t"
- "rd %%psr, %5\n\t"
- "wr %5, 0x20, %%psr\n\t" /* disable traps */
- "ld [%1], %2\n\t"
- "and %2, %3, %0\n\t" /* get old bit */
- "andn %2, %3, %2\n\t" /* set new val */
- "st %2, [%1]\n\t"
- "wr %5, 0x0, %%psr\n\t" : /* enable traps */
- "=r" (retval) :
- "r" (addr), "r" (tmp=0), "r" (mask=0),
- "r" (nr), "r" (psr=0));
-
- return retval; /* confuse gcc ;-) */
+#ifdef LITTLE_ENDIAN_BITOPS
+
+
+ int retval;
+ unsigned char *addr = (unsigned char *)vaddr;
+ unsigned char mask;
+#ifndef TEST_BITOPS
+ unsigned long flags;
+#endif
+
+ addr += nr >> 3;
+ mask = 1 << (nr & 7);
+
+#ifndef TEST_BITOPS
+ save_flags(flags);
+ cli();
+#endif
+
+ retval = (mask & *addr) != 0;
+ *addr &= ~mask;
+
+#ifndef TEST_BITOPS
+ restore_flags(flags);
+#endif
+
+ return retval;
+
+
+#else /* BIG ENDIAN BITOPS */
+
+ int retval;
+ unsigned long *addr = vaddr;
+ unsigned long mask;
+#ifndef TEST_BITOPS
+ unsigned long flags;
+#endif
+
+ addr += nr>>5;
+ mask = 1 << (nr&31);
+
+#ifndef TEST_BITOPS
+ save_flags(flags);
+ cli();
+#endif
+
+ retval = (mask & *addr) != 0;
+ *addr &= ~mask;
+
+#ifndef TEST_BITOPS
+ restore_flags(flags);
+#endif
+
+ return retval;
+
+
+#endif
}
-extern __inline__ unsigned int change_bit(unsigned int nr, void *addr)
+extern __inline__ unsigned int change_bit(unsigned int nr, void *vaddr)
{
- register unsigned long retval, tmp, mask, psr;
-
- __asm__ __volatile__("or %%g0, 0x1, %3\n\t"
- "sll %3, %4, %3\n\t"
- "rd %%psr, %5\n\t"
- "wr %5, 0x20, %%psr\n\t" /* disable traps */
- "ld [%1], %2\n\t"
- "and %3, %2, %0\n\t" /* get old bit val */
- "xor %3, %2, %2\n\t" /* set new val */
- "st %2, [%1]\n\t"
- "wr %5, 0x0, %%psr\n\t" : /* enable traps */
- "=r" (retval) :
- "r" (addr), "r" (tmp=0), "r" (mask=0),
- "r" (nr), "r" (psr=0));
-
- return retval; /* confuse gcc ;-) */
+#ifdef LITTLE_ENDIAN_BITOPS
+
+
+ int retval;
+ unsigned char *addr = (unsigned char *)vaddr;
+ unsigned char mask;
+#ifndef TEST_BITOPS
+ unsigned long flags;
+#endif
+
+ addr += nr >> 3;
+ mask = 1 << (nr & 7);
+
+#ifndef TEST_BITOPS
+ save_flags(flags);
+ cli();
+#endif
+
+ retval = (mask & *addr) != 0;
+ *addr ^= mask;
+
+#ifndef TEST_BITOPS
+ restore_flags(flags);
+#endif
+
+ return retval;
+
+#else /* BIG ENDIAN BITOPS */
+
+
+ int retval;
+ unsigned long *addr = vaddr;
+ unsigned long mask;
+#ifndef TEST_BITOPS
+ unsigned long flags;
+#endif
+
+ addr += nr>>5;
+ mask = 1 << (nr&31);
+
+#ifndef TEST_BITOPS
+ save_flags(flags);
+ cli();
+#endif
+
+ retval = (mask & *addr) != 0;
+ *addr ^= mask;
+
+#ifndef TEST_BITOPS
+ restore_flags(flags);
+#endif
+
+ return retval;
+
+
+#endif
}
/* The following routine need not be atomic. */
-extern __inline__ unsigned int test_bit(int nr, void *addr)
+extern __inline__ unsigned int test_bit(int nr, void *vaddr)
{
- register unsigned long retval, tmp;
+#ifdef LITTLE_ENDIAN_BITOPS
+
+ unsigned char mask;
+ unsigned char *addr = (unsigned char *)vaddr;
+
+ addr += nr >> 3;
+ mask = 1 << (nr & 7);
+ return ((mask & *addr) != 0);
- __asm__ __volatile__("ld [%1], %2\n\t"
- "or %%g0, 0x1, %0\n\t"
- "sll %0, %3, %0\n\t"
- "and %0, %2, %0\n\t" :
- "=r" (retval) :
- "r" (addr), "r" (tmp=0),
- "r" (nr));
+#else /* BIG ENDIAN BITOPS */
- return retval; /* confuse gcc :> */
+ unsigned long mask;
+ unsigned long *addr = vaddr;
+ addr += (nr>>5);
+ mask = 1 << (nr&31);
+ return ((mask & *addr) != 0);
+
+#endif
}
/* There has to be a faster way to do this, sigh... */
extern __inline__ unsigned long ffz(unsigned long word)
{
- register unsigned long cnt, tmp, tmp2;
+ register unsigned long cnt;
cnt = 0;
- __asm__("or %%g0, %3, %2\n\t"
- "1: and %2, 0x1, %1\n\t"
- "srl %2, 0x1, %2\n\t"
- "cmp %1, 0\n\t"
- "bne,a 1b\n\t"
- "add %0, 0x1, %0\n\t" :
- "=r" (cnt) :
- "r" (tmp=0), "r" (tmp2=0), "r" (word));
-
+#ifdef LITTLE_ENDIAN_BITOPS
+
+ for(int byte_bit = 24; byte_bit >=0; byte_bit -= 8)
+ for(int bit = 0; bit<8; bit++)
+ if((word>>(byte_bit+bit))&1)
+ cnt++;
+ else
+ return cnt;
+
+#else /* BIT ENDIAN BITOPS */
+ while(cnt<32) {
+ if(!((word>>cnt)&1))
+ return cnt;
+ else
+ cnt++;
+ }
return cnt;
+#endif
+
}
/* find_next_zero_bit() finds the first zero bit in a bit string of length
* on Linus's ALPHA routines, which are pretty portable BTW.
*/
-extern __inline__ unsigned long find_next_zero_bit(void *addr, unsigned long size, unsigned long offset)
+extern __inline__ unsigned long
+find_next_zero_bit(void *addr, unsigned long size, unsigned long offset)
{
- unsigned long *p = ((unsigned long *) addr) + (offset >> 6);
- unsigned long result = offset & ~31UL;
- unsigned long tmp;
-
- if (offset >= size)
- return size;
- size -= result;
- offset &= 31UL;
- if (offset)
- {
- tmp = *(p++);
- tmp |= ~0UL >> (32-offset);
- if (size < 32)
- goto found_first;
- if (~tmp)
- goto found_middle;
- size -= 32;
- result += 32;
- }
- while (size & ~32UL)
- {
- if (~(tmp = *(p++)))
- goto found_middle;
- result += 32;
- size -= 32;
- }
- if (!size)
- return result;
- tmp = *p;
+#ifdef LITTLE_ENDIAN_BITOPS
+
+ /* FOO, needs to be written */
+
+#else /* BIG ENDIAN BITOPS */
+ unsigned long *p = ((unsigned long *) addr) + (offset >> 6);
+ unsigned long result = offset & ~31UL;
+ unsigned long tmp;
+
+ if (offset >= size)
+ return size;
+ size -= result;
+ offset &= 31UL;
+ if (offset)
+ {
+ tmp = *(p++);
+ tmp |= ~0UL >> (32-offset);
+ if (size < 32)
+ goto found_first;
+ if (~tmp)
+ goto found_middle;
+ size -= 32;
+ result += 32;
+ }
+ while (size & ~32UL)
+ {
+ if (~(tmp = *(p++)))
+ goto found_middle;
+ result += 32;
+ size -= 32;
+ }
+ if (!size)
+ return result;
+ tmp = *p;
found_first:
- tmp |= ~0UL << size;
+ tmp |= ~0UL << size;
found_middle:
- return result + ffz(tmp);
+ return result + ffz(tmp);
+#endif
}
/* Linus sez that gcc can optimize the following correctly, we'll see if this
#define CONFIG_BUGSPARC
#include <asm/openprom.h>
+#include <asm/page.h>
-extern struct linux_romvec *romvec;
-extern int tbase_needs_unmapping; /* We do the bug workaround in pagetables.c */
+extern pgd_t swapper_pg_dir[16384];
static void check_mmu(void)
{
lvec = romvec;
root_node = (*(romvec->pv_nodeops->no_nextnode))(0);
- tbase_needs_unmapping=0;
present = 0;
(*(romvec->pv_nodeops->no_getprop))(root_node, "buserr-type",
(char *) &present);
if(present == 1)
{
- tbase_needs_unmapping=1;
- printk("MMU bug found: not allowing trapbase to be cached\n");
+ printk("MMU bug found: uncaching trap table\n");
+ for(present = (unsigned long) &trapbase; present < (unsigned long)
+ &swapper_pg_dir; present+=PAGE_SIZE)
+ put_pte(present, (get_pte(present) | PTE_NC));
}
return;
--- /dev/null
+/* cache.h: Cache specific code for the Sparc. These include flushing
+ * and direct tag/data line access.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#ifndef _SPARC_CACHE_H
+#define _SPARC_CACHE_H
+
+#include <asm/asi.h>
+
+/* Direct access to the instruction cache is provided through and
+ * alternate address space. The IDC bit must be off in the ICCR on
+ * HyperSparcs for these accesses to work. The code below does not do
+ * any checking, the caller must do so. These routines are for
+ * diagnostics only, but coule end up being useful. Use with care.
+ * Also, you are asking for trouble if you execute these in one of the
+ * three instructions following a %asr/%psr access or modification.
+ */
+
+/* First, cache-tag access. */
+extern inline unsigned int get_icache_tag(int setnum, int tagnum)
+{
+ unsigned int vaddr, retval;
+
+ vaddr = ((setnum&1) << 12) | ((tagnum&0x7f) << 5);
+ __asm__ __volatile__("lda [%1] %2, %0\n\t" :
+ "=r" (retval) :
+ "r" (vaddr), "i" (ASI_M_TXTC_TAG));
+ return retval;
+}
+
+extern inline void put_icache_tag(int setnum, int tagnum, unsigned int entry)
+{
+ unsigned int vaddr;
+
+ vaddr = ((setnum&1) << 12) | ((tagnum&0x7f) << 5);
+ __asm__ __volatile__("sta %0, [%1] %2\n\t" : :
+ "r" (entry), "r" (vaddr), "i" (ASI_M_TXTC_TAG) :
+ "memory");
+ return;
+}
+
+/* Second cache-data access. The data is returned two-32bit quantities
+ * at a time.
+ */
+extern inline void get_icache_data(int setnum, int tagnum, int subblock,
+ unsigned int *data)
+{
+ unsigned int value1, value2, vaddr;
+
+ vaddr = ((setnum&0x1) << 12) | ((tagnum&0x7f) << 5) | ((subblock&0x3) << 3);
+ __asm__ __volatile__("ldda [%2] %3, %%g2\n\t"
+ "or %%g0, %%g2, %0\n\t"
+ "or %%g0, %%g3, %1\n\t" :
+ "=r" (value1), "=r" (value2) :
+ "r" (vaddr), "i" (ASI_M_TXTC_DATA) :
+ "g2", "g3");
+ data[0] = value1; data[1] = value2;
+ return;
+}
+
+extern inline void put_icache_data(int setnum, int tagnum, int subblock,
+ unsigned int *data)
+{
+ unsigned int value1, value2, vaddr;
+
+ vaddr = ((setnum&0x1) << 12) | ((tagnum&0x7f) << 5) | ((subblock&0x3) << 3);
+ value1 = data[0]; value2 = data[1];
+ __asm__ __volatile__("or %%g0, %0, %%g2\n\t"
+ "or %%g0, %1, %%g3\n\t"
+ "stda %%g2, [%2] %3\n\t" : :
+ "r" (value1), "r" (value2),
+ "r" (vaddr), "i" (ASI_M_TXTC_DATA) :
+ "g2", "g3", "memory" /* no joke */);
+ return;
+}
+
+/* Different types of flushes with the ICACHE. Some of the flushes
+ * affect both the ICACHE and the external cache. Others only clear
+ * the ICACHE entries on the cpu itself. V8's (most) allow
+ * granularity of flushes on the packet (element in line), whole line,
+ * and entire cache (ie. all lines) level. The ICACHE only flushes are
+ * ROSS HyperSparc specific and are in ross.h
+ */
+
+/* Flushes which clear out both the on-chip and external caches */
+extern inline void flush_ei_page(unsigned int addr)
+{
+ __asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
+ "r" (addr), "i" (ASI_M_FLUSH_PAGE) :
+ "memory");
+ return;
+}
+
+extern inline void flush_ei_seg(unsigned int addr)
+{
+ __asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
+ "r" (addr), "i" (ASI_M_FLUSH_SEG) :
+ "memory");
+ return;
+}
+
+extern inline void flush_ei_region(unsigned int addr)
+{
+ __asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
+ "r" (addr), "i" (ASI_M_FLUSH_REGION) :
+ "memory");
+ return;
+}
+
+extern inline void flush_ei_ctx(unsigned int addr)
+{
+ __asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
+ "r" (addr), "i" (ASI_M_FLUSH_CTX) :
+ "memory");
+ return;
+}
+
+extern inline void flush_ei_user(unsigned int addr)
+{
+ __asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
+ "r" (addr), "i" (ASI_M_FLUSH_USER) :
+ "memory");
+ return;
+}
+
+#endif /* !(_SPARC_CACHE_H) */
-/* clock.h: Definitions for the clock/timer chips on the Sparc.
+/* clock.h: Definitions for clock operations on the Sparc.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
*/
+#ifndef _SPARC_CLOCK_H
+#define _SPARC_CLOCK_H
-/* Clock timer structures. The interrupt timer has two properties which
- * are the counter (which is handled in do_timer in sched.c) and the limit.
- * This limit is where the timer's counter 'wraps' around. Oddly enough,
- * the sun4c timer when it hits the limit wraps back to 1 and not zero
- * thus when calculating the value at which it will fire a microsecond you
- * must adjust by one. Thanks SUN for designing such great hardware ;(
- */
-
-/* Note that I am only going to use the timer that interrupts at
- * Sparc IRQ 10. There is another one available that can fire at
- * IRQ 14. If I can think of some creative uses for it this may
- * change. It might make a nice kernel/user profiler etc.
- */
-
-struct sparc_timer_info {
- unsigned int cur_count10;
- unsigned int timer_limit10;
- unsigned int cur_count14;
- unsigned int timer_limit14;
-};
-
-struct sparc_clock_info {
- unsigned char hsec;
- unsigned char hr;
- unsigned char min;
- unsigned char sec;
- unsigned char mon;
- unsigned char day;
- unsigned char yr;
- unsigned char wkday;
- unsigned char ram_hsec;
- unsigned char ram_hr;
- unsigned char ram_min;
- unsigned char ram_sec;
- unsigned char ram_mon;
- unsigned char ram_day;
- unsigned char ram_year;
- unsigned char ram_wkday;
- unsigned char intr_reg;
- unsigned char cmd_reg;
- unsigned char foo[14];
-};
-
-#define TIMER_PHYSADDR 0xf3000000
-
-/* YUCK YUCK YUCK, grrr... */
-#define TIMER_STRUCT ((struct sparc_timer_info *)((struct sparc_clock_info *) TIMER_VADDR))
+/* Foo for now. */
+#endif /* !(_SPARC_CLOCK_H) */
#define _SPARC_CONTREGS_H
/* contregs.h: Addresses of registers in the ASI_CONTROL alternate address
- space. These are for the mmu's context register, etc.
+ * space. These are for the mmu's context register, etc.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
- Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
-*/
+/* 4 = sun4 (as in sun4 sysmaint student book), c = sun4c (according to davem) */
-#define AC_CONTEXT 0x30000000 /* current mmu-context, handy for invalidate()'s ;-) */
-#define AC_SENABLE 0x40000000 /* system dvma/cache enable, plus special reset poking */
-#define AC_CACHETAGS 0x80000000 /* direct access to the VAC cache, unused... */
-#define AC_SYNC_ERR 0x60000000 /* what type of synchronous memory error happened */
-#define AC_SYNC_VA 0x60000004 /* what virtual address caused the error to occur */
-#define AC_ASYNC_ERR 0x60000008 /* what type of asynchronous mem-error happened */
-#define AC_ASYNC_VA 0x6000000c /* what virtual address caused the async-err to happen */
-#define AC_CACHEDDATA 0x90000000 /* where the actual VAC cached data sits */
+#define AC_IDPROM 0x00000000 /* 4 ID PROM, R/O, byte, 32 bytes */
+#define AC_CONTEXT 0x30000000 /* 4c current mmu-context, handy for invalidate()'s ;-) */
+#define AC_SENABLE 0x40000000 /* 4c system dvma/cache enable, plus special reset poking */
+#define AC_UDVMA_ENB 0x50000000 /* 4 Not used on Sun boards, byte */
+#define AC_BUS_ERROR 0x60000000 /* 4 Cleared on read, byte. Probably same as sun4c. */
+#define AC_SYNC_ERR 0x60000000 /* c what type of synchronous memory error happened */
+#define AC_SYNC_VA 0x60000004 /* c what virtual address caused the error to occur */
+#define AC_ASYNC_ERR 0x60000008 /* c what type of asynchronous mem-error happened */
+#define AC_ASYNC_VA 0x6000000c /* c what virtual address caused the async-err to happen */
+#define AC_LEDS 0x70000000 /* 4 Zero turns on LEDs, byte */
+#define AC_CACHETAGS 0x80000000 /* 4c direct access to the VAC cache, unused... */
+#define AC_CACHEDDATA 0x90000000 /* c where the actual VAC cached data sits */
+#define AC_UDVMA_MAP 0xD0000000 /* 4 Not used on Sun boards, byte */
+#define AC_VME_VECTOR 0xE0000000 /* 4 For non-Autovector VME, byte */
+#define AC_BOOT_SCC 0xF0000000 /* 4 To bypass MMU and access Zilog 8530 on boot. byte. */
+
+/* SPARCstation-5. I changed Paul's names to the hardware guy's ones. --P3 */
+#define AC_M_PCR 0x0000 /* 5 Processor Control Register */
+#define AC_M_CTPR 0x0100 /* 5 Context Table Pointer Register */
+#define AC_M_CXR 0x0200 /* 5 Context Register */
+#define AC_M_SFSR 0x0300 /* 5 Synchronous Fault Status Register */
+#define AC_M_SFAR 0x0400 /* 5 Synchronous Fault Address Register */
+#define AC_M_AFSR 0x0500 /* 5 Asynchronous Fault Status Register */
+#define AC_M_AFAR 0x0600 /* 5 Asynchronous Fault Address Register */
+#define AC_M_RESET 0x0700 /* 5 Reset Register Aieee! */
+#define AC_M_RPR 0x1000 /* 5 Root Pointer Register */
+#define AC_M_TSUTRCR 0x1000 /* 5 TLB Replacement Control Reg on Tsunami */
+#define AC_M_IAPTP 0x1100 /* 5 Instruction Access PTP */
+#define AC_M_DAPTP 0x1200 /* 5 Data Access PTP */
+#define AC_M_ITR 0x1300 /* 5 Index Tag Register */
+#define AC_M_TRCR 0x1400 /* 5 TLB Replacement Control Register */
+
+/* The following are Ross HyperSparc only. */
+#define AC_M_RPR1 0x1500 /* 5 Root Pointer Register (entry 2) */
+#define AC_M_IAPTP1 0x1600 /* 5 Instruction Access PTP (entry 2) */
+#define AC_M_DAPTP1 0x1700 /* 5 Data Access PTP (entry 2) */
#endif /* _SPARC_CONTREGS_H */
--- /dev/null
+/* cypress.h: Cypress module specific definitions and defines.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#ifndef _SPARC_CYPRESS_H
+#define _SPARC_CYPRESS_H
+
+/* Cypress chips have %psr 'impl' of '0001' and 'vers' of '0001'. */
+
+/* The MMU control register fields on the Sparc Cypress 604/605 MMU's.
+ *
+ * ---------------------------------------------------------------
+ * |implvers| MCA | MCM |MV| MID |BM| C|RSV|MR|CM|CL|CE|RSV|NF|ME|
+ * ---------------------------------------------------------------
+ * 31 24 23-22 21-20 19 18-15 14 13 12 11 10 9 8 7-2 1 0
+ *
+ * MCA: MultiChip Access -- Used for configuration of multiple
+ * CY7C604/605 cache units.
+ * MCM: MultiChip Mask -- Again, for multiple cache unit config.
+ * MV: MultiChip Valid -- Indicates MCM and MCA have valid settings.
+ * MID: ModuleID -- Unique processor ID for MBus transactions. (605 only)
+ * BM: Boot Mode -- 0 = not in boot mode, 1 = in boot mode
+ * C: Cacheable -- Indicates whether accesses are cacheable while
+ * the MMU is off. 0=no 1=yes
+ * MR: MemoryReflection -- Indicates whether the bus attacted to the
+ * MBus supports memory reflection. 0=no 1=yes (605 only)
+ * CM: CacheMode -- Indicates whether the cache is operating in write
+ * through or copy-back mode. 0=write-through 1=copy-back
+ * CL: CacheLock -- Indicates if the entire cache is locked or not.
+ * 0=not-locked 1=locked (604 only)
+ * CE: CacheEnable -- Is the virtual cache on? 0=no 1=yes
+ * NF: NoFault -- Do faults generate traps? 0=yes 1=no
+ * ME: MmuEnable -- Is the MMU doing translations? 0=no 1=yes
+ */
+
+/* NEEDS TO BE FIXED */
+#define CYPRESS_MCABITS 0x01800000
+#define CYPRESS_MCMBITS 0x00600000
+#define CYPRESS_MVALID 0x00040000
+#define CYPRESS_MIDMASK 0x0003c000 /* Only on 605 */
+#define CYPRESS_BMODE 0x00002000
+#define CYPRESS_ACENABLE 0x00001000
+#define CYPRESS_MRFLCT 0x00000800 /* Only on 605 */
+#define CYPRESS_CMODE 0x00000400
+#define CYPRESS_CLOCK 0x00000200 /* Only on 604 */
+#define CYPRESS_CENABLE 0x00000100
+#define CYPRESS_NFAULT 0x00000002
+#define CYPRESS_MENABLE 0x00000001
+
+#endif /* !(_SPARC_CYPRESS_H) */
{
usecs *= 0x000010c6; /* Sparc is 32-bit just like ix86 */
- __asm__("sethi %hi(_loops_per_sec), %o1\n\t"
- "ld [%o1 + %lo(_loops_per_sec)], %o1\n\t"
- "call ___delay\n\t"
- "umul %o1, %o0, %o0\n\t");
+ __delay(loops_per_sec*usecs);
+
}
/* calibrate_delay() wants this... */
* on the Sparc. It may end up being real hairy to plug
* into this code, maybe not, we'll see.
*
- * Copyright (C) David S. Miller (davem@caip.rutgers.edu)
+ * Copyright 1995 (C) David S. Miller (davem@caip.rutgers.edu)
*/
+#ifndef _ASM_SPARC_DMA_H
+#define _ASM_SPARC_DMA_H
+
#include <asm/vac-ops.h> /* for invalidate's, etc. */
+#include <asm/sbus.h>
+#include <asm/delay.h>
+#include <asm/oplib.h>
+
+/* DMA probing routine */
+extern unsigned long probe_dma(unsigned long);
+/* These are irrelevant for Sparc DMA, but we leave it in so that
+ * things can compile.
+ */
#define MAX_DMA_CHANNELS 8
#define MAX_DMA_ADDRESS 0x0
-#ifndef _ASM_SPARC_DMA_H
-#define _ASM_SPARC_DMA_H
+/* Structure to describe the current status of DMA registers on the Sparc */
+struct sparc_dma_registers {
+ volatile unsigned long cond_reg; /* DMA condition register */
+ volatile char * st_addr; /* Start address of this transfer */
+ volatile unsigned long cnt; /* How many bytes to transfer */
+ volatile unsigned long dma_test; /* DMA test register */
+};
+
+/* Linux DMA information structure, filled during probe. */
+struct Linux_SBus_DMA {
+ struct linux_sbus_device *SBus_dev; /* pointer to sbus device struct */
+ struct sparc_dma_registers *DMA_regs; /* Pointer to DMA regs in IO space */
+
+ /* Status, misc info */
+ int node; /* Prom node for this DMA device */
+ int dma_running; /* Are we using the DMA now? */
+
+ /* DMA revision: 0=REV0 1=REV1 2=REV2 3=DMA_PLUS */
+ int dma_rev;
+};
+
+extern struct Linux_SBus_DMA Sparc_DMA;
+
+/* Main routines in dma.c */
+extern void dump_dma_regs(struct sparc_dma_registers *);
+extern unsigned long probe_dma(unsigned long);
+extern void sparc_dma_init_transfer(struct sparc_dma_registers *,
+ unsigned long, int, int);
+extern int sparc_dma_interrupt(struct sparc_dma_registers *);
+
+/* Fields in the cond_reg register */
+/* First, the version identification bits */
+#define DMA_DEVICE_ID 0xf0000000 /* Device identification bits */
+#define DMA_VERS0 0x00000000 /* Sunray DMA version */
+#define DMA_VERS1 0x80000000 /* DMA rev 1 */
+#define DMA_VERS2 0xa0000000 /* DMA rev 2 */
+#define DMA_VERSPLUS 0x90000000 /* DMA rev 1 PLUS */
+
+#define DMA_HNDL_INTR 0x00000001 /* An interrupt needs to be handled */
+#define DMA_HNDL_ERROR 0x00000002 /* We need to take care of an error */
+#define DMA_FIFO_ISDRAIN 0x0000000c /* The DMA FIFO is draining */
+#define DMA_INT_ENAB 0x00000010 /* Turn on interrupts */
+#define DMA_FIFO_INV 0x00000020 /* Invalidate the FIFO */
+#define DMA_ACC_SZ_ERR 0x00000040 /* The access size was bad */
+#define DMA_FIFO_STDRAIN 0x00000040 /* DMA_VERS1 Drain the FIFO */
+#define DMA_RST_SCSI 0x00000080 /* Reset the SCSI controller */
+#define DMA_ST_WRITE 0x00000100 /* If set, write from device to memory */
+#define DMA_ENABLE 0x00000200 /* Fire up DMA, handle requests */
+#define DMA_PEND_READ 0x00000400 /* DMA_VERS1/0/PLUS Read is pending */
+#define DMA_BCNT_ENAB 0x00002000 /* If on, use the byte counter */
+#define DMA_TERM_CNTR 0x00004000 /* Terminal counter */
+#define DMA_CSR_DISAB 0x00010000 /* No FIFO drains during csr */
+#define DMA_SCSI_DISAB 0x00020000 /* No FIFO drains during reg */
+#define DMA_BRST_SZ 0x000c0000 /* SBUS transfer r/w burst size */
+#define DMA_ADDR_DISAB 0x00100000 /* No FIFO drains during addr */
+#define DMA_2CLKS 0x00200000 /* Each transfer equals 2 clock ticks */
+#define DMA_3CLKS 0x00400000 /* Each transfer equals 3 clock ticks */
+#define DMA_CNTR_DISAB 0x00800000 /* No intr's when DMA_TERM_CNTR is set */
+#define DMA_AUTO_NADDR 0x01000000 /* Use "auto next address" feature */
+#define DMA_SCSI_ON 0x02000000 /* Enable SCSI dma */
+#define DMA_LOADED_ADDR 0x04000000 /* Address has been loaded */
+#define DMA_LOADED_NADDR 0x08000000 /* Next address has been loaded */
+
+/* Only 24-bits of the byte count are significant */
+#define DMA_BYTE_CNT_MASK 0x00ffffff
+
+/* Pause until counter runs out or BIT isn't set in the DMA condition
+ * register.
+ */
+extern inline void sparc_dma_pause(struct sparc_dma_registers *dma_regs,
+ unsigned long bit)
+{
+ int ctr = 50000; /* Let's find some bugs ;) */
+
+ /* Busy wait until the bit is not set any more */
+ while((dma_regs->cond_reg&bit) && (ctr>0)) {
+ ctr--;
+ __delay(1);
+ }
+
+ /* Check for bogus outcome. */
+ if(ctr==0) {
+ printk("DMA Grrr: I tried for wait for the assertion of bit %08xl to clear",
+ (unsigned int) bit);
+ printk(" in the DMA condition register and it did not!\n");
+ printk("Cannot continue, halting...\n");
+ prom_halt();
+ }
+
+ return;
+}
+
+/* Enable DMA interrupts */
+extern inline void sparc_dma_enable_interrupts(struct sparc_dma_registers *dma_regs)
+{
+ dma_regs->cond_reg |= DMA_INT_ENAB;
+}
+
+/* Disable DMA interrupts from coming in */
+extern inline void sparc_dma_disable_interrupts(struct sparc_dma_registers *dma_regs)
+{
+ dma_regs->cond_reg &= ~(DMA_INT_ENAB);
+}
+
+/* Reset the DMA module. */
+extern inline void sparc_dma_reset(struct sparc_dma_registers *dma_regs)
+{
+ /* Let the current FIFO drain itself */
+ sparc_dma_pause(dma_regs, (DMA_FIFO_ISDRAIN));
+
+ /* Reset the logic */
+ dma_regs->cond_reg |= (DMA_RST_SCSI); /* assert */
+ __delay(400); /* let the bits set ;) */
+ dma_regs->cond_reg &= ~(DMA_RST_SCSI); /* de-assert */
+
+ sparc_dma_enable_interrupts(dma_regs); /* Re-enable interrupts */
+
+ /* Enable FAST transfers if available */
+ if(Sparc_DMA.dma_rev>1) { dma_regs->cond_reg |= DMA_3CLKS; }
+ Sparc_DMA.dma_running = 0;
+ return;
+}
#endif /* !(_ASM_SPARC_DMA_H) */
--- /dev/null
+/* ecc.h: Definitions and defines for the external cache/memory
+ * controller on the sun4m.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#ifndef _SPARC_ECC_H
+#define _SPARC_ECC_H
+
+/* These registers are accessed through the SRMMU passthrough ASI 0x20 */
+#define ECC_ENABLE 0x00000000 /* ECC enable register */
+#define ECC_FSTATUS 0x00000008 /* ECC fault status register */
+#define ECC_FADDR 0x00000010 /* ECC fault address register */
+#define ECC_DIGNOSTIC 0x00000018 /* ECC diagnostics register */
+#define ECC_MBAENAB 0x00000020 /* MBus arbiter enable register */
+#define ECC_DMESG 0x00001000 /* Diagnostic message passing area */
+
+/* ECC MBus Arbiter Enable register:
+ *
+ * ----------------------------------------
+ * | |SBUS|MOD3|MOD2|MOD1|RSV|
+ * ----------------------------------------
+ * 31 5 4 3 2 1 0
+ *
+ * SBUS: Enable MBus Arbiter on the SBus 0=off 1=on
+ * MOD3: Enable MBus Arbiter on MBus module 3 0=off 1=on
+ * MOD2: Enable MBus Arbiter on MBus module 2 0=off 1=on
+ * MOD1: Enable MBus Arbiter on MBus module 1 0=off 1=on
+ */
+
+#define ECC_MBAE_SBUS 0x00000010
+#define ECC_MBAE_MOD3 0x00000008
+#define ECC_MBAE_MOD2 0x00000004
+#define ECC_MBAE_MOD1 0x00000002
+
+/* ECC Fault Control Register layout:
+ *
+ * -----------------------------
+ * | RESV | ECHECK | EINT |
+ * -----------------------------
+ * 31 2 1 0
+ *
+ * ECHECK: Enable ECC checking. 0=off 1=on
+ * EINT: Enable Interrupts for correctable errors. 0=off 1=on
+ */
+#define ECC_FCR_CHECK 0x00000002
+#define ECC_FCR_INTENAB 0x00000001
+
+/* ECC Fault Address Register Zero layout:
+ *
+ * -----------------------------------------------------
+ * | MID | S | RSV | VA | BM |AT| C| SZ |TYP| PADDR |
+ * -----------------------------------------------------
+ * 31-28 27 26-22 21-14 13 12 11 10-8 7-4 3-0
+ *
+ * MID: ModuleID of the faulting processor. ie. who did it?
+ * S: Supervisor/Privileged access? 0=no 1=yes
+ * VA: Bits 19-12 of the virtual faulting address, these are the
+ * superset bits in the virtual cache and can be used for
+ * a flush operation if necessary.
+ * BM: Boot mode? 0=no 1=yes This is just like the SRMMU boot
+ * mode bit.
+ * AT: Did this fault happen during an atomic instruction? 0=no
+ * 1=yes. This means either an 'ldstub' or 'swap' instruction
+ * was in progress (but not finished) when this fault happened.
+ * This indicated whether the bus was locked when the fault
+ * occurred.
+ * C: Did the pte for this access indicate that it was cacheable?
+ * 0=no 1=yes
+ * SZ: The size of the transaction.
+ * TYP: The transaction type.
+ * PADDR: Bits 35-32 of the physical address for the fault.
+ */
+#define ECC_FADDR0_MIDMASK 0xf0000000
+#define ECC_FADDR0_S 0x08000000
+#define ECC_FADDR0_VADDR 0x003fc000
+#define ECC_FADDR0_BMODE 0x00002000
+#define ECC_FADDR0_ATOMIC 0x00001000
+#define ECC_FADDR0_CACHE 0x00000800
+#define ECC_FADDR0_SIZE 0x00000700
+#define ECC_FADDR0_TYPE 0x000000f0
+#define ECC_FADDR0_PADDR 0x0000000f
+
+/* ECC Fault Address Register One layout:
+ *
+ * -------------------------------------
+ * | Physical Address 31-0 |
+ * -------------------------------------
+ * 31 0
+ *
+ * You get the upper 4 bits of the physical address from the
+ * PADDR field in ECC Fault Address Zero register.
+ */
+
+/* ECC Fault Status Register layout:
+ *
+ * ----------------------------------------------
+ * | RESV|C2E|MULT|SYNDROME|DWORD|UNC|TIMEO|BS|C|
+ * ----------------------------------------------
+ * 31-18 17 16 15-8 7-4 3 2 1 0
+ *
+ * C2E: A C2 graphics error occurred. 0=no 1=yes (SS10 only)
+ * MULT: Multiple errors occurres ;-O 0=no 1=prom_panic(yes)
+ * SYNDROME: Controller is mentally unstable.
+ * DWORD:
+ * UNC: Uncorrectable error. 0=no 1=yes
+ * TIMEO: Timeout occurred. 0=no 1=yes
+ * BS: C2 graphics bad slot access. 0=no 1=yes (SS10 only)
+ * C: Correctable error? 0=no 1=yes
+ */
+
+#define ECC_FSR_C2ERR 0x00020000
+#define ECC_FSR_MULT 0x00010000
+#define ECC_FSR_SYND 0x0000ff00
+#define ECC_FSR_DWORD 0x000000f0
+#define ECC_FSR_UNC 0x00000008
+#define ECC_FSR_TIMEO 0x00000004
+#define ECC_FSR_BADSLOT 0x00000002
+#define ECC_FSR_C 0x00000001
+
+#endif /* !(_SPARC_ECC_H) */
--- /dev/null
+/* eeprom.h: Definitions for the Sun eeprom.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+/* The EEPROM and the Mostek Mk48t02 use the same IO address space
+ * for their registers/data areas. The IDPROM lives here too.
+ */
#ifndef __SPARC_HEAD_H
#define __SPARC_HEAD_H
-#define KERNSIZE 134217728 /* this is how much of a mapping the prom promises */
-#define PAGESIZE 4096 /* luckily this is the same on sun4c's and sun4m's */
-#define PAGESHIFT 12
-#define PROM_BASE -1568768 /* casa 'de PROM */
-#define LOAD_ADDR 0x4000 /* prom jumps to us here */
+#define KERNBASE 0xf0000000 /* First address the kernel will eventually be */
+#define LOAD_ADDR 0x4000 /* prom jumps to us here unless this is elf /boot */
#define C_STACK 96
#define SUN4C_SEGSZ (1 << 18)
-#define USRSTACK 0x0 /* no joke, this is temporary, trust me */
-#define INT_ENABLE_REG_PHYSADR 0xf5000000
-#define INTS_ENAB 0x01
+#define SRMMU_L1_KBASE_OFFSET ((KERNBASE>>24)<<2) /* Used in boot remapping. */
+#define INTS_ENAB 0x01 /* entry.S uses this. */
-#define BOOT_MSG_LEN 61
-#define BOOT_MSG2_LEN 50
+#define NCPUS 4 /* Architectual limit of sun4m. */
+#define SUN4_PROM_VECTOR 0xFFE81000 /* To safely die on a SUN4 */
+#define SUN4_PRINTF 0x84 /* Offset into SUN4_PROM_VECTOR */
#define WRITE_PAUSE nop; nop; nop;
-#define PAGE_SIZE 4096
+#define NOP_INSN 0x01000000 /* Used to patch sparc_save_state */
/* Here are some trap goodies */
-
/* Generic trap entry. */
-
#define TRAP_ENTRY(type, label) \
mov (type), %l3; b label; rd %psr, %l0; nop;
+/* Notice that for the system calls we pull a trick. We load up a
+ * different pointer to the system call vector table in %l7, but call
+ * the same generic system call low-level entry point. The trap table
+ * entry sequences are also HyperSparc pipeline friendly ;-)
+ */
+
+/* Software trap for Linux system calls. */
+#define LINUX_SYSCALL_TRAP \
+ sethi %hi(C_LABEL(sys_call_table)), %l7; or %l7, %lo(C_LABEL(sys_call_table)), %l7; b linux_sparc_syscall; mov %psr, %l0;
+
+/* Software trap for SunOS4.1.x system calls. */
+#define SUNOS_SYSCALL_TRAP \
+ sethi %hi(C_LABEL(sys_call_table)), %l7; or %l7, %lo(C_LABEL(sys_call_table)), %l7; b linux_sparc_syscall; mov %psr, %l0;
+
+/* Software trap for Slowaris system calls. */
+#define SOLARIS_SYSCALL_TRAP \
+ sethi %hi(C_LABEL(sys_call_table)), %l7; or %l7, %lo(C_LABEL(sys_call_table)), %l7; b linux_sparc_syscall; mov %psr, %l0;
+
+/* Software trap for Sparc-netbsd system calls. */
+#define NETBSD_SYSCALL_TRAP \
+ sethi %hi(C_LABEL(sys_call_table)), %l7; or %l7, %lo(C_LABEL(sys_call_table)), %l7; b linux_sparc_syscall; mov %psr, %l0;
+
+/* The Get Condition Codes software trap for userland. */
+#define GETCC_TRAP \
+ b getcc_trap_handler; mov %psr, %l0; nop; nop
+
+/* The Set Condition Codes software trap for userland. */
+#define SETCC_TRAP \
+ b setcc_trap_handler; mov %psr, %l0; nop; nop
+
/* This is for hard interrupts from level 1-14, 15 is non-maskable (nmi) and
* gets handled with another macro.
*/
-
#define TRAP_ENTRY_INTERRUPT(int_level) \
- mov int_level, %l3; b stray_irq_entry; rd %psr, %l0; nop;
+ mov int_level, %l3; b real_irq_entry; rd %psr, %l0; nop;
-/* Here is the macro for soft interrupts (ie. not as urgent as hard ones)
- * which need to jump to a different handler.
+/* NMI's (Non Maskable Interrupts) are special, you can't keep them
+ * from coming in, and basically if you get one, the shows over. ;(
*/
-
-#define TRAP_ENTRY_INTERRUPT_SOFT(int_level, ident) \
- mov int_level, %l3; rd %psr, %l0; b stray_irq_entry; mov ident, %l4;
+#define NMI_TRAP \
+ b linux_trap_nmi; mov %psr, %l0; nop; nop
/* The above two macros are for generic traps. The following is made
* especially for timer interrupts at IRQ level 14.
*/
-
#define TRAP_ENTRY_TIMER \
- mov 10, %l3; rd %psr, %l0; b sparc_timer; nop;
-
-/* Non-maskable interrupt entry macro. You have to turn off all interrupts
- * to not receive this. This is usually due to a asynchronous memory error.
- * All we can really do is stop the show. :-(
- */
-
-#define TRAP_ENTRY_INTERRUPT_NMI(t_type, jmp_to) \
- mov t_type, %l3; b jmp_to; rd %psr, %l0; nop;
+ rd %psr, %l0; b sparc_timer; nop; nop;
/* Trap entry code in entry.S needs the offsets into task_struct
* to get at the thread_struct goodies during window craziness.
* See TRAP_WIN_CLEAN in entry.S for details.
*/
-#define THREAD_UWINDOWS 0x3bc
-#define THREAD_WIM 0x3c0
-#define THREAD_W_SAVED 0x3c4
-#define THREAD_KSP 0x3c8
-#define THREAD_USP 0x3cc
-#define THREAD_REG_WINDOW 0x3d4
+/* First generic task_struct offsets */
+#define TASK_STATE 0x000
+#define TASK_PRI 0x008
+#define TASK_KSTACK_PG 0x250
+
+#define THREAD_UWINDOWS 0x3b8
+#define THREAD_WIM 0x3bc
+#define THREAD_W_SAVED 0x3c0
+#define THREAD_KSP 0x3c4
+#define THREAD_USP 0x3c8
+#define THREAD_PSR 0x3cc
+#define THREAD_PC 0x3d0
+#define THREAD_NPC 0x3d4
+#define THREAD_Y 0x3d8
+#define THREAD_REG_WINDOW 0x3e0
+
+/* More fun offset macros. These are for pt_regs. */
+
+#define PT_PSR 0x0
+#define PT_PC 0x4
+#define PT_NPC 0x8
+#define PT_Y 0xc
+#define PT_G0 0x10
+#define PT_G1 0x14
+#define PT_G2 0x18
+#define PT_G3 0x1c
+#define PT_G4 0x20
+#define PT_G5 0x24
+#define PT_G6 0x28
+#define PT_G7 0x2c
+#define PT_I0 0x30
+#define PT_I1 0x34
+#define PT_I2 0x38
+#define PT_I3 0x3c
+#define PT_I4 0x40
+#define PT_I5 0x44
+#define PT_I6 0x48
+#define PT_I7 0x4c
#endif __SPARC_HEAD_H
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
*/
+#ifndef _SPARC_IDPROM_H
+#define _SPARC_IDPROM_H
+
extern struct linux_romvec *romvec;
-#define IDPROM_ADDR (0xffd04000 + 0x7d8)
-#define IDPROM_SIZE 36
+/* Offset into the EEPROM where the id PROM is located on the 4c */
+#define IDPROM_OFFSET 0x7d8
+
+/* On sun4m; physical. */
+/* MicroSPARC(-II) does not decode 31rd bit, but it works. */
+#define IDPROM_OFFSET_M 0xfd8
struct idp_struct
{
unsigned char dummy[16]; /* XXX */
};
+extern struct idp_struct *idprom;
+
+#define IDPROM_SIZE (sizeof(struct idp_struct))
+
+#endif /* !(_SPARC_IDPROM_H) */
#ifndef __SPARC_IO_H
#define __SPARC_IO_H
+#include <linux/kernel.h>
+
#include <asm/page.h> /* IO address mapping routines need this */
+#include <asm/system.h>
/*
* Defines for io operations on the Sparc. Whether a memory access is going
#define inb_p inb
#define outb_p outb
-extern inline void mapioaddr(unsigned long physaddr, unsigned long virt_addr)
-{
- unsigned long page_entry;
-
- page_entry = physaddr >> PAGE_SHIFT;
- page_entry |= (PTE_V | PTE_ACC | PTE_NC | PTE_IO); /* kernel io addr */
+extern void sun4c_mapioaddr(unsigned long, unsigned long, int bus_type, int rdonly);
+extern void srmmu_mapioaddr(unsigned long, unsigned long, int bus_type, int rdonly);
- put_pte(virt_addr, page_entry);
- return;
+extern inline void mapioaddr(unsigned long physaddr, unsigned long virt_addr,
+ int bus, int rdonly)
+{
+ switch(sparc_cpu_model) {
+ case sun4c:
+ sun4c_mapioaddr(physaddr, virt_addr, bus, rdonly);
+ break;
+ case sun4m:
+ case sun4d:
+ case sun4e:
+ srmmu_mapioaddr(physaddr, virt_addr, bus, rdonly);
+ break;
+ default:
+ printk("mapioaddr: Trying to map IO space for unsupported machine.\n");
+ printk("mapioaddr: sparc_cpu_model = %d\n", sparc_cpu_model);
+ printk("mapioaddr: Halting...\n");
+ halt();
+ };
+ return;
}
+extern void *sparc_alloc_io (void *, void *, int, char *, int, int);
+extern void *sparc_dvma_malloc (int, char *);
+
#endif /* !(__SPARC_IO_H) */
--- /dev/null
+#ifndef __SPARC_IPSUM_H
+#define __SPARC_IPSUM_H
+/* ipsum.h: IP/UDP/TCP checksum routines on the Sparc.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+/*
+ * This routine computes a UDP checksum.
+ */
+extern inline unsigned short udp_check(struct udphdr *uh, int len, u32 saddr, u32 daddr)
+{
+ /* uhh.. eventually */
+ return 0;
+}
+
+/*
+ * This routine computes a TCP checksum.
+ */
+extern inline unsigned short tcp_check(struct tcphdr *th, int len, u32 saddr, u32 daddr)
+{
+ /* uhh.. eventually */
+ return 0;
+}
+
+
+/*
+ * This routine does all the checksum computations that don't
+ * require anything special (like copying or special headers).
+ */
+
+extern inline unsigned short ip_compute_csum(unsigned char * buff, int len)
+{
+ /* uhh.. eventually */
+ return 0;
+}
+
+/*
+ * This is a version of ip_compute_csum() optimized for IP headers, which
+ * always checksum on 4 octet boundaries.
+ */
+
+static inline unsigned short ip_fast_csum(unsigned char * buff, int wlen)
+{
+ /* uhh.. eventually */
+ return 0;
+}
+
+#endif /* !(__SPARC_IPSUM_H) */
/*
* linux/include/asm-sparc/irq.h
*
- * Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
*/
#include <linux/linkage.h>
+#include <asm/system.h> /* For NCPUS */
+
extern void disable_irq(unsigned int);
extern void enable_irq(unsigned int);
+/* On the sun4m, just like the timers, we have both per-cpu and master
+ * interrupt registers.
+ */
+
+/* These registers are used for sending/receiving irqs from/to
+ * different cpu's.
+ */
+struct sun4m_intreg_percpu {
+ unsigned int tbt; /* Interrupts still pending for this cpu. */
+
+ /* These next two registers are WRITE-ONLY and are only
+ * "on bit" sensitive, "off bits" written have NO affect.
+ */
+ unsigned int clear; /* Clear this cpus irqs here. */
+ unsigned int set; /* Set this cpus irqs here. */
+ unsigned char space[PAGE_SIZE - 12];
+};
+
+struct sun4m_intregs {
+ struct sun4m_intreg_percpu cpu_intregs[NCPUS];
+ unsigned int tbt; /* IRQ's that are still pending. */
+ unsigned int irqs; /* Master IRQ bits. */
+
+ /* Again, like the above, two these registers are WRITE-ONLY. */
+ unsigned int clear; /* Clear master IRQ's by setting bits here. */
+ unsigned int set; /* Set master IRQ's by setting bits here. */
+
+ /* This register is both READ and WRITE. */
+ unsigned int undirected_target; /* Which cpu gets undirected irqs. */
+};
+
+extern struct sun4m_intregs *sun4m_interrupts;
+
+/* Bit field defines for the interrupt registers on various
+ * Sparc machines.
+ */
+
+/* The sun4c interrupt register. */
+#define SUN4C_INT_ENABLE 0x01 /* Allow interrupts. */
+#define SUN4C_INT_E14 0x80 /* Enable level 14 IRQ. */
+#define SUN4C_INT_E10 0x20 /* Enable level 10 IRQ. */
+#define SUN4C_INT_E8 0x10 /* Enable level 8 IRQ. */
+#define SUN4C_INT_E6 0x08 /* Enable level 6 IRQ. */
+#define SUN4C_INT_E4 0x04 /* Enable level 4 IRQ. */
+#define SUN4C_INT_E1 0x02 /* Enable level 1 IRQ. */
+
+/* The sun4m interrupt registers. MUST RESEARCH THESE SOME MORE XXX */
+#define SUN4M_INT_ENABLE 0x80000000
+#define SUN4M_INT_E14 0x00000080
+#define SUN4M_INT_E10 0x00080000
+
+#if 0 /* These aren't used on the Sparc (yet), but kept for
+ * future reference, they could come in handy.
+ */
#define __STR(x) #x
#define STR(x) __STR(x)
asmlinkage void FAST_IRQ_NAME(nr); \
asmlinkage void BAD_IRQ_NAME(nr); \
asm code comes here
+#endif
#endif
--- /dev/null
+/* kdebug.h: Defines and definitions for debugging the Linux kernel
+ * under various kernel debuggers.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+#ifndef _SPARC_KDEBUG_H
+#define _SPARC_KDEBUG_H
+
+#include <asm/openprom.h>
+
+/* The debugger lives in 1MB of virtual address space right underneath
+ * the boot prom.
+ */
+
+#define DEBUG_FIRSTVADDR 0xffc00000
+#define DEBUG_LASTVADDR LINUX_OPPROM_BEGVM
+
+/* Breakpoints are enter through trap table entry 126. So in sparc assembly
+ * if you want to drop into the debugger you do:
+ *
+ * t DEBUG_BP_TRAP
+ */
+
+#define DEBUG_BP_TRAP 126
+
+#ifndef __ASSEMBLY__
+/* The debug vector is passed in %o1 at boot time. It is a pointer to
+ * a structure in the debuggers address space. Here is it's format.
+ */
+
+typedef unsigned int (*debugger_funct)(void);
+
+struct kernel_debug {
+ /* First the entry point into the debugger. You jump here
+ * to give control over to the debugger.
+ */
+ unsigned long kdebug_entry;
+ unsigned long kdebug_trapme; /* Figure out later... */
+ /* The following is the number of pages that the debugger has
+ * taken from to total pool.
+ */
+ unsigned long *kdebug_stolen_pages;
+ /* Ok, after you remap yourself and/or change the trap table
+ * from what you were left with at boot time you have to call
+ * this synchronization function so the debugger can check out
+ * what you have done.
+ */
+ debugger_funct teach_debugger;
+}; /* I think that is it... */
+
+extern struct kernel_debug *linux_dbvec;
+
+/* Use this macro in C-code to enter the debugger. */
+extern __inline__ void sp_enter_debugger(void)
+{
+ printk("Entering debugger in file %s line %d\n", __FILE__, __LINE__);
+ __asm__ __volatile__("jmpl %0, %%o7\n\t"
+ "nop\n\t" : :
+ "r" (linux_dbvec) : "o7", "memory");
+}
+
+#define SP_ENTER_DEBUGGER do { \
+ if((linux_dbvec!=0) && ((*(short *)linux_dbvec)!=-1)) \
+ sp_enter_debugger(); \
+ } while(0)
+
+#endif /* !(__ASSEMBLY__) */
+
+/* Some nice offset defines for assembler code. */
+#define KDEBUG_ENTRY_OFF 0x0
+#define KDEBUG_DUNNO_OFF 0x4
+#define KDEBUG_DUNNO2_OFF 0x8
+#define KDEBUG_TEACH_OFF 0xc
+
+#endif /* !(_SPARC_KDEBUG_H) */
--- /dev/null
+/* machines.h: Defines for taking apart the machine type value in the
+ * idprom and determining the kind of machine we are on.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+#ifndef _SPARC_MACHINES_H
+#define _SPARC_MACHINES_H
+
+struct Sun_Machine_Models {
+ char *name;
+ unsigned char id_machtype;
+};
+
+/* Current number of machines we know about that has an IDPROM
+ * machtype entry including one entry for the 0x80 OBP machines.
+ */
+#define NUM_SUN_MACHINES 15
+
+extern struct Sun_Machine_Models Sun_Machines[NUM_SUN_MACHINES];
+
+/* The machine type in the idprom area looks like this:
+ *
+ * ---------------
+ * | ARCH | MACH |
+ * ---------------
+ * 7 4 3 0
+ *
+ * The ARCH field determines the architecture line (sun4, sun4c, etc).
+ * The MACH field determines the machine make within that architecture.
+ */
+
+#define SM_ARCH_MASK 0xf0
+#define SM_SUN4 0x20
+#define SM_SUN4C 0x50
+#define SM_SUN4M 0x70
+#define SM_SUN4M_OBP 0x80
+
+#define SM_TYP_MASK 0x0f
+/* Sun4 machines */
+#define SM_4_260 0x01 /* Sun 4/200 series */
+#define SM_4_110 0x02 /* Sun 4/100 series */
+#define SM_4_330 0x03 /* Sun 4/300 series */
+#define SM_4_470 0x04 /* Sun 4/400 series */
+
+/* Sun4c machines Full Name - PROM NAME */
+#define SM_4C_SS1 0x01 /* Sun4c SparcStation 1 - Sun 4/60 */
+#define SM_4C_IPC 0x02 /* Sun4c SparcStation IPC - Sun 4/40 */
+#define SM_4C_SS1PLUS 0x03 /* Sun4c SparcStation 1+ - Sun 4/65 */
+#define SM_4C_SLC 0x04 /* Sun4c SparcStation SLC - Sun 4/20 */
+#define SM_4C_SS2 0x05 /* Sun4c SparcStation 2 - Sun 4/75 */
+#define SM_4C_ELC 0x06 /* Sun4c SparcStation ELC - Sun 4/25 */
+#define SM_4C_IPX 0x07 /* Sun4c SparcStation IPX - Sun 4/50 */
+
+/* Sun4m machines, these predate the OpenBoot. These values only mean
+ * something if the value in the ARCH field is SM_SUN4M, if it is
+ * SM_SUN4M_OBP then you have the following situation:
+ * 1) You either have a sun4d, a sun4e, or a recently made sun4m.
+ * 2) You have to consult OpenBoot to determine which machine this is.
+ */
+#define SM_4M_SS60 0x01 /* Sun4m SparcSystem 600 */
+#define SM_4M_SS50 0x02 /* Sun4m SparcStation 10 */
+#define SM_4M_SS40 0x03 /* Sun4m SparcStation 5 */
+
+/* Sun4d machines -- N/A */
+/* Sun4e machines -- N/A */
+/* Sun4u machines -- N/A */
+
+#endif /* !(_SPARC_MACHINES_H) */
--- /dev/null
+/* mbus.h: Various defines for MBUS modules.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#ifndef _SPARC_MBUS_H
+#define _SPARC_MBUS_H
+
+#include <asm/ross.h> /* HyperSparc stuff */
+#include <asm/cypress.h> /* Cypress Chips */
+#include <asm/viking.h> /* Ugh, bug city... */
+
+enum mbus_module {
+ HyperSparc = 0,
+ Cypress = 1,
+ Cypress_vE = 2,
+ Cypress_vD = 3,
+ Swift_ok = 4,
+ Swift_bad_c = 5,
+ Swift_lots_o_bugs = 6,
+ Tsunami = 7,
+ Viking_12 = 8,
+ Viking_2x = 9,
+ Viking_30 = 10,
+ Viking_35 = 11,
+ Viking_new = 12,
+ SRMMU_INVAL_MOD = 13,
+};
+
+extern enum mbus_module srmmu_modtype;
+extern unsigned int viking_rev, swift_rev, cypress_rev;
+
+/* HW Mbus module bugs we have to deal with */
+#define HWBUG_COPYBACK_BROKEN 0x00000001
+#define HWBUG_ASIFLUSH_BROKEN 0x00000002
+#define HWBUG_VACFLUSH_BITROT 0x00000004
+#define HWBUG_KERN_ACCBROKEN 0x00000008
+#define HWBUG_KERN_CBITBROKEN 0x00000010
+#define HWBUG_MODIFIED_BITROT 0x00000020
+#define HWBUG_PC_BADFAULT_ADDR 0x00000040
+#define HWBUG_SUPERSCALAR_BAD 0x00000080
+#define HWBUG_PACINIT_BITROT 0x00000100
+
+extern unsigned int hwbug_bitmask;
+
+/* First the module type values. To find out which you have, just load
+ * the mmu control register from ASI_M_MMUREG alternate adress space and
+ * shift the value right 28 bits.
+ */
+/* IMPL field means the company which produced the chip. */
+#define MBUS_VIKING 0x4 /* bleech, Texas Instruments Module */
+#define MBUS_LSI 0x3 /* LSI Logics */
+#define MBUS_ROSS 0x1 /* Ross is nice */
+#define MBUS_FMI 0x0 /* Fujitsu Microelectronics/Swift */
+
+/* Ross Module versions */
+#define ROSS_604_REV_CDE 0x0 /* revisions c, d, and e */
+#define ROSS_604_REV_F 0x1 /* revision f */
+#define ROSS_605 0xf /* revision a, a.1, and a.2 */
+#define ROSS_605_REV_B 0xe /* revision b */
+
+/* TI Viking Module versions */
+#define VIKING_REV_12 0x1 /* Version 1.2 or SPARCclassic's CPU */
+#define VIKING_REV_2 0x2 /* Version 2.1, 2.2, 2.3, and 2.4 */
+#define VIKING_REV_30 0x3 /* Version 3.0 */
+#define VIKING_REV_35 0x4 /* Version 3.5 */
+
+/* LSI Logics. */
+#define LSI_L64815 0x0
+
+/* Fujitsu */
+#define FMI_AURORA 0x4 /* MB8690x, a Swift module... */
+
+/* For multiprocessor support we need to be able to obtain the CPU id and
+ * the MBUS Module id.
+ */
+
+/* The CPU ID is encoded in the trap base register, 20 bits to the left of
+ * bit zero, with 2 bits being significant.
+ */
+#define TBR_ID_SHIFT 0x20
+
+extern inline int get_cpuid(void)
+{
+ register int retval;
+ __asm__ __volatile__("rd %%tbr, %0\n\t"
+ "srl %0, %1, %0\n\t" :
+ "=r" (retval) :
+ "i" (TBR_ID_SHIFT));
+ return (retval & 3);
+}
+
+extern inline int get_modid(void)
+{
+ return (get_cpuid() | 0x8);
+}
+
+
+#endif /* !(_SPARC_MBUS_H) */
#define _SPARC_MEMERR_H
/* memerr.h: Bit fields in the asynchronous and synchronous memory error
- registers used to determine what 'type' of error has just
- induced a trap.
-
- Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
-*/
+ * registers used to determine what 'type' of error has just
+ * induced a trap.
+ *
+ * Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
+ */
/* synchronous error register fields come first... */
--- /dev/null
+#ifndef _SPARC_MEMREG_H
+#define _SPARC_MEMREG_H
+/* memreg.h: Definitions of the values found in the synchronous
+ * and asynchronous memory error registers when a fault
+ * occurs on the sun4c.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+/* First the synchronous error codes, these are usually just
+ * normal page faults.
+ */
+
+#define SUN4C_SYNC_WDRESET 0x1 /* watchdog reset, only the prom sees this */
+#define SUN4C_SYNC_SIZE 0x2 /* bad access size? whuz this? */
+#define SUN4C_SYNC_PARITY 0x8 /* bad ram chips caused a parity error */
+#define SUN4C_SYNC_SBUS 0x10 /* the SBUS had some problems... */
+#define SUN4C_SYNC_NOMEM 0x20 /* translation pointed to non-existant ram */
+#define SUN4C_SYNC_PROT 0x40 /* access violated pte protection settings */
+#define SUN4C_SYNC_NPRESENT 0x80 /* pte said that page was not present */
+#define SUN4C_SYNC_BADWRITE 0x8000 /* while writing something went bogus */
+
+/* Now the asynchronous error codes, these are almost always produced
+ * by the cache writing things back to memory and getting a bad translation.
+ * Bad DVMA transactions can cause these faults too.
+ */
+
+#define SUN4C_ASYNC_BADDVMA 0x10 /* error during DVMA access */
+#define SUN4C_ASYNC_NOMEM 0x20 /* write back pointed to bad phys addr */
+#define SUN4C_ASYNC_BADWB 0x80 /* write back points to non-present page */
+
+/* These are the values passed as the first arguement to the fault
+ * entry c-code from the assembly entry points.
+ */
+#define FAULT_ASYNC 0x0
+#define FAULT_SYNC 0x1
+
+#endif /* !(_SPARC_MEMREG_H) */
--- /dev/null
+/* mostek.h: Describes the various Mostek time of day clock registers.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+#ifndef _SPARC_MOSTEK_H
+#define _SPARC_MOSTEK_H
+
+#include <asm/idprom.h>
+
+/* First the Mostek 48t02 clock chip. The registers other than the
+ * control register are in binary coded decimal.
+ */
+struct mostek48t02 {
+ char eeprom[2008]; /* This is the eeprom, don't touch! */
+ struct idp_struct idprom; /* The idprom lives here. */
+ volatile unsigned char creg; /* Control register */
+ volatile unsigned char sec; /* Seconds (0-59) */
+ volatile unsigned char min; /* Minutes (0-59) */
+ volatile unsigned char hour; /* Hour (0-23) */
+ volatile unsigned char dow; /* Day of the week (1-7) */
+ volatile unsigned char dom; /* Day of the month (1-31) */
+ volatile unsigned char mnth; /* Month of year (1-12) */
+ volatile unsigned char yr; /* Year (0-99) */
+};
+
+extern struct mostek48t02 *mstk48t02_regs;
+
+/* Control register values. */
+#define MSTK_CREG_WRITE 0x80 /* Must set this before placing values. */
+#define MSTK_CREG_READ 0x40 /* Stop the clock, I want to fetch values. */
+#define MSTK_CREG_SIGN 0x20 /* Grrr... whats this??? */
+
+#define MSTK_YR_ZERO 1968 /* If year reg has zero, it is 1968 */
+#define MSTK_CVT_YEAR(yr) ((yr) + MSTK_YR_ZERO)
+
+/* Fun with masks. */
+#define MSTK_SEC_MASK 0x7f
+#define MSTK_MIN_MASK 0x7f
+#define MSTK_HOUR_MASK 0x3f
+#define MSTK_DOW_MASK 0x07
+#define MSTK_DOM_MASK 0x3f
+#define MSTK_MNTH_MASK 0x1f
+#define MSTK_YR_MASK 0xff
+
+/* Conversion routines. */
+#define MSTK_REGVAL_TO_DECIMAL(x) (((x) & 0xf) + 0xa * ((x) >> 0x4))
+#define MSTK_DECIMAL_TO_REGVAL(x) ((((x) / 0xa) << 0x4) + ((x) % 0xa))
+
+/* Macros to make register access easier on our fingers. These give you
+ * the decimal value of the register requested if applicable. You pass
+ * the a pointer to a 'struct mostek48t02'.
+ */
+#define MSTK_REG_CREG(ptr) (ptr->creg)
+#define MSTK_REG_SEC(ptr) (MSTK_REGVAL_TO_DECIMAL((ptr->sec & MSTK_SEC_MASK)))
+#define MSTK_REG_MIN(ptr) (MSTK_REGVAL_TO_DECIMAL((ptr->min & MSTK_MIN_MASK)))
+#define MSTK_REG_HOUR(ptr) (MSTK_REGVAL_TO_DECIMAL((ptr->hour & MSTK_HOUR_MASK)))
+#define MSTK_REG_DOW(ptr) (MSTK_REGVAL_TO_DECIMAL((ptr->dow & MSTK_DOW_MASK)))
+#define MSTK_REG_DOM(ptr) (MSTK_REGVAL_TO_DECIMAL((ptr->dom & MSTK_DOM_MASK)))
+#define MSTK_REG_MNTH(ptr) (MSTK_REGVAL_TO_DECIMAL((ptr->mnth & MSTK_MNTH_MASK)))
+#define MSTK_REG_YR(ptr) (MSTK_REGVAL_TO_DECIMAL((ptr->yr & MSTK_YR_MASK)))
+
+/* The Mostek 48t02 clock chip. Found on Sun4m's I think. It has the
+ * same (basically) layout of the 48t02 chip.
+ */
+struct mostek48t08 {
+ char offset[6*1024]; /* Magic things may be here, who knows? */
+ struct mostek48t02 regs; /* Here is what we are interested in. */
+};
+extern struct mostek48t08 *mstk48t08_regs;
+
+enum sparc_clock_type { MSTK48T02, MSTK48T08, MSTK_INVALID };
+extern enum sparc_clock_type sp_clock_typ;
+
+#endif /* !(_SPARC_MOSTEK_H) */
--- /dev/null
+/* mp.h: Multiprocessing definitions for the Sparc.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+#ifndef _SPARC_MP_H
+#define _SPARC_MP_H
+
+#include <asm/traps.h>
+#include <asm/page.h>
+#include <asm/vaddrs.h>
+
+extern int linux_smp_still_initting;
+
+struct sparc_percpu {
+ struct tt_entry trap_table[NUM_SPARC_TRAPS]; /* One page */
+ unsigned int kernel_stack[PAGE_SIZE/4]; /* One page */
+ int cpuid; /* Who am I? */
+ int cpu_is_alive; /* Linux has fired it up. */
+ int cpu_is_idling; /* Is sitting in the idle loop. */
+ /* More to come... */
+ char filler[PERCPU_ENTSIZE-(PAGE_SIZE*2)-0xc];
+};
+
+extern struct sparc_percpu *percpu_table;
+
+struct prom_cpuinfo {
+ int prom_node;
+ int mid;
+};
+
+extern struct prom_cpuinfo linux_cpus[NCPUS];
+
+#endif /* !(_SPARC_MP_H) */
--- /dev/null
+/* mpmbox.h: Interface and defines for the OpenProm mailbox
+ * facilities for MP machines under Linux.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#ifndef _SPARC_MPMBOX_H
+#define _SPARC_MPMBOX_H
+
+/* The prom allocates, for each CPU on the machine an unsigned
+ * byte in physical ram. You probe the device tree prom nodes
+ * for these values. The purpose of this byte is to be able to
+ * pass messages from one cpu to another.
+ */
+
+/* These are the main message types we have to look for in our
+ * Cpu mailboxes, based upon these values we decide what course
+ * of action to take.
+ */
+
+/* The CPU is executing code in the kernel. */
+#define MAILBOX_ISRUNNING 0xf0
+
+/* Another CPU called romvec->pv_exit(), you should call
+ * prom_stopcpu() when you see this in your mailbox.
+ */
+#define MAILBOX_EXIT 0xfb
+
+/* Another CPU called romvec->pv_enter(), you should call
+ * prom_cpuidle() when this is seen.
+ */
+#define MAILBOX_GOSPIN 0xfc
+
+/* Another CPU has hit a breakpoint either into kadb or the prom
+ * itself. Just like MAILBOX_GOSPIN, you should call prom_cpuidle()
+ * at this point.
+ */
+#define MAILBOX_BPT_SPIN 0xfd
+
+/* Oh geese, some other nitwit got a damn watchdog reset. The partys
+ * over so go call prom_stopcpu().
+ */
+#define MAILBOX_WDOG_STOP 0xfe
+
+#ifndef __ASSEMBLY__
+
+/* Handy macro's to determine a cpu's state. */
+
+/* Is the cpu still in Power On Self Test? */
+#define MBOX_POST_P(letter) ((letter) >= 0x00 && (letter) <= 0x7f)
+
+/* Is the cpu at the 'ok' prompt of the PROM? */
+#define MBOX_PROMPROMPT_P(letter) ((letter) >= 0x80 && (letter) <= 0x8f)
+
+/* Is the cpu spinning in the PROM? */
+#define MBOX_PROMSPIN_P(letter) ((letter) >= 0x90 && (letter) <= 0xef)
+
+/* Sanity check... This is junk mail, throw it out. */
+#define MBOX_BOGON_P(letter) ((letter) >= 0xf1 && (letter) <= 0xfa)
+
+/* Is the cpu actively running an application/kernel-code? */
+#define MBOX_RUNNING_P(letter) ((letter) == MAILBOX_ISRUNNING)
+
+#endif /* !(__ASSEMBLY__) */
+
+#endif /* !(_SPARC_MPMBOX_H) */
--- /dev/null
+/* mxcc.h: Definitions of the Viking MXCC registers
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#ifndef _SPARC_MXCC_H
+#define _SPARC_MXCC_H
+
+/* These registers are accessed through ASI 0x2. */
+#define MXCC_DATSTREAM 0x1C00000 /* Data stream register */
+#define MXCC_SRCSTREAM 0x1C00100 /* Source stream register */
+#define MXCC_DESSTREAM 0x1C00200 /* Destination stream register */
+#define MXCC_RMCOUNT 0x1C00300 /* Count of references and misses */
+#define MXCC_STEST 0x1C00804 /* Internal self-test */
+#define MXCC_CREG 0x1C00A04 /* Control register */
+#define MXCC_SREG 0x1C00B00 /* Status register */
+#define MXCC_RREG 0x1C00C04 /* Reset register */
+#define MXCC_EREG 0x1C00E00 /* Error code register */
+#define MXCC_PREG 0x1C00F04 /* Address port register */
+
+/* The MXCC Control Register:
+ *
+ * ----------------------------------------------------------------------
+ * | | RRC | RSV |PRE|MCE|PARE|ECE|RSV|
+ * ----------------------------------------------------------------------
+ * 31 10 9 8-6 5 4 3 2 1-0
+ *
+ * RRC: Controls what you read from MXCC_RMCOUNT reg.
+ * 0=Misses 1=References
+ * PRE: Prefetch enable
+ * MCE: Multiple Command Enable
+ * PARE: Parity enable
+ * ECE: External cache enable
+ */
+
+#define MXCC_CTL_RRC 0x00000200
+#define MXCC_CTL_PRE 0x00000020
+#define MXCC_CTL_MCE 0x00000010
+#define MXCC_CTL_PARE 0x00000008
+#define MXCC_CTL_ECE 0x00000004
+
+/* The MXCC Error Register:
+ *
+ * --------------------------------------------------------
+ * |ME| RSV|CE|PEW|PEE|ASE|EIV| MOPC|ECODE|PRIV|RSV|HPADDR|
+ * --------------------------------------------------------
+ * 31 30 29 28 27 26 25 24-15 14-7 6 5-3 2-0
+ *
+ * ME: Multiple Errors have occurred
+ * CE: Cache consistancy Error
+ * PEW: Parity Error during a Write operation
+ * PEE: Parity Error involving the External cache
+ * ASE: ASynchronous Error
+ * EIV: This register is toast
+ * MOPC: MXCC Operation Code for instance causing error
+ * ECODE: The Error CODE
+ * PRIV: A privileged mode error? 0=no 1=yes
+ * HPADDR: High PhysicalADDRess bits (35-32)
+ */
+
+#define MXCC_ERR_ME 0x80000000
+#define MXCC_ERR_CE 0x20000000
+#define MXCC_ERR_PEW 0x10000000
+#define MXCC_ERR_PEE 0x08000000
+#define MXCC_ERR_ASE 0x04000000
+#define MXCC_ERR_EIV 0x02000000
+#define MXCC_ERR_MOPC 0x01FF8000
+#define MXCC_ERR_ECODE 0x00007F80
+#define MXCC_ERR_PRIV 0x00000040
+#define MXCC_ERR_HPADDR 0x0000000f
+
+/* The MXCC Port register:
+ *
+ * -----------------------------------------------------
+ * | | MID | |
+ * -----------------------------------------------------
+ * 31 21 20-18 17 0
+ *
+ * MID: The moduleID of the cpu your read this from.
+ */
+
+#endif /* !(_SPARC_MXCC_H) */
#define __SPARC_OPENPROM_H
/* openprom.h: Prom structures and defines for access to the OPENBOOT
- prom routines and data areas.
-
- Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
-*/
+ * prom routines and data areas.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
/* In the v0 interface of the openboot prom we could traverse a nice
- little list structure to figure out where in vm-space the prom had
- mapped itself and how much space it was taking up. In the v2 prom
- interface we have to rely on 'magic' values. :-( Most of the machines
- I have checked on have the prom mapped here all the time though.
-*/
+ * little list structure to figure out where in vm-space the prom had
+ * mapped itself and how much space it was taking up. In the v2 prom
+ * interface we have to rely on 'magic' values. :-( Most of the machines
+ * I have checked on have the prom mapped here all the time though.
+ */
+
+#define KADB_DEBUGGER_BEGVM 0xffc00000 /* Where kern debugger is in virt-mem */
+
#define LINUX_OPPROM_BEGVM 0xffd00000
#define LINUX_OPPROM_ENDVM 0xfff00000
#define LINUX_OPPROM_MAGIC 0x10010407
+#ifndef __ASSEMBLY__
/* The device functions structure for the v0 prom. Nice and neat, open,
- close, read & write divvied up between net + block + char devices. We
- also have a seek routine only usable for block devices. The divide
- and conquer strategy of this struct becomes unnecessary for v2.
-
- V0 device names are limited to two characters, 'sd' for scsi-disk,
- 'le' for local-ethernet, etc. Note that it is technically possible
- to boot a kernel off of a tape drive and use the tape as the root
- partition! In order to do this you have to have 'magic' formatted
- tapes from Sun supposedly :-)
-*/
+ * close, read & write divvied up between net + block + char devices. We
+ * also have a seek routine only usable for block devices. The divide
+ * and conquer strategy of this struct becomes unnecessary for v2.
+ *
+ * V0 device names are limited to two characters, 'sd' for scsi-disk,
+ * 'le' for local-ethernet, etc. Note that it is technically possible
+ * to boot a kernel off of a tape drive and use the tape as the root
+ * partition! In order to do this you have to have 'magic' formatted
+ * tapes from Sun supposedly :-)
+ */
struct linux_dev_v0_funcs {
int (*v0_devopen)(char *device_str);
};
/* The OpenBoot Prom device operations for version-2 interfaces are both
- good and bad. They now allow you to address ANY device whatsoever
- that is in the machine via these funny "device paths". They look like
- this:
-
- "/sbus/esp@0,0xf004002c/sd@3,1"
-
- You can basically reference any device on the machine this way, and
- you pass this string to the v2 dev_ops. Producing these strings all
- the time can be a pain in the rear after a while. Why v2 has memory
- allocations in here are beyond me. Perhaps they figure that if you
- are going to use only the prom's device drivers then your memory
- management is either non-existent or pretty sad. :-)
-*/
+ * good and bad. They now allow you to address ANY device whatsoever
+ * that is in the machine via these funny "device paths". They look like
+ * this:
+ *
+ * "/sbus/esp@0,0xf004002c/sd@3,1"
+ *
+ * You can basically reference any device on the machine this way, and
+ * you pass this string to the v2 dev_ops. Producing these strings all
+ * the time can be a pain in the rear after a while. Why v2 has memory
+ * allocations in here are beyond me. Perhaps they figure that if you
+ * are going to use only the prom's device drivers then your memory
+ * management is either non-existent or pretty sad. :-)
+ */
struct linux_dev_v2_funcs {
- int (*v2_aieee)(int d); /* figure this out later... */
+ int (*v2_inst2pkg)(int d); /* Convert ihandle to phandle */
/* "dumb" prom memory management routines, probably
- only safe to use for mapping device address spaces...
- */
+ * only safe to use for mapping device address spaces...
+ */
char* (*v2_dumb_mem_alloc)(char* va, unsigned sz);
void (*v2_dumb_mem_free)(char* va, unsigned sz);
/* "dumb" mmap() munmap(), copy on write? what's that? */
- char* (*v2_dumb_mmap)(char* virta, int asi, unsigned prot, unsigned sz);
+ char* (*v2_dumb_mmap)(char* virta, int which_io, unsigned paddr, unsigned sz);
void (*v2_dumb_munmap)(char* virta, unsigned size);
/* Basic Operations, self-explanatory */
int (*v2_dev_write)(int d, char* buf, int nbytes);
void (*v2_dev_seek)(int d, int hi, int lo);
- /* huh? */
+ /* Never issued (multistage load support) */
void (*v2_wheee2)(void);
void (*v2_wheee3)(void);
};
/* Just like the device ops, they slightly screwed up the mem-list
- from v0 to v2. Probably easier on the prom-writer dude, sucks for
- us though. See above comment about prom-vm mapped address space
- magic numbers. :-(
-*/
+ * from v0 to v2. Probably easier on the prom-writer dude, sucks for
+ * us though. See above comment about prom-vm mapped address space
+ * magic numbers. :-(
+ */
struct linux_mlist_v0 {
struct linux_mlist_v0 *theres_more;
unsigned num_bytes;
};
-/* The linux_mlist_v0's are pointer by this structure. One list
- per description. This means one list for total physical memory,
- one for prom's address mapping, and one for physical mem left after
- the kernel is loaded.
+/* The linux_mlist_v0's are pointed to by this structure. One list
+ * per description. This means one list for total physical memory,
+ * one for prom's address mapping, and one for physical mem left after
+ * the kernel is loaded.
*/
+
struct linux_mem_v0 {
struct linux_mlist_v0 **v0_totphys; /* all of physical */
struct linux_mlist_v0 **v0_prommap; /* addresses map'd by prom */
};
/* Prom version-2 gives us the raw strings for boot arguments and
- boot device path. We also get the stdin and stdout file pseudo
- descriptors for use with the mungy v2 device functions.
-*/
+ * boot device path. We also get the stdin and stdout file pseudo
+ * descriptors for use with the mungy v2 device functions.
+ */
struct linux_bootargs_v2 {
char **bootpath; /* V2: Path to boot device */
char **bootargs; /* V2: Boot args */
};
/* This is the actual Prom Vector from which everything else is accessed
- via struct and function pointers, etc. The prom when it loads us into
- memory plops a pointer to this master structure in register %o0 before
- it jumps to the kernel start address. I will update this soon to cover
- the v3 semantics (cpu_start, cpu_stop and other SMP fun things). :-)
-*/
+ * via struct and function pointers, etc. The prom when it loads us into
+ * memory plops a pointer to this master structure in register %o0 before
+ * it jumps to the kernel start address. I will update this soon to cover
+ * the v3 semantics (cpu_start, cpu_stop and other SMP fun things). :-)
+ */
+
struct linux_romvec {
/* Version numbers. */
unsigned int pv_magic_cookie; /* Magic Mushroom... */
struct linux_bootargs_v2 pv_v2bootargs; /* V2: Boot args+std-in/out */
struct linux_dev_v2_funcs pv_v2devops; /* V2: device operations */
- int whatzthis[15]; /* huh? */
+ int filler[15];
/*
* The following is machine-dependent.
/* v3_cpustart() will start the cpu 'whichcpu' in mmu-context
* 'thiscontext' executing at address 'prog_counter'
- *
- * XXX Have to figure out what 'cancontext' means.
*/
- int (*v3_cpustart)(unsigned int whichcpu, int cancontext,
+ int (*v3_cpustart)(unsigned int whichcpu, int ctxtbl_ptr,
int thiscontext, char* prog_counter);
/* v3_cpustop() will cause cpu 'whichcpu' to stop executing
* are not in the openprom vectors but rather found by indirection from
* there. So the taste balances out.
*/
-struct linux_prom_addr {
- int oa_space; /* address space (may be relative) */
- unsigned int oa_base; /* address within space */
- unsigned int oa_size; /* extent (number of bytes) */
-};
struct linux_nodeops {
/*
char* (*no_nextprop)(int node, char* name);
};
+/* More fun PROM structures for device probing. */
+#define PROMREG_MAX 16
+#define PROMVADDR_MAX 16
+#define PROMINTR_MAX 15
+
+struct linux_prom_registers {
+ int which_io; /* is this in OBIO space? */
+ char *phys_addr; /* The physical address of this register */
+ int reg_size; /* How many bytes does this register take up? */
+};
+
+struct linux_prom_irqs {
+ int pri; /* IRQ priority */
+ int vector; /* This is foobar, what does it do? */
+};
+
+/* Element of the "ranges" vector */
+struct linux_prom_ranges {
+ unsigned int ot_child_space;
+ unsigned int ot_child_base; /* Bus feels this */
+ unsigned int ot_parent_space;
+ unsigned int ot_parent_base; /* CPU looks from here */
+ unsigned int or_size;
+};
+
+#endif /* !(__ASSEMBLY__) */
+
#endif /* !(__SPARC_OPENPROM_H) */
--- /dev/null
+/* oplib.h: Describes the interface and available routines in the
+ * Linux Prom library.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#ifndef __SPARC_OPLIB_H
+#define __SPARC_OPLIB_H
+
+#include <asm/openprom.h>
+
+/* The master romvec pointer... */
+extern struct linux_romvec *romvec;
+
+/* Enumeration to describe the prom major version we have detected. */
+enum prom_major_version {
+ PROM_V0, /* Origional sun4c V0 prom */
+ PROM_V2, /* sun4c and early sun4m V2 prom */
+ PROM_V3, /* sun4m and later, up to sun4d/sun4e machines V3 */
+ PROM_P1275, /* IEEE compliant ISA based Sun PROM, only sun4u */
+};
+
+extern enum prom_major_version prom_vers;
+/* Revision, and firmware revision. */
+extern unsigned int prom_rev, prom_prev;
+
+/* Root node of the prom device tree, this stays constant after
+ * initialization is complete.
+ */
+extern int prom_root_node;
+
+/* Pointer to prom structure containing the device tree traversal
+ * and usage utility functions. Only prom-lib should use these,
+ * users use the interface defined by the library only!
+ */
+extern struct linux_nodeops *prom_nodeops;
+
+/* The functions... */
+
+/* You must call prom_init() before using any of the library services,
+ * preferably as early as possible. Pass it the romvec pointer.
+ */
+extern int prom_init(struct linux_romvec *rom_ptr);
+
+/* Boot argument acquisition, returns the boot command line string. */
+extern char *prom_getbootargs(void);
+
+/* Device utilities. */
+
+/* Map and unmap devices in IO space at virtual addresses. Note that the
+ * virtual address you pass is a request and the prom may put your mappings
+ * somewhere else, so check your return value as that is where your new
+ * mappings really are!
+ *
+ * Another note, these are only available on V2 or higher proms!
+ */
+extern char *prom_mapio(char *virt_hint, int io_space, unsigned int phys_addr, unsigned int num_bytes);
+extern void prom_unmapio(char *virt_addr, unsigned int num_bytes);
+
+/* Device operations. */
+
+/* Open the device described by the passed string. Note, that the format
+ * of the string is different on V0 vs. V2->higher proms. The caller must
+ * know what he/she is doing! Returns the device descriptor, an int.
+ */
+extern int prom_devopen(char *device_string);
+
+/* Close a previously opened device described by the passed integer
+ * descriptor.
+ */
+extern int prom_devclose(int device_handle);
+
+/* Do a seek operation on the device described by the passed integer
+ * descriptor.
+ */
+extern void prom_seek(int device_handle, unsigned int seek_hival,
+ unsigned int seek_lowval);
+
+/* Machine memory configuration routine. */
+
+/* This function returns a V0 format memory descriptor table, it has three
+ * entries. One for the total amount of physical ram on the machine, one
+ * for the amount of physical ram available, and one describing the virtual
+ * areas which are allocated by the prom. So, in a sense the physical
+ * available is a calculation of the total physical minus the physcial mapped
+ * by the prom with virtual mappings.
+ *
+ * These lists are returned pre-sorted, this should make your life easier
+ * since the prom itself is way too lazy to do such nice things.
+ */
+extern struct linux_mem_v0 *prom_meminfo(void);
+
+/* Miscellaneous routines, don't really fit in any category per se. */
+
+/* Reboot the machine with the command line passed. */
+extern void prom_reboot(char *boot_command);
+
+/* Evaluate the forth string passed. */
+extern void prom_feval(char *forth_string);
+
+/* Enter the prom, with possibility of continuation with the 'go'
+ * command in newer proms.
+ */
+extern void prom_halt(void);
+
+/* Enter the prom, with no chance of continuation for the stand-alone
+ * which calls this.
+ */
+extern void prom_die(void);
+
+/* Set the PROM 'sync' callback function to the passed function pointer.
+ * When the user gives the 'sync' command at the prom prompt while the
+ * kernel is still active, the prom will call this routine.
+ *
+ * XXX The arguments are different on V0 vs. V2->higher proms, grrr! XXX
+ */
+typedef void (*sync_func_t)(void);
+extern void prom_setsync(sync_func_t func_ptr);
+
+/* Acquire the IDPROM of the root node in the prom device tree. This
+ * gets passed a buffer where you would like it stuffed. The return value
+ * is the format type of this idprom or 0xff on error.
+ */
+extern unsigned char prom_getidp(char *idp_buffer, int idpbuf_size);
+
+/* Get the prom major version. */
+extern int prom_version(void);
+
+/* Get the prom plugin revision. */
+extern int prom_getrev(void);
+
+/* Get the prom firmware revision. */
+extern int prom_getprev(void);
+
+/* Character operations to/from the console.... */
+
+/* Non-blocking get character from console. */
+extern int prom_nbgetchar(void);
+
+/* Non-blocking put character to console. */
+extern int prom_nbputchar(char character);
+
+/* Blocking get character from console. */
+extern char prom_getchar(void);
+
+/* Blocking put character to console. */
+extern void prom_putchar(char character);
+
+/* Prom's internal printf routine, don't use in kernel/boot code. */
+void prom_printf(char *fmt, ...);
+
+/* Multiprocessor operations... */
+
+/* Start the CPU with the given device tree node, context table, and context
+ * at the passed program counter.
+ */
+extern int prom_startcpu(int cpunode, struct linux_prom_registers *context_table,
+ int context, char *program_counter);
+
+/* Stop the CPU with the passed device tree node. */
+extern int prom_stopcpu(int cpunode);
+
+/* Idle the CPU with the passed device tree node. */
+extern int prom_idlecpu(int cpunode);
+
+/* Re-Start the CPU with the passed device tree node. */
+extern int prom_restartcpu(int cpunode);
+
+/* PROM memory allocation facilities... */
+
+/* Allocated at possibly the given virtual address a chunk of the
+ * indicated size.
+ */
+extern char *prom_alloc(char *virt_hint, unsigned int size);
+
+/* Free a previously allocated chunk. */
+extern void prom_free(char *virt_addr, unsigned int size);
+
+/* Sun4/sun4c specific memory-management startup hook. */
+
+/* Map the passed segment in the given context at the passed
+ * virtual address.
+ */
+extern void prom_putsegment(int context, unsigned long virt_addr,
+ int physical_segment);
+
+/* PROM device tree traversal functions... */
+
+/* Get the child node of the given node, or zero if no child exists. */
+extern int prom_getchild(int parent_node);
+
+/* Get the next sibling node of the given node, or zero if no further
+ * siblings exist.
+ */
+extern int prom_getsibling(int node);
+
+/* Get the length, at the passed node, of the given property type.
+ * Returns -1 on error (ie. no such property at this node).
+ */
+extern int prom_getproplen(int thisnode, char *property);
+
+/* Fetch the requested property using the given buffer. Returns
+ * the number of bytes the prom put into your buffer or -1 on error.
+ */
+extern int prom_getproperty(int thisnode, char *property,
+ char *prop_buffer, int propbuf_size);
+
+/* Acquire an integer property. */
+extern int prom_getint(int node, char *property);
+
+/* Acquire an integer property, with a default value. */
+extern int prom_getintdefault(int node, char *property, int defval);
+
+/* Acquire a boolean property, 0=FALSE 1=TRUE. */
+extern int prom_getbool(int node, char *prop);
+
+/* Acquire a string property, null string on error. */
+extern void prom_getstring(int node, char *prop, char *buf, int bufsize);
+
+/* Does the passed node have the given "name"? YES=1 NO=0 */
+extern int prom_nodematch(int thisnode, char *name);
+
+/* Search all siblings starting at the passed node for "name" matching
+ * the given string. Returns the node on success, zero on failure.
+ */
+extern int prom_searchsiblings(int node_start, char *name);
+
+/* Return the first property type, as a string, for the given node.
+ * Returns a null string on error.
+ */
+extern char *prom_firstprop(int node);
+
+/* Returns the next property after the passed property for the given
+ * node. Returns null string on failure.
+ */
+extern char *prom_nextprop(int node, char *prev_property);
+
+/* Set the indicated property at the given node with the passed value.
+ * Returns the number of bytes of your value that the prom took.
+ */
+extern int prom_setprop(int node, char *prop_name, char *prop_value,
+ int value_size);
+
+/* Dorking with Bus ranges... */
+
+/* Adjust reg values with the passed ranges. */
+extern void prom_adjust_regs(struct linux_prom_registers *regp, int nregs,
+ struct linux_prom_ranges *rangep, int nranges);
+
+/* Adjust child ranges with the passed parent ranges. */
+extern void prom_adjust_ranges(struct linux_prom_ranges *cranges, int ncranges,
+ struct linux_prom_ranges *pranges, int npranges);
+
+/* Apply promlib probed OBIO ranges to registers. */
+extern void prom_apply_obio_ranges(struct linux_prom_registers *obioregs, int nregs);
+
+/* Apply promlib probed SBUS ranges to registers. */
+extern void prom_apply_sbus_ranges(struct linux_prom_registers *sbusregs, int nregs);
+
+#endif /* !(__SPARC_OPLIB_H) */
/* page.h: Various defines and such for MMU operations on the Sparc for
- the Linux kernel.
-
- Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
-*/
+ * the Linux kernel.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
#ifndef _SPARC_PAGE_H
#define _SPARC_PAGE_H
#include <asm/asi.h> /* for get/set segmap/pte routines */
#include <asm/contregs.h> /* for switch_to_context */
+#include <asm/head.h> /* for KERNBASE */
#define PAGE_SHIFT 12 /* This is the virtual page... */
-
-#ifndef __ASSEMBLY__
-#define PAGE_SIZE (1UL << PAGE_SHIFT)
+#define PAGE_OFFSET KERNBASE
+#define PAGE_SIZE (1 << PAGE_SHIFT)
/* to mask away the intra-page address bits */
#define PAGE_MASK (~(PAGE_SIZE-1))
#ifdef __KERNEL__
+#ifndef __ASSEMBLY__
/* The following structure is used to hold the physical
- * memory configuration of the machine. This is filled
- * in probe_memory() and is later used by mem_init() to
- * set up mem_map[]. We statically allocate 14 of these
- * structs, this is arbitrary. The entry after the last
- * valid one has num_bytes==0.
+ * memory configuration of the machine. This is filled in
+ * probe_memory() and is later used by mem_init() to set up
+ * mem_map[]. We statically allocate SPARC_PHYS_BANKS of
+ * these structs, this is arbitrary. The entry after the
+ * last valid one has num_bytes==0.
*/
struct sparc_phys_banks {
unsigned long num_bytes;
};
+#define SPARC_PHYS_BANKS 32
+
+extern struct sparc_phys_banks sp_banks[SPARC_PHYS_BANKS];
+
#define CONFIG_STRICT_MM_TYPECHECKS
#ifdef CONFIG_STRICT_MM_TYPECHECKS
#endif
/* The current va context is global and known, so all that is needed to
- * do an invalidate is flush the VAC.
+ * do an invalidate is flush the VAC on a sun4c or call the ASI flushing
+ * routines on a SRMMU.
*/
-#define invalidate() flush_vac_context() /* how conveeeiiiiinnnient :> */
+extern void (*invalidate)(void);
/* to align the pointer to the (next) page boundary */
#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK)
-#define PAGE_OFFSET 0
-#define MAP_NR(addr) (((unsigned long)(addr)) >> PAGE_SHIFT)
+/* We now put the free page pool mapped contiguously in high memory above
+ * the kernel.
+ */
+#define MAP_NR(addr) ((((unsigned long)addr) - PAGE_OFFSET) >> PAGE_SHIFT)
#define MAP_PAGE_RESERVED (1<<15)
#define PTE_RESV 0x00f80000 /* reserved bits */
#define PTE_PHYPG 0x0007ffff /* phys pg number, sun4c only uses 16bits */
+/* SRMMU defines */
+/* The fields in an srmmu virtual address when it gets translated.
+ *
+ * -------------------------------------------------------------
+ * | INDEX 1 | INDEX 2 | INDEX 3 | PAGE OFFSET |
+ * -------------------------------------------------------------
+ * 31 24 23 18 17 12 11 0
+ */
+#define SRMMU_IDX1_SHIFT 24
+#define SRMMU_IDX1_MASK 0xff000000
+#define SRMMU_IDX2_SHIFT 18
+#define SRMMU_IDX2_MASK 0x00fc0000
+#define SRMMU_IDX3_SHIFT 12
+#define SRMMU_IDX3_MASK 0x0003f000
+
+#define SRMMU_PGOFFSET_MASK 0x00000fff
+/* The page table sizes for the various levels in bytes. */
+#define SRMMU_LV1_PTSIZE 1024
+#define SRMMU_LV2_PTSIZE 256
+#define SRMMU_LV3_PTSIZE 256
+
+/* Definition of the values in the ET field of PTD's and PTE's */
+#define SRMMU_ET_INVALID 0x0
+#define SRMMU_ET_PTD 0x1
+#define SRMMU_ET_PTE 0x2
+#define SRMMU_ET_RESV 0x3
+#define SRMMU_ET_PTDBAD 0x3 /* Upward compatability my butt. */
+
+/* Page table directory bits.
+ *
+ * ----------------
+ * | PTP | ET |
+ * ----------------
+ * 31 2 1 0
+ *
+ * PTP: The physical page table pointer. This value appears on
+ * bits 35->6 on the physical address bus during translation.
+ *
+ * ET: Entry type field. Must be 1 for a PTD.
+ */
+
+#define SRMMU_PTD_PTP_SHIFT 0x2
+#define SRMMU_PTD_PTP_MASK 0xfffffffc
+#define SRMMU_PTD_PTP_PADDR_SHIFT 0x4
+#define SRMMU_PTD_ET_SHIFT 0x0
+#define SRMMU_PTD_ET_MASK 0x00000003
+
+/* Page table entry bits.
+ *
+ * -------------------------------------------------
+ * | Physical Page Number | C | M | R | ACC | ET |
+ * -------------------------------------------------
+ * 31 8 7 6 5 4 2 1 0
+ *
+ * PPN: Physical page number, high order 24 bits of the 36-bit
+ * physical address, thus is you mask off all the non
+ * PPN bits you have the physical address of your page.
+ * No shifting necessary.
+ *
+ * C: Whether the page is cacheable in the mmu TLB or not. If
+ * not set the CPU cannot cache values to these addresses. For
+ * IO space translations this bit should be clear.
+ *
+ * M: Modified. This tells whether the page has been written to
+ * since the bit was last cleared. NOTE: this does not include
+ * accesses via the ASI physical page pass through since that does
+ * not use the MMU.
+ *
+ * R: References. This tells whether the page has been referenced
+ * in any way shape or form since the last clearing of the bit.
+ * NOTE: this does not include accesses via the ASI physical page
+ * pass through since that does not use the MMU.
+ *
+ * ACC: Access permissions for this page. This is further explained below
+ * with appropriate macros.
+ */
+
+#define SRMMU_PTE_PPN_SHIFT 0x8
+#define SRMMU_PTE_PPN_MASK 0xffffff00
+#define SRMMU_PTE_PPN_PADDR_SHIFT 0x4
+#define SRMMU_PTE_C_SHIFT 0x7
+#define SRMMU_PTE_C_MASK 0x00000080
+#define SRMMU_PTE_M_SHIFT 0x6
+#define SRMMU_PTE_M_MASK 0x00000040
+#define SRMMU_PTE_R_SHIFT 0x5
+#define SRMMU_PTE_R_MASK 0x00000020
+#define SRMMU_PTE_ACC_SHIFT 0x2
+#define SRMMU_PTE_ACC_MASK 0x0000001c
+#define SRMMU_PTE_ET_SHIFT 0x0
+#define SRMMU_PTE_ET_MASK 0x00000003
+
+/* SRMMU pte access bits.
+ *
+ * BIT USER ACCESS SUPERVISOR ACCESS
+ * --- -------------- -----------------
+ * 0x0 read only read only
+ * 0x1 read&write read&write
+ * 0x2 read&execute read&execute
+ * 0x3 read&write&execute read&write&execute
+ * 0x4 execute only execute only
+ * 0x5 read only read&write
+ * 0x6 ACCESS DENIED read&execute
+ * 0x7 ACCESS DENIED read&write&execute
+ *
+ * All these values are shifted left two bits.
+ */
+
+#define SRMMU_ACC_US_RDONLY 0x00
+#define SRMMU_ACC_US_RDWR 0x04
+#define SRMMU_ACC_US_RDEXEC 0x08
+#define SRMMU_ACC_US_RDWREXEC 0x0c
+#define SRMMU_ACC_US_EXECONLY 0x10
+#define SRMMU_ACC_U_RDONLY 0x14
+#define SRMMU_ACC_S_RDWR 0x14
+#define SRMMU_ACC_U_ACCDENIED 0x18
+#define SRMMU_ACC_S_RDEXEC 0x18
+#define SRMMU_ACC_U_ACCDENIED2 0x1c
+#define SRMMU_ACC_S_RDWREXEC 0x1c
+
+#ifndef __ASSEMBLY__
+
+/* SUN4C pte, segmap, and context manipulation */
extern __inline__ unsigned long get_segmap(unsigned long addr)
{
register unsigned long entry;
- __asm__ __volatile__("lduba [%1] 0x3, %0" :
+ __asm__ __volatile__("lduba [%1] %2, %0" :
"=r" (entry) :
- "r" (addr));
+ "r" (addr), "i" (ASI_SEGMAP));
- return (entry&0x7f);
+ return (entry&0xff);
}
extern __inline__ void put_segmap(unsigned long addr, unsigned long entry)
{
- __asm__ __volatile__("stba %1, [%0] 0x3" : : "r" (addr), "r" (entry&0x7f));
+ __asm__ __volatile__("stba %1, [%0] %2" : : "r" (addr), "r" (entry&0xff),
+ "i" (ASI_SEGMAP));
return;
}
{
register unsigned long entry;
- __asm__ __volatile__("lda [%1] 0x4, %0" :
+ __asm__ __volatile__("lda [%1] %2, %0" :
"=r" (entry) :
- "r" (addr));
+ "r" (addr), "i" (ASI_PTE));
return entry;
}
extern __inline__ void put_pte(unsigned long addr, unsigned long entry)
{
- __asm__ __volatile__("sta %1, [%0] 0x4" : :
+ __asm__ __volatile__("sta %1, [%0] %2" : :
"r" (addr),
- "r" (entry));
+ "r" (entry), "i" (ASI_PTE));
return;
}
-extern __inline__ void switch_to_context(int context)
-{
- __asm__ __volatile__("stba %0, [%1] 0x2" : :
- "r" (context),
- "r" (0x30000000));
-
- return;
-}
+extern void (*switch_to_context)(int);
extern __inline__ int get_context(void)
{
register int ctx;
- __asm__ __volatile__("lduba [%1] 0x2, %0" :
+ __asm__ __volatile__("lduba [%1] %2, %0" :
"=r" (ctx) :
- "r" (0x30000000));
+ "r" (AC_CONTEXT), "i" (ASI_CONTROL));
return ctx;
}
typedef unsigned short mem_map_t;
+#endif /* __ASSEMBLY__ */
+
#endif /* __KERNEL__ */
#endif /* _SPARC_PAGE_H */
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
*/
-/* PMD_SHIFT determines the size of the area a second-level page table can map */
-#define PMD_SHIFT 18
-#define PMD_SIZE (1UL << PMD_SHIFT)
-#define PMD_MASK (~(PMD_SIZE-1))
-
-/* PGDIR_SHIFT determines what a third-level page table entry can map */
-#define PGDIR_SHIFT 18
-#define PGDIR_SIZE (1UL << PGDIR_SHIFT)
-#define PGDIR_MASK (~(PGDIR_SIZE-1))
-#define PGDIR_ALIGN(addr) (((addr)+PGDIR_SIZE-1)&PGDIR_MASK)
+#include <linux/mm.h>
+#include <asm/asi.h>
+#include <asm/pgtsun4c.h>
+#include <asm/pgtsrmmu.h>
+
+extern void load_mmu(void);
+
+extern unsigned int pmd_shift;
+extern unsigned int pmd_size;
+extern unsigned int pmd_mask;
+extern unsigned int (*pmd_align)(unsigned int);
+
+extern unsigned int pgdir_shift;
+extern unsigned int pgdir_size;
+extern unsigned int pgdir_mask;
+extern unsigned int (*pgdir_align)(unsigned int);
+
+extern unsigned int ptrs_per_pte;
+extern unsigned int ptrs_per_pmd;
+extern unsigned int ptrs_per_pgd;
+
+extern unsigned int ptrs_per_page;
+
+extern unsigned long (*(vmalloc_start))(void);
+
+#define VMALLOC_VMADDR(x) ((unsigned long)(x))
+#define VMALLOC_START vmalloc_start()
+
+extern pgprot_t page_none;
+extern pgprot_t page_shared;
+extern pgprot_t page_copy;
+extern pgprot_t page_readonly;
+extern pgprot_t page_kernel;
+extern pgprot_t page_invalid;
+
+#define PMD_SHIFT (pmd_shift)
+#define PMD_SIZE (pmd_size)
+#define PMD_MASK (pmd_mask)
+#define PMD_ALIGN (pmd_align)
+#define PGDIR_SHIFT (pgdir_shift)
+#define PGDIR_SIZE (pgdir_size)
+#define PGDIR_MASK (pgdir_mask)
+#define PGDIR_ALIGN (pgdir_align)
+#define PTRS_PER_PTE (ptrs_per_pte)
+#define PTRS_PER_PMD (ptrs_per_pmd)
+#define PTRS_PER_PGD (ptrs_per_pgd)
+
+#define PAGE_NONE (page_none)
+#define PAGE_SHARED (page_shared)
+#define PAGE_COPY (page_copy)
+#define PAGE_READONLY (page_readonly)
+#define PAGE_KERNEL (page_kernel)
+#define PAGE_INVALID (page_invalid)
+
+/* Top-level page directory */
+extern pgd_t swapper_pg_dir[1024];
-/*
- * Just following the i386 lead, because it works on the Sparc sun4c
- * machines. Two-level, therefore there is no real PMD.
+/* Page table for 0-4MB for everybody, on the Sparc this
+ * holds the same as on the i386.
*/
+extern unsigned long pg0[1024];
-#define PTRS_PER_PTE 1024
-#define PTRS_PER_PMD 1
-#define PTRS_PER_PGD 1024
+extern unsigned long ptr_in_current_pgd;
/* the no. of pointers that fit on a page: this will go away */
#define PTRS_PER_PAGE (PAGE_SIZE/sizeof(void*))
-/* Just any arbitrary offset to the start of the vmalloc VM area: the
- * current 8MB value just means that there will be a 8MB "hole" after the
- * physical memory until the kernel virtual memory starts. That means that
- * any out-of-bounds memory accesses will hopefully be caught.
- * The vmalloc() routines leaves a hole of 4kB between each vmalloced
- * area for the same reason. ;)
- */
-#define VMALLOC_OFFSET (8*1024*1024)
-#define VMALLOC_START ((high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))
-#define VMALLOC_VMADDR(x) (TASK_SIZE + (unsigned long)(x))
-
-/*
- * Sparc page table fields.
- */
-
-#define _PAGE_VALID 0x80000000 /* valid page */
-#define _PAGE_WRITE 0x40000000 /* can be written to */
-#define _PAGE_PRIV 0x20000000 /* bit to signify privileged page */
-#define _PAGE_NOCACHE 0x10000000 /* non-cacheable page */
-#define _PAGE_REF 0x02000000 /* Page has been accessed/referenced */
-#define _PAGE_DIRTY 0x01000000 /* Page has been modified, is dirty */
-#define _PAGE_COW 0x00800000 /* COW page, hardware ignores this bit (untested) */
-
-
-/* Sparc sun4c mmu has only a writable bit. Thus if a page is valid it can be
- * read in a load, and executed as code automatically. Although, the memory fault
- * hardware does make a distinction between date-read faults and insn-read faults
- * which is determined by which trap happened plus magic sync/async fault register
- * values which must be checked in the actual fault handler.
- */
-
-/* We want the swapper not to swap out page tables, thus dirty and writable
- * so that the kernel can change the entries as needed. Also valid for
- * obvious reasons.
- */
-#define _PAGE_TABLE (_PAGE_VALID | _PAGE_WRITE | _PAGE_DIRTY)
-#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_REF | _PAGE_DIRTY)
-
-#define PAGE_NONE __pgprot(_PAGE_VALID | _PAGE_REF)
-#define PAGE_SHARED __pgprot(_PAGE_VALID | _PAGE_WRITE | _PAGE_REF)
-#define PAGE_COPY __pgprot(_PAGE_VALID | _PAGE_REF | _PAGE_COW)
-#define PAGE_READONLY __pgprot(_PAGE_VALID | _PAGE_REF)
-#define PAGE_KERNEL __pgprot(_PAGE_VALID | _PAGE_WRITE | _PAGE_NOCACHE | _PAGE_REF | _PAGE_PRIV)
-#define PAGE_INVALID __pgprot(_PAGE_PRIV)
-
-#define _PAGE_NORMAL(x) __pgprot(_PAGE_VALID | _PAGE_REF | (x))
-
/* I define these like the i386 does because the check for text or data fault
* is done at trap time by the low level handler. Maybe I can set these bits
* then once determined. I leave them like this for now though.
#define __S110 PAGE_SHARED
#define __S111 PAGE_SHARED
+/* Contexts on the Sparc. */
+#define MAX_CTXS 256
+#define NO_CTX 0xffff /* In tss.context means task has no context currently */
+extern struct task_struct * ctx_tasks[MAX_CTXS];
+extern int ctx_tasks_last_frd;
-extern unsigned long pg0[1024];
+extern int num_contexts;
+
+/* This routine allocates a new context. And 'p' must not be 'current'! */
+extern inline int alloc_mmu_ctx(struct task_struct *p)
+{
+ int i;
+
+ for(i=0; i<num_contexts; i++)
+ if(ctx_tasks[i] == NULL) break;
+
+ if(i<num_contexts) {
+ p->tss.context = i;
+ ctx_tasks[i] = p;
+ return i;
+ }
+
+ /* Have to free one up */
+ ctx_tasks_last_frd++;
+ if(ctx_tasks_last_frd >= num_contexts) ctx_tasks_last_frd=0;
+ /* Right here is where we invalidate the user mappings that were
+ * present. TODO
+ */
+ ctx_tasks[ctx_tasks_last_frd]->tss.context = NO_CTX;
+ ctx_tasks[ctx_tasks_last_frd] = p;
+ p->tss.context = ctx_tasks_last_frd;
+ return ctx_tasks_last_frd;
+}
/*
* BAD_PAGETABLE is used when we need a bogus page-table, while
#define ZERO_PAGE __zero_page()
/* number of bits that fit into a memory pointer */
-#define BITS_PER_PTR (8*sizeof(unsigned long)) /* better check this stuff */
+#define BITS_PER_PTR (8*sizeof(unsigned long))
/* to align the pointer to a pointer address */
#define PTR_MASK (~(sizeof(void*)-1))
#define SIZEOF_PTR_LOG2 2
+extern unsigned long (*pte_page)(pte_t);
+extern unsigned long (*pmd_page)(pmd_t);
+extern unsigned long (*pgd_page)(pgd_t);
/* to set the page-dir
*
* Therefore there is no global idea of 'the' page directory, although we
* make a virtual one in kernel memory so that we can keep the stats on
* all the pages since not all can be loaded at once in the mmu.
+ *
+ * Actually on the SRMMU things do work exactly like the i386, the
+ * page tables live in real physical ram, no funky TLB buisness. But
+ * we have to do lots of flushing. And we have to update the root level
+ * page table pointer for this process if it has a context.
*/
-#define SET_PAGE_DIR(tsk,pgdir)
+extern void (*sparc_update_rootmmu_dir)(struct task_struct *, pgd_t *pgdir);
+#define SET_PAGE_DIR(tsk,pgdir) \
+do { sparc_update_rootmmu_dir(tsk, pgdir); } while (0)
+
/* to find an entry in a page-table */
#define PAGE_PTR(address) \
((unsigned long)(address)>>(PAGE_SHIFT-SIZEOF_PTR_LOG2)&PTR_MASK&~PAGE_MASK)
extern unsigned long high_memory;
-extern inline int pte_none(pte_t pte) { return !pte_val(pte); }
-extern inline int pte_present(pte_t pte) { return pte_val(pte) & _PAGE_VALID; }
-extern inline int pte_inuse(pte_t *ptep) { return mem_map[MAP_NR(ptep)] > 1; }
-extern inline void pte_clear(pte_t *ptep) { pte_val(*ptep) = 0; }
-extern inline void pte_reuse(pte_t *ptep)
-{
- if(!(mem_map[MAP_NR(ptep)] & MAP_PAGE_RESERVED))
- mem_map[MAP_NR(ptep)]++;
-}
-
-extern inline int pmd_none(pmd_t pmd) { return !pmd_val(pmd); }
-extern inline int pmd_bad(pmd_t pmd) { return (pmd_val(pmd) & ~PAGE_MASK) != _PAGE_TABLE || pmd_val(pmd) > high_memory; }
-extern inline int pmd_present(pmd_t pmd) { return pmd_val(pmd) & _PAGE_VALID; }
-extern inline int pmd_inuse(pmd_t *pmdp) { return 0; }
-extern inline void pmd_clear(pmd_t *pmdp) { pmd_val(*pmdp) = 0; }
-extern inline void pmd_reuse(pmd_t * pmdp) { }
-
-extern inline int pgd_none(pgd_t pgd) { return !pgd_val(pgd); }
-extern inline int pgd_bad(pgd_t pgd) { return (pgd_val(pgd) & ~PAGE_MASK) != _PAGE_TABLE || pgd_val(pgd) > high_memory; }
-extern inline int pgd_present(pgd_t pgd) { return pgd_val(pgd) & _PAGE_VALID; }
-extern inline int pgd_inuse(pgd_t *pgdp) { return mem_map[MAP_NR(pgdp)] > 1; }
-extern inline void pgd_clear(pgd_t * pgdp) { pgd_val(*pgdp) = 0; }
-extern inline void pgd_reuse(pgd_t *pgdp)
-{
- if (!(mem_map[MAP_NR(pgdp)] & MAP_PAGE_RESERVED))
- mem_map[MAP_NR(pgdp)]++;
-}
+extern int (*pte_none)(pte_t);
+extern int (*pte_present)(pte_t);
+extern int (*pte_inuse)(pte_t *);
+extern void (*pte_clear)(pte_t *);
+extern void (*pte_reuse)(pte_t *);
+
+extern int (*pmd_none)(pmd_t);
+extern int (*pmd_bad)(pmd_t);
+extern int (*pmd_present)(pmd_t);
+extern int (*pmd_inuse)(pmd_t *);
+extern void (*pmd_clear)(pmd_t *);
+extern void (*pmd_reuse)(pmd_t *);
+
+extern int (*pgd_none)(pgd_t);
+extern int (*pgd_bad)(pgd_t);
+extern int (*pgd_present)(pgd_t);
+extern int (*pgd_inuse)(pgd_t *);
+extern void (*pgd_clear)(pgd_t *);
+extern void (*pgd_reuse)(pgd_t *);
/*
* The following only work if pte_present() is true.
* Undefined behaviour if not..
*/
-extern inline int pte_read(pte_t pte) { return pte_val(pte) & _PAGE_VALID; }
-extern inline int pte_write(pte_t pte) { return pte_val(pte) & _PAGE_WRITE; }
-extern inline int pte_exec(pte_t pte) { return pte_val(pte) & _PAGE_VALID; }
-extern inline int pte_dirty(pte_t pte) { return pte_val(pte) & _PAGE_REF; }
-extern inline int pte_young(pte_t pte) { return pte_val(pte) & _PAGE_REF; }
-extern inline int pte_cow(pte_t pte) { return pte_val(pte) & _PAGE_COW; }
-
-extern inline pte_t pte_wrprotect(pte_t pte) { pte_val(pte) &= ~_PAGE_WRITE; return pte; }
-extern inline pte_t pte_rdprotect(pte_t pte) { pte_val(pte) &= ~_PAGE_VALID; return pte; }
-extern inline pte_t pte_exprotect(pte_t pte) { pte_val(pte) &= ~_PAGE_VALID; return pte; }
-extern inline pte_t pte_mkclean(pte_t pte) { pte_val(pte) &= ~_PAGE_DIRTY; return pte; }
-extern inline pte_t pte_mkold(pte_t pte) { pte_val(pte) &= ~_PAGE_REF; return pte; }
-extern inline pte_t pte_uncow(pte_t pte) { pte_val(pte) &= ~_PAGE_COW; return pte; }
-extern inline pte_t pte_mkwrite(pte_t pte) { pte_val(pte) |= _PAGE_WRITE; return pte; }
-extern inline pte_t pte_mkread(pte_t pte) { pte_val(pte) |= _PAGE_VALID; return pte; }
-extern inline pte_t pte_mkexec(pte_t pte) { pte_val(pte) |= _PAGE_VALID; return pte; }
-extern inline pte_t pte_mkdirty(pte_t pte) { pte_val(pte) |= _PAGE_DIRTY; return pte; }
-extern inline pte_t pte_mkyoung(pte_t pte) { pte_val(pte) |= _PAGE_REF; return pte; }
-extern inline pte_t pte_mkcow(pte_t pte) { pte_val(pte) |= _PAGE_COW; return pte; }
+extern int (*pte_read)(pte_t);
+extern int (*pte_write)(pte_t);
+extern int (*pte_exec)(pte_t);
+extern int (*pte_dirty)(pte_t);
+extern int (*pte_young)(pte_t);
+extern int (*pte_cow)(pte_t);
+
+extern pte_t (*pte_wrprotect)(pte_t);
+extern pte_t (*pte_rdprotect)(pte_t);
+extern pte_t (*pte_exprotect)(pte_t);
+extern pte_t (*pte_mkclean)(pte_t);
+extern pte_t (*pte_mkold)(pte_t);
+extern pte_t (*pte_uncow)(pte_t);
+extern pte_t (*pte_mkwrite)(pte_t);
+extern pte_t (*pte_mkread)(pte_t);
+extern pte_t (*pte_mkexec)(pte_t);
+extern pte_t (*pte_mkdirty)(pte_t);
+extern pte_t (*pte_mkyoung)(pte_t);
+extern pte_t (*pte_mkcow)(pte_t);
/*
* Conversion functions: convert a page and protection to a page entry,
* and a page entry and page directory to the page they refer to.
*/
-extern inline pte_t mk_pte(unsigned long page, pgprot_t pgprot)
-{ pte_t pte; pte_val(pte) = page | pgprot_val(pgprot); return pte; }
-
-extern inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
-{ pte_val(pte) = (pte_val(pte) & _PAGE_CHG_MASK) | pgprot_val(newprot); return pte; }
-
-extern inline unsigned long pte_page(pte_t pte) { return pte_val(pte) & PAGE_MASK; }
-
-extern inline unsigned long pmd_page(pmd_t pmd) { return pmd_val(pmd) & PAGE_MASK; }
+extern pte_t (*mk_pte)(unsigned long, pgprot_t);
-extern inline unsigned long pgd_page(pgd_t pgd) { return pgd_val(pgd) & PAGE_MASK; }
+extern void (*pgd_set)(pgd_t *, pte_t *);
-extern inline void pgd_set(pgd_t * pgdp, pte_t * ptep)
-{ pgd_val(*pgdp) = _PAGE_TABLE | (unsigned long) ptep; }
+extern pte_t (*pte_modify)(pte_t, pgprot_t);
/* to find an entry in a page-table-directory */
-#define PAGE_DIR_OFFSET(tsk,address) \
-((((unsigned long)(address)) >> 22) + (pgd_t *) (tsk)->tss.cr3)
-
-/* to find an entry in a page-table-directory */
-extern inline pgd_t * pgd_offset(struct task_struct * tsk, unsigned long address)
-{
- return (pgd_t *) tsk->tss.cr3 + (address >> PGDIR_SHIFT);
-}
+extern pgd_t * (*pgd_offset)(struct task_struct *, unsigned long);
/* Find an entry in the second-level page table.. */
-extern inline pmd_t * pmd_offset(pgd_t * dir, unsigned long address)
-{
- return (pmd_t *) dir;
-}
+extern pmd_t * (*pmd_offset)(pgd_t *, unsigned long);
/* Find an entry in the third-level page table.. */
-extern inline pte_t * pte_offset(pmd_t * dir, unsigned long address)
-{
- return (pte_t *) pmd_page(*dir) + ((address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1));
-}
-
+extern pte_t * (*pte_offset)(pmd_t *, unsigned long);
/*
* Allocate and free page tables. The xxx_kernel() versions are
* used to allocate a kernel page table - this turns on ASN bits
* if any, and marks the page tables reserved.
*/
-extern inline void pte_free_kernel(pte_t * pte)
-{
- mem_map[MAP_NR(pte)] = 1;
- free_page((unsigned long) pte);
-}
+extern void (*pte_free_kernel)(pte_t *);
-extern inline pte_t * pte_alloc_kernel(pmd_t * pmd, unsigned long address)
-{
- address = (address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1);
- if (pmd_none(*pmd)) {
- pte_t * page = (pte_t *) get_free_page(GFP_KERNEL);
- if (pmd_none(*pmd)) {
- if (page) {
- pmd_val(*pmd) = _PAGE_TABLE | (unsigned long) page;
- mem_map[MAP_NR(page)] = MAP_PAGE_RESERVED;
- return page + address;
- }
- pmd_val(*pmd) = _PAGE_TABLE | (unsigned long) BAD_PAGETABLE;
- return NULL;
- }
- free_page((unsigned long) page);
- }
- if (pmd_bad(*pmd)) {
- printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd));
- pmd_val(*pmd) = _PAGE_TABLE | (unsigned long) BAD_PAGETABLE;
- return NULL;
- }
- return (pte_t *) pmd_page(*pmd) + address;
-}
+extern pte_t * (*pte_alloc_kernel)(pmd_t *, unsigned long);
/*
* allocating and freeing a pmd is trivial: the 1-entry pmd is
* inside the pgd, so has no extra memory associated with it.
*/
-extern inline void pmd_free_kernel(pmd_t * pmd)
-{
-}
+extern void (*pmd_free_kernel)(pmd_t *);
-extern inline pmd_t * pmd_alloc_kernel(pgd_t * pgd, unsigned long address)
-{
- return (pmd_t *) pgd;
-}
+extern pmd_t * (*pmd_alloc_kernel)(pgd_t *, unsigned long);
-extern inline void pte_free(pte_t * pte)
-{
- free_page((unsigned long) pte);
-}
+extern void (*pte_free)(pte_t *);
-extern inline pte_t * pte_alloc(pmd_t * pmd, unsigned long address)
-{
- address = (address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1);
- if (pmd_none(*pmd)) {
- pte_t * page = (pte_t *) get_free_page(GFP_KERNEL);
- if (pmd_none(*pmd)) {
- if (page) {
- pmd_val(*pmd) = _PAGE_TABLE | (unsigned long) page;
- return page + address;
- }
- pmd_val(*pmd) = _PAGE_TABLE | (unsigned long) BAD_PAGETABLE;
- return NULL;
- }
- free_page((unsigned long) page);
- }
- if (pmd_bad(*pmd)) {
- printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd));
- pmd_val(*pmd) = _PAGE_TABLE | (unsigned long) BAD_PAGETABLE;
- return NULL;
- }
- return (pte_t *) pmd_page(*pmd) + address;
-}
+extern pte_t * (*pte_alloc)(pmd_t *, unsigned long);
/*
* allocating and freeing a pmd is trivial: the 1-entry pmd is
* inside the pgd, so has no extra memory associated with it.
*/
-extern inline void pmd_free(pmd_t * pmd)
-{
-}
+extern void (*pmd_free)(pmd_t *);
+
+extern pmd_t * (*pmd_alloc)(pgd_t *, unsigned long);
+
+extern void (*pgd_free)(pgd_t *);
+
+/* A page directory on the sun4c needs 16k, thus we request an order of
+ * two.
+ *
+ * I need 16k for a sun4c page table, so I use kmalloc since kmalloc_init()
+ * is called before pgd_alloc ever is (I think).
+ */
+
+extern pgd_t * (*pgd_alloc)(void);
-extern inline pmd_t * pmd_alloc(pgd_t * pgd, unsigned long address)
+extern int invalid_segment;
+
+/* Sun4c specific routines. They can stay inlined. */
+extern inline int alloc_sun4c_pseg(void)
{
- return (pmd_t *) pgd;
+ int oldseg, i;
+ /* First see if any are free already */
+ for(i=0; i<PSEG_ENTRIES; i++)
+ if(phys_seg_map[i]==PSEG_AVL) return i;
+
+ /* Uh-oh, gotta unallocate a TLB pseg */
+ oldseg=0;
+ for(i=0; i<PSEG_ENTRIES; i++) {
+ /* Can not touch PSEG_KERNEL and PSEG_RSV segmaps */
+ if(phys_seg_map[i]!=PSEG_USED) continue;
+ /* Ok, take a look at it's lifespan */
+ oldseg = (phys_seg_life[i]>oldseg) ? phys_seg_life[i] : oldseg;
+ }
+ phys_seg_life[oldseg]=PSEG_BORN;
+ return oldseg;
}
-extern inline void pgd_free(pgd_t *pgd)
+/* Age all psegs except pseg_skip */
+extern inline void age_sun4c_psegs(int pseg_skip)
{
- free_page((unsigned long) pgd);
+ int i;
+
+ for(i=0; i<pseg_skip; i++) phys_seg_life[i]++;
+ i++;
+ while(i<PSEG_ENTRIES) phys_seg_life[i++]++;
+ return;
}
-extern inline pgd_t *pgd_alloc(void)
+
+/*
+ * This is only ever called when the sun4c page fault routines run
+ * so we can keep this here as the srmmu code will never get to it.
+ */
+extern inline void update_mmu_cache(struct vm_area_struct * vma,
+ unsigned long address, pte_t pte)
{
- return (pgd_t *) get_free_page(GFP_KERNEL);
-}
+ unsigned long clr_addr;
+ int segmap;
+
+ segmap = (int) get_segmap(address & SUN4C_REAL_PGDIR_MASK);
+ if(segmap == invalid_segment) {
+ segmap = alloc_sun4c_pseg();
+ put_segmap((address & SUN4C_REAL_PGDIR_MASK), segmap);
+ phys_seg_map[segmap] = PSEG_USED;
+
+ /* We got a segmap, clear all the pte's in it. */
+ for(clr_addr=(address&SUN4C_REAL_PGDIR_MASK); clr_addr<((address&SUN4C_REAL_PGDIR_MASK) + SUN4C_REAL_PGDIR_SIZE);
+ clr_addr+=PAGE_SIZE)
+ put_pte(clr_addr, 0);
+ }
+
+ /* Do aging */
+ age_sun4c_psegs(segmap);
+ put_pte((address & PAGE_MASK), pte_val(pte));
+ return;
-extern pgd_t swapper_pg_dir[1024];
+}
#endif /* !(_SPARC_PGTABLE_H) */
--- /dev/null
+/* pgtsfmmu.h: Spitfire V9 MMU support goes here.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#ifndef _SPARC_PGTSFMMU_H
+#define _SPARC_PGTSFMMU_H
+
+/* Spitfire is four-level.... I think... It also has a seperate TLB for
+ * data and instruction mappings.
+ */
+#define SFMMU_PMD_SHIFT 16
+#define SFMMU_PMD_SIZE (1UL << SFMMU_PMD_SHIFT)
+#define SFMMU_PMD_MASK (~(SFMMU_PMD_SIZE-1))
+#define SFMMU_PMD_ALIGN(addr) (((addr)+SFMMU_PMD_SIZE-1)&SFMMU_PMD_MASK)
+
+#define SFMMU_PGDIR_SHIFT 19
+#define SFMMU_PGDIR_SIZE (1UL << SFMMU_PGDIR_SHIFT)
+#define SFMMU_PGDIR_MASK (~(SFMMU_PGDIR_SIZE-1))
+#define SFMMU_PGDIR_ALIGN(addr) (((addr)+SFMMU_PGDIR_SIZE-1)&SFMMU_PGDIR_MASK)
+
+#define SFMMU_PGMAP_SHIFT 22
+#define SFMMU_PGDIR_SIZE (1UL << SFMMU_PGDIR_SHIFT)
+#define SFMMU_PGDIR_MASK (~(SFMMU_PGDIR_SIZE-1))
+#define SFMMU_PGDIR_ALIGN(addr) (((addr)+SFMMU_PGDIR_SIZE-1)&SFMMU_PGDIR_MASK)
+
+
+#endif /* !(_SPARC_PGTSFMMU_H) */
+
--- /dev/null
+/* pgtsrmmu.h: SRMMU page table defines and code.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/page.h> /* just in case */
+
+#ifndef _SPARC_PGTSRMMU_H
+#define _SPARC_PGTSRMMU_H
+
+#define SRMMU_PAGE_TABLE_SIZE 0x100 /* 64 entries, 4 bytes a piece */
+#define SRMMU_PMD_TABLE_SIZE 0x100 /* 64 entries, 4 bytes a piece */
+#define SRMMU_PGD_TABLE_SIZE 0x400 /* 256 entries, 4 bytes a piece */
+
+/* PMD_SHIFT determines the size of the area a second-level page table can map */
+#define SRMMU_PMD_SHIFT 18
+#define SRMMU_PMD_SIZE (1UL << SRMMU_PMD_SHIFT)
+#define SRMMU_PMD_MASK (~(SRMMU_PMD_SIZE-1))
+#define SRMMU_PMD_ALIGN(addr) (((addr)+SRMMU_PMD_SIZE-1)&SRMMU_PMD_MASK)
+
+/* PGDIR_SHIFT determines what a third-level page table entry can map */
+#define SRMMU_PGDIR_SHIFT 24
+#define SRMMU_PGDIR_SIZE (1UL << SRMMU_PGDIR_SHIFT)
+#define SRMMU_PGDIR_MASK (~(SRMMU_PGDIR_SIZE-1))
+#define SRMMU_PGDIR_ALIGN(addr) (((addr)+SRMMU_PGDIR_SIZE-1)&SRMMU_PGDIR_MASK)
+
+/*
+ * Three-level on SRMMU.
+ */
+
+#define SRMMU_PTRS_PER_PTE 64
+#define SRMMU_PTRS_PER_PMD 64
+#define SRMMU_PTRS_PER_PGD 256
+
+/* Just any arbitrary offset to the start of the vmalloc VM area: the
+ * current 8MB value just means that there will be a 8MB "hole" after the
+ * physical memory until the kernel virtual memory starts. That means that
+ * any out-of-bounds memory accesses will hopefully be caught.
+ * The vmalloc() routines leaves a hole of 4kB between each vmalloced
+ * area for the same reason. ;)
+ */
+#define SRMMU_VMALLOC_OFFSET (8*1024*1024)
+#define SRMMU_VMALLOC_START ((high_memory + SRMMU_VMALLOC_OFFSET) & ~(SRMMU_VMALLOC_OFFSET-1))
+
+/*
+ * Sparc SRMMU page table fields.
+ */
+
+#define _SRMMU_PAGE_VALID (SRMMU_ET_PTE)
+#define _SRMMU_PMD_VALID (SRMMU_ET_PTD)
+#define _SRMMU_PGD_VALID (SRMMU_ET_PTD)
+#define _SRMMU_PAGE_WRITE_USR (SRMMU_ACC_US_RDWR)
+#define _SRMMU_PAGE_WRITE_KERN (SRMMU_ACC_S_RDWR)
+#define _SRMMU_PAGE_EXEC (SRMMU_ACC_US_RDEXEC)
+#define _SRMMU_PAGE_RDONLY (SRMMU_ACC_US_RDONLY)
+#define _SRMMU_PAGE_NOREAD (SRMMU_ACC_U_ACCDENIED)
+#define _SRMMU_PAGE_NOCACHE (~SRMMU_PTE_C_MASK)
+#define _SRMMU_PAGE_PRIV (SRMMU_ACC_S_RDWREXEC)
+#define _SRMMU_PAGE_REF (SRMMU_PTE_R_MASK)
+#define _SRMMU_PAGE_DIRTY (SRMMU_PTE_M_MASK)
+#define _SRMMU_PAGE_COW (SRMMU_ACC_U_RDONLY)
+#define _SRMMU_PAGE_UNCOW (SRMMU_ACC_US_RDWR)
+
+/* We want the swapper not to swap out page tables, thus dirty and writable
+ * so that the kernel can change the entries as needed. Also valid for
+ * obvious reasons.
+ */
+#define _SRMMU_PAGE_TABLE (_SRMMU_PAGE_VALID | _SRMMU_PAGE_WRITE_KERN | _SRMMU_PAGE_REF | _SRMMU_PAGE_DIRTY)
+#define _SRMMU_PAGE_CHG_MASK (_SRMMU_PAGE_REF | _SRMMU_PAGE_DIRTY | SRMMU_ET_PTE)
+#define _SRMMU_PMD_CHG_MASK (SRMMU_ET_PTD)
+#define _SRMMU_PGD_CHG_MASK (SRMMU_ET_PTD)
+
+#define SRMMU_PAGE_NONE __pgprot(_SRMMU_PAGE_VALID | _SRMMU_PAGE_REF)
+#define SRMMU_PAGE_SHARED __pgprot(_SRMMU_PAGE_VALID | _SRMMU_PAGE_WRITE_USR | _SRMMU_PAGE_REF)
+#define SRMMU_PAGE_COPY __pgprot(_SRMMU_PAGE_VALID | _SRMMU_PAGE_REF | _SRMMU_PAGE_COW)
+#define SRMMU_PAGE_READONLY __pgprot(_SRMMU_PAGE_VALID | _SRMMU_PAGE_REF | SRMMU_ACC_US_RDONLY)
+#define SRMMU_PAGE_KERNEL __pgprot(_SRMMU_PAGE_VALID | _SRMMU_PAGE_PRIV | SRMMU_PTE_C_MASK)
+#define SRMMU_PAGE_INVALID __pgprot(SRMMU_ET_INVALID)
+
+#define _SRMMU_PAGE_NORMAL(x) __pgprot(_SRMMU_PAGE_VALID | _SRMMU_PAGE_REF | (x))
+
+/* SRMMU Register addresses */
+#define SRMMU_CTRL_REG 0x00000000
+#define SRMMU_CTXTBL_PTR 0x00000100
+#define SRMMU_CTX_REG 0x00000200
+#define SRMMU_FAULT_STATUS 0x00000300
+#define SRMMU_FAULT_ADDR 0x00000400
+#define SRMMU_AFAULT_STATUS 0x00000500
+#define SRMMU_AFAULT_ADDR 0x00000600
+
+/* The SRMMU control register fields:
+ * -------------------------------------------------------------------
+ * | IMPL | VERS | SysControl | PSO | Resv | No Fault | Enable |
+ * -------------------------------------------------------------------
+ * 31 28 27 24 23 8 7 6 2 1 0
+ *
+ * IMPL: Indicates the implementation of this SRMMU, read-only.
+ * VERS: The version of this implementation, again read-only.
+ * SysControl: This is an implementation specific field, the SRMMU
+ * specification does not define anything for this field.
+ * PSO: This determines whether the memory model as seen by the CPU
+ * is Partial Store Order (PSO=1) or Total Store Ordering (PSO=0).
+ * Resv: Don't touch these bits ;)
+ * No Fault: If zero, any fault acts as expected where the fault status
+ * and address registers are updated and a trap hits the CPU.
+ * When this bit is one, on any fault other than in ASI 9, the
+ * MMU updates the status and address fault registers but does
+ * not signal the CPU with a trap. This is useful to beat
+ * race conditions in low-level code when we have to throw
+ * a register window onto the stack in a spill/fill handler
+ * on multiprocessors.
+ * Enable: If one the MMU is doing translations, if zero the addresses
+ * given to the bus are pure physical.
+ */
+
+#define SRMMU_CTREG_IMPL_MASK 0xf0000000
+#define SRMMU_CTREG_IMPL_SHIFT 28
+#define SRMMU_CTREG_VERS_MASK 0x0f000000
+#define SRMMU_CTREG_VERS_SHIFT 24
+#define SRMMU_CTREG_SYSCNTRL_MASK 0x00ffff00
+#define SRMMU_CTREG_SYSCNTRL_SHIFT 8
+#define SRMMU_CTREG_PSO_MASK 0x00000080
+#define SRMMU_CTREG_PSO_SHIFT 7
+#define SRMMU_CTREG_RESV_MASK 0x0000007c
+#define SRMMU_CTREG_RESV_SHIFT 2
+#define SRMMU_CTREG_NOFAULT_MASK 0x00000002
+#define SRMMU_CTREG_NOFAULT_SHIFT 1
+#define SRMMU_CTREG_ENABLE_MASK 0x00000001
+#define SRMMU_CTREG_ENABLE_SHIFT 0
+
+/* Get the MMU control register */
+extern inline unsigned int srmmu_get_mmureg(void)
+{
+ register unsigned int retval;
+ __asm__ __volatile__("lda [%%g0] %1, %0\n\t" :
+ "=r" (retval) :
+ "i" (ASI_M_MMUREGS));
+ return retval;
+}
+
+/* Set the MMU control register */
+extern inline void srmmu_set_mmureg(unsigned long regval)
+{
+ __asm__ __volatile__("sta %0, [%%g0] %1\n\t" : :
+ "r" (regval), "i" (ASI_M_MMUREGS) : "memory");
+
+ return;
+}
+
+/* The SRMMU Context Table Pointer Register:
+ * ---------------------------------
+ * | Context Table Pointer | Resv |
+ * ---------------------------------
+ * 31 2 1 0
+ *
+ * This is where the MMU goes to in physical RAM to fetch the
+ * elements in the context table. The non-Resv bits of this
+ * address appear in bits 6-35 of the physical bus during miss
+ * processing, then indexed by the value in the Context Register.
+ * This table must be aligned on a boundary equal to the size of
+ * the table, we provide a nice macro for doing this based upon
+ * the significant bits in the context register.
+ */
+#define SRMMU_CTP_ADDR_MASK 0xfffffffc
+#define SRMMU_CTP_ADDR_PADDR_SHIFT 0x4
+#define SRMMU_CTP_RESV_MASK 0x00000003
+
+#define SRMMU_SIGBITS_TO_ALIGNMENT(numbits) ((1 << (numbits + 2)))
+
+
+/* Set the address of the context table. You pass this routine
+ * the physical address, we do the magic shifting for you.
+ */
+extern inline void srmmu_set_ctable_ptr(unsigned long paddr)
+{
+ unsigned long ctp;
+
+ ctp = (paddr >> SRMMU_CTP_ADDR_PADDR_SHIFT);
+ ctp &= SRMMU_CTP_ADDR_MASK;
+
+ __asm__ __volatile__("sta %0, [%1] %2\n\t" : :
+ "r" (ctp), "r" (SRMMU_CTXTBL_PTR),
+ "i" (ASI_M_MMUREGS) :
+ "memory");
+ return;
+}
+
+
+/* Get the address of the context table. We return the physical
+ * address of the table, again we do the shifting here.
+ */
+extern inline unsigned long srmmu_get_ctable_ptr(void)
+{
+ register unsigned int retval;
+
+ __asm__ __volatile__("lda [%1] %2, %0\n\t" :
+ "=r" (retval) :
+ "r" (SRMMU_CTXTBL_PTR),
+ "i" (ASI_M_MMUREGS));
+
+ retval &= SRMMU_CTP_ADDR_MASK;
+ retval = (retval << SRMMU_CTP_ADDR_PADDR_SHIFT);
+ return retval;
+}
+
+/* Set the context on an SRMMU */
+extern inline void srmmu_set_context(int context)
+{
+ __asm__ __volatile__("sta %0, [%1] %2\n\t" : :
+ "r" (context), "r" (SRMMU_CTX_REG),
+ "i" (ASI_M_MMUREGS) : "memory");
+ return;
+}
+
+/* Get the context on an SRMMU */
+extern inline int srmmu_get_context(void)
+{
+ register int retval;
+ __asm__ __volatile__("lda [%1] %2, %0\n\t" :
+ "=r" (retval) :
+ "r" (SRMMU_CTX_REG),
+ "i" (ASI_M_MMUREGS));
+ return retval;
+}
+
+/* SRMMU diagnostic register:
+ * --------------------------------------------------------
+ * | Virtual Address | PDC entry | DiagReg | Resv |
+ * --------------------------------------------------------
+ * 31 12 11 4 3 2 1 0
+ *
+ * An SRMMU implementation has the choice of providing this register
+ * and I don't know much about it.
+ */
+
+#define SRMMU_DIAG_VADDR_MASK 0xfffff000
+#define SRMMU_DIAG_PDC_MASK 0x00000ff0
+#define SRMMU_DIAG_REG_MASK 0x0000000c
+#define SRMMU_DIAG_RESV_MASK 0x00000003
+
+/* SRMMU Fault Status Register:
+ * -----------------------------------------------------------
+ * | Reserved | EBE | L | AT | FT | FAV | OW |
+ * -----------------------------------------------------------
+ * 31 18 17 10 9 8 7 5 4 2 1 0
+ *
+ * WARNING!!! On certain VERY BROKEN Viking Sun4d modules this register
+ * is complete TOAST! During a fault you cannot trust the values
+ * contained in this register, you must calculate them yourself
+ * by first using the trap program counter to decode the
+ * instruction the code tried to execute (ie. load or store) and
+ * the address they tried to access. I think the Fault Virtual
+ * Address register may be ok on these chips, but who knows. Grrr.
+ *
+ * Reserved: These bits must be zero.
+ * EBE: External bus error bits, implementation dependant (at least
+ * we know what the bits mean on sun4d Viking modules) ;)
+ * L: The level in tree traversal at which the fault occured. The
+ * values are... 0 = context table
+ * 1 = level-1 page table
+ * 2 = level-2 page table
+ * 3 = level-3 page table
+ * AT: Access type field. This is decoded as follows...
+ * 0 -- Load from user data space
+ * 1 -- Load from supervisor data space
+ * 2 -- Read/Execute from user instruction space
+ * 3 -- Read/Execute from supervisor instruction space
+ * 4 -- Store to user data space
+ * 5 -- Store to supervisor data space
+ * 6 -- Store to user instruction space
+ * 7 -- Store to supervisor instruction space (emacs does this)
+ * On the Viking -- TOAST!
+ * FT: This is the fault type field. It is used to determine what was
+ * wrong in the attempted translation. It can be one of...
+ * 0 -- None
+ * 1 -- Invalid address error
+ * 2 -- Protection violation error
+ * 3 -- Priviledge violation error
+ * 4 -- Translation error (your tables are fucked up)
+ * 5 -- Bus access error (you lose)
+ * 6 -- Internal error (might as well have a Viking)
+ * 7 -- Reserved (don't touch)
+ * FAV: Fault Address Valid bit. When set to one the fault address
+ * register contents are valid. It need not be valid for text
+ * faults as the trapped PC tells us this anyway.
+ * OW: The Overwrite Bit, if set to one, this register has been
+ * written to more than once by the hardware since software did
+ * a read. This mean multiple faults have occurred and you have
+ * to a manual page table tree traversal to continue the handling
+ * of the first fault. And on the Viking module....
+ *
+ * The Fault Address Register is just a 32-bit register representing the
+ * virtual address which caused the fault. It's validity is determined
+ * by the following equation:
+ * if(module==VIKING || FSR.FAV==0) forget_it();
+ * It's ok for the FAV to be invalid for a text fault because we can
+ * use the trapped program counter, however for a data fault we are SOL.
+ * I'll probably have to write a workaround for this situation too ;-(
+ */
+
+#define SRMMU_FSR_RESV_MASK 0xfffc0000 /* Reserved bits */
+#define SRMMU_FSR_EBE_MASK 0x0003fc00 /* External Bus Error bits */
+#define SRMMU_FSR_EBE_BERR 0x00000400 /* Bus Error */
+#define SRMMU_FSR_EBE_BTIMEO 0x00000800 /* Bus Time Out */
+#define SRMMU_FSR_EBE_UNCOR 0x00001000 /* Uncorrectable Error */
+#define SRMMU_FSR_EBE_UNDEF 0x00002000 /* Undefined Error */
+#define SRMMU_FSR_EBE_PARITY 0x00004000 /* Parity error */
+#define SRMMU_FSR_EBE_TPARITY 0x00006000 /* Tsunami parity error */
+#define SRMMU_FSR_EBE_SBUF 0x00008000 /* Store Buffer error */
+#define SRMMU_FSR_EBE_CSA 0x00010000 /* Control space access error (bad ASI) */
+#define SRMMU_FSR_EBE_EMRT 0x00020000 /* Viking Emergency Response Team */
+#define SRMMU_FSR_L_MASK 0x00000300 /* Fault level bits */
+#define SRMMU_FSR_L_CTABLE 0x00000000 /* Context table level flt/err */
+#define SRMMU_FSR_L_ONE 0x00000100 /* Level1 ptable flt/err */
+#define SRMMU_FSR_L_TWO 0x00000200 /* Level2 ptable flt/err */
+#define SRMMU_FSR_L_THREE 0x00000300 /* Level3 ptable flt/err */
+#define SRMMU_FSR_AT_MASK 0x000000e0 /* Access Type bits */
+#define SRMMU_FSR_AT_LUD 0x00000000 /* Load from User Data space */
+#define SRMMU_FSR_AT_LSD 0x00000020 /* What I'll need after writing this code */
+#define SRMMU_FSR_AT_RXUI 0x00000040 /* Read/Execute from user text */
+#define SRMMU_FSR_AT_RXSI 0x00000060 /* Read/Execute from supv text */
+#define SRMMU_FSR_AT_SUD 0x00000080 /* Store to user data space */
+#define SRMMU_FSR_AT_SSD 0x000000a0 /* Store to supv data space */
+#define SRMMU_FSR_AT_SUI 0x000000c0 /* Store to user text */
+#define SRMMU_FSR_AT_SSI 0x000000e0 /* Store to supv text */
+#define SRMMU_FSR_FT_MASK 0x0000001c /* Fault Type bits */
+#define SRMMU_FSR_FT_NONE 0x00000000 /* No fault occurred */
+#define SRMMU_FSR_FT_IADDR 0x00000002 /* Invalid address */
+#define SRMMU_FSR_FT_PROT 0x00000004 /* Protection violation */
+#define SRMMU_FSR_FT_PRIV 0x00000008 /* Privilege violation */
+#define SRMMU_FSR_FT_TRANS 0x0000000a /* Translation error */
+#define SRMMU_FSR_FT_BACC 0x0000000c /* Bus Access error */
+#define SRMMU_FSR_FT_IACC 0x0000000e /* Internal error */
+#define SRMMU_FSR_FT_RESV 0x00000010 /* Reserved, should not get this */
+#define SRMMU_FSR_FAV_MASK 0x00000002 /* Fault Address Valid bits */
+#define SRMMU_FSR_OW_MASK 0x00000001 /* SFSR OverWritten bits */
+
+/* Read the Fault Status Register on the SRMMU */
+extern inline unsigned int srmmu_get_fstatus(void)
+{
+ register unsigned int retval;
+
+ __asm__ __volatile__("lda [%1] %2, %0\n\t" :
+ "=r" (retval) :
+ "r" (SRMMU_FAULT_STATUS), "i" (ASI_M_MMUREGS));
+ return retval;
+}
+
+/* Read the Fault Address Register on the SRMMU */
+extern inline unsigned int srmmu_get_faddr(void)
+{
+ register unsigned int retval;
+
+ __asm__ __volatile__("lda [%1] %2, %0\n\t" :
+ "=r" (retval) :
+ "r" (SRMMU_FAULT_ADDR), "i" (ASI_M_MMUREGS));
+ return retval;
+}
+
+/* SRMMU Asynchronous Fault Status Register:
+ * -----------------------------------------
+ * | RESERVED |UCE|BTO|BERR|RSV|HFADDR|AFO|
+ * -----------------------------------------
+ * 31 13 12 11 10 9-8 7-4 0
+ *
+ * UCE: UnCorrectable Error
+ * BTO: Bus TimeOut
+ * BERR: Genreic Bus Error
+ * HFADDR: High 4 bits of the faulting address
+ * AFO: Asynchronous Fault Occurred
+ */
+#define SRMMU_AFSR_RESVMASK 0xffffe000
+#define SRMMU_AFSR_UCE 0x00001000
+#define SRMMU_AFSR_BTO 0x00000800
+#define SRMMU_AFSR_BERR 0x00000400
+#define SRMMU_AFSR_HFADDR 0x000000f0
+#define SRMMU_AFSR_AFO 0x00000001
+
+/* Read the asynchronous fault register */
+extern inline unsigned int srmmu_get_afstatus(void)
+{
+ register unsigned int retval;
+
+ __asm__ __volatile__("lda [%1] %2, %0\n\t" :
+ "=r" (retval) :
+ "r" (SRMMU_AFAULT_STATUS), "i" (ASI_M_MMUREGS));
+ return retval;
+}
+
+/* Read the Asynchronous Fault Address Register on the SRMMU */
+extern inline unsigned int srmmu_get_afaddr(void)
+{
+ register unsigned int retval;
+
+ __asm__ __volatile__("lda [%1] %2, %0\n\t" :
+ "=r" (retval) :
+ "r" (SRMMU_AFAULT_ADDR), "i" (ASI_M_MMUREGS));
+ return retval;
+}
+
+
+/* Flush the entire TLB cache on the SRMMU. */
+extern inline void srmmu_flush_whole_tlb(void)
+{
+ __asm__ __volatile__("sta %%g0, [%0] %1\n\t": :
+ "r" (0x400), /* Flush entire TLB!! */
+ "i" (ASI_M_FLUSH_PROBE) : "memory");
+
+ return;
+}
+
+/* Probe for an entry in the page-tables of the SRMMU. */
+extern inline unsigned long srmmu_hwprobe(unsigned long vaddr)
+{
+ unsigned long retval;
+
+ __asm__ __volatile__("lda [%1] %2, %0\n\t" :
+ "=r" (retval) :
+ "r" (vaddr | 0x400), "i" (ASI_M_FLUSH_PROBE));
+
+ return retval;
+}
+
+#endif /* !(_SPARC_PGTSRMMU_H) */
--- /dev/null
+/* pgtsun4.c: Regular Sun4 MMU support goes in here.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#ifndef _SPARC_PGTSUN4_H
+#define _SPARC_PGTSUN4_H
+
+#endif /* !(_SPARC_PGTSUN4_H) */
--- /dev/null
+/* pgtsun4c.h: Sun4c specific pgtable.h defines and code.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+#ifndef _SPARC_PGTSUN4C_H
+#define _SPARC_PGTSUN4C_H
+
+#define SUN4C_PAGE_TABLE_SIZE 0x100 /* 64 entries, 4 bytes a piece */
+
+/* NOTE: Now we put the free page pool and the page structures
+ * up in high memory above the kernel image which itself
+ * starts at KERNBASE. Also note PAGE_OFFSET in page.h
+ * This is just like what Linus does on the ALPHA.
+ */
+
+/* PMD_SHIFT determines the size of the area a second-level page table can map */
+#define SUN4C_PMD_SHIFT 22
+#define SUN4C_PMD_SIZE (1UL << SUN4C_PMD_SHIFT)
+#define SUN4C_PMD_MASK (~(SUN4C_PMD_SIZE-1))
+#define SUN4C_PMD_ALIGN(addr) (((addr)+SUN4C_PMD_SIZE-1)&SUN4C_PMD_MASK)
+
+/* PGDIR_SHIFT determines what a third-level page table entry can map */
+#define SUN4C_PGDIR_SHIFT 22
+#define SUN4C_PGDIR_SIZE (1UL << SUN4C_PGDIR_SHIFT)
+#define SUN4C_PGDIR_MASK (~(SUN4C_PGDIR_SIZE-1))
+#define SUN4C_PGDIR_ALIGN(addr) (((addr)+SUN4C_PGDIR_SIZE-1)&SUN4C_PGDIR_MASK)
+
+/* To make sun4c_paging_init() happy, I provide the following macros. */
+#define SUN4C_REAL_PGDIR_SHIFT 18
+#define SUN4C_REAL_PGDIR_SIZE (1UL << SUN4C_REAL_PGDIR_SHIFT)
+#define SUN4C_REAL_PGDIR_MASK (~(SUN4C_REAL_PGDIR_SIZE-1))
+#define SUN4C_REAL_PGDIR_ALIGN(addr) (((addr)+SUN4C_REAL_PGDIR_SIZE-1)&SUN4C_REAL_PGDIR_MASK)
+
+/*
+ * To be efficient, and not have to worry about allocating such
+ * a huge pgd, we make the kernel sun4c tables each hold 1024
+ * entries and the pgd similarly.
+ */
+
+#define SUN4C_PTRS_PER_PTE 1024
+#define SUN4C_PTRS_PER_PMD 1
+#define SUN4C_PTRS_PER_PGD 1024
+
+/* Just any arbitrary offset to the start of the vmalloc VM area: the
+ * current 8MB value just means that there will be a 8MB "hole" after the
+ * physical memory until the kernel virtual memory starts. That means that
+ * any out-of-bounds memory accesses will hopefully be caught.
+ * The vmalloc() routines leaves a hole of 4kB between each vmalloced
+ * area for the same reason. ;)
+ */
+#define SUN4C_VMALLOC_OFFSET (8*1024*1024)
+#define SUN4C_VMALLOC_START ((high_memory + SUN4C_VMALLOC_OFFSET) & ~(SUN4C_VMALLOC_OFFSET-1))
+
+/*
+ * Sparc SUN4C page table fields. (for now, basically the same as the i386)
+ */
+
+#define _SUN4C_PAGE_VALID 0x80000000 /* valid page */
+#define _SUN4C_PAGE_WRITE 0x40000000 /* can be written to */
+#define _SUN4C_PAGE_USER 0x00000000 /* User page */
+#define _SUN4C_PAGE_NOCACHE 0x10000000 /* non-cacheable page */
+#define _SUN4C_PAGE_PRIV 0x20000000 /* bit to signify privileged page */
+#define _SUN4C_PAGE_REF 0x02000000 /* Page has been accessed/referenced */
+#define _SUN4C_PAGE_DIRTY 0x01000000 /* Page has been modified, is dirty */
+#define _SUN4C_PAGE_COW 0x00800000 /* COW page */
+
+/* Sparc sun4c mmu has only a writable bit. Thus if a page is valid it can be
+ * read in a load, and executed as code automatically. Although, the memory
+ * fault hardware does make a distinction between data-read faults and
+ * insn-read faults which is determined by which trap happened plus magic
+ * sync/async fault register values which must be checked in the actual
+ * fault handler.
+ */
+
+#define _SUN4C_PFN_MASK 0x0000ffff /* just the page frame number */
+#define _SUN4C_MMU_MASK 0xffff0000 /* just the non-page pte bits */
+
+/* The following are for pgd/pmd's */
+#define _SUN4C_PGD_PFN_MASK 0x00ffffff /* bits to hold page tables address */
+#define _SUN4C_PGD_MMU_MASK 0xff000000 /* pgd/pfn protection bits */
+#define _SUN4C_PGD_PAGE_SHIFT 8 /* bits to shift to obtain address */
+
+/* We want the swapper not to swap out page tables, thus dirty and writable
+ * so that the kernel can change the entries as needed. Also valid for
+ * obvious reasons.
+ */
+#define _SUN4C_PAGE_TABLE (_SUN4C_PAGE_VALID | _SUN4C_PAGE_WRITE | _SUN4C_PAGE_REF | _SUN4C_PAGE_DIRTY | _SUN4C_PAGE_PRIV | _SUN4C_PAGE_NOCACHE) /* No cache for now */
+#define _SUN4C_PAGE_CHG_MASK (_SUN4C_PAGE_REF | _SUN4C_PAGE_DIRTY | _SUN4C_PFN_MASK)
+#define _SUN4C_PGD_CHG_MASK (_SUN4C_PAGE_REF | _SUN4C_PAGE_DIRTY | _SUN4C_PGD_PFN_MASK)
+
+#define SUN4C_PAGE_NONE __pgprot(_SUN4C_PAGE_VALID | _SUN4C_PAGE_REF)
+#define SUN4C_PAGE_SHARED __pgprot(_SUN4C_PAGE_VALID | _SUN4C_PAGE_WRITE | _SUN4C_PAGE_REF)
+#define SUN4C_PAGE_COPY __pgprot(_SUN4C_PAGE_VALID | _SUN4C_PAGE_REF | _SUN4C_PAGE_COW)
+#define SUN4C_PAGE_READONLY __pgprot(_SUN4C_PAGE_VALID | _SUN4C_PAGE_REF)
+#define SUN4C_PAGE_KERNEL __pgprot(_SUN4C_PAGE_VALID | _SUN4C_PAGE_WRITE | _SUN4C_PAGE_PRIV | _SUN4C_PAGE_REF | _SUN4C_PAGE_DIRTY | _SUN4C_PAGE_NOCACHE)
+#define SUN4C_PAGE_INVALID __pgprot(0)
+
+#define _SUN4C_PAGE_NORMAL(x) __pgprot(_SUN4C_PAGE_VALID | _SUN4C_PAGE_REF | (x))
+
+/* The Sun4c mmu physical segment map allocation data structure.
+ * For each physical segmap available on the mmu we have one entry,
+ * 127 on the sun4c (except SparcStation 2's which seem to have 255)
+ * and 512 on the sun4. Each segmap can be in various stages of
+ * allocation.
+ */
+#define PSEG_ENTRIES 513 /* We allocate 513 entries for simplicity */
+extern unsigned int phys_seg_map[PSEG_ENTRIES];
+extern unsigned int phys_seg_life[PSEG_ENTRIES];
+
+/* for phys_seg_map entries */
+#define PSEG_AVL 0x0 /* Physical segment is available/free */
+#define PSEG_USED 0x1 /* A segmap currently being used */
+#define PSEG_RSV 0x2 /* This segmap is reserved (used for proms addr space) */
+#define PSEG_KERNEL 0x3 /* This is a kernel hard segment, cannot deallocate */
+
+/* for phys_seg_life entries */
+/* The idea is, every call to update_mmu_cache we increment all the life
+ * counters. When we re-allocate or allocate a physical segment for the
+ * first time we set the phys_seg_life entry to PSEG_BORN. Also, when we
+ * fill a pte for a segment already loaded we *decrease* the life count
+ * by two for that segment. We'll see how this works.
+ */
+#define PSEG_BORN 0x00 /* Just allocated */
+
+#endif /* !(_SPARC_PGTSUN4C_H) */
#ifndef __ASM_SPARC_PROCESSOR_H
#define __ASM_SPARC_PROCESSOR_H
+#include <linux/sched.h> /* For intr_count */
+
+#include <asm/ptrace.h> /* For pt_regs declaration */
+
/*
* Bus types
*/
-#define EISA_bus 1
+#define EISA_bus 0
#define EISA_bus__is_a_macro /* for versions in ksyms.c */
#define MCA_bus 0
#define MCA_bus__is_a_macro /* for versions in ksyms.c */
/*
* Write Protection works right in supervisor mode on the Sparc
*/
-
+#if 0 /* Let's try this out ;) */
#define wp_works_ok 1
#define wp_works_ok__is_a_macro /* for versions in ksyms.c */
+#else
+extern char wp_works_ok;
+#endif
/*
* User space process size: 3GB. This is hardcoded into a few places,
* so don't change it unless you know what you are doing.
*
- * "this is gonna have to change to 1gig for the sparc" - David S. Miller
+ * With the way identity mapping works on the sun4c, this is the best
+ * value to use.
+ *
+ * This has to be looked into for a unified sun4c/sun4m task size.
*/
-#define TASK_SIZE (0xC0000000UL)
+#define TASK_SIZE (0xC000000UL)
/*
* Size of io_bitmap in longwords: 32 is ports 0-0x3ff.
*/
#define IO_BITMAP_SIZE 32
-/* The first five entries here MUST be the first four. This allows me to
+/* The first four entries here MUST be the first four. This allows me to
* do %lo(offset) loads and stores in entry.S. See TRAP_WIN_CLEAN to see
* why.
*/
unsigned long uwindows; /* how many user windows are in the set */
unsigned long wim; /* user's window invalid mask */
unsigned long w_saved; /* how many windows saved in reg_window[] */
- unsigned long ksp; /* kernel stack pointer */
- unsigned long usp; /* user's sp, throw reg windows here */
- unsigned long psr; /* save for condition codes */
- unsigned long reg_window[16*24];
- unsigned long cr3; /* why changed from ptbr? */
- unsigned int pcc;
- unsigned int asn;
- unsigned long unique;
- unsigned long flags;
- unsigned long res1, res2;
- unsigned long pc; /* program counter */
- unsigned long npc; /* next program counter */
+ unsigned long ksp; /* kernel stack pointer */
+ unsigned long usp; /* user's sp, throw reg windows here */
+ unsigned long psr; /* save for condition codes */
+ unsigned long pc; /* program counter */
+ unsigned long npc; /* next program counter */
+ unsigned long yreg;
+ unsigned long align; /* to get 8-byte alignment XXX */
+ unsigned long reg_window[16];
+ unsigned long pgd_ptr;
+ int context; /* The context allocated to this thread */
/* 8 local registers + 8 in registers * 24 register windows.
- * Most sparcs I know of only have 8 windows implemented,
+ * Most sparcs I know of only have 7 or 8 windows implemented,
* we determine how many at boot time and store that value
* in nwindows.
*/
- unsigned long globl_regs[8]; /* global regs need to be saved too */
- unsigned long yreg;
unsigned long float_regs[64]; /* V8 and below have 32, V9 has 64 */
};
-#define INIT_MMAP { &init_task, 0x0, 0x40000000, \
- PAGE_SHARED , VM_READ | VM_WRITE | VM_EXEC }
+#define INIT_MMAP { &init_task, (PAGE_OFFSET), (0xff000000UL), \
+ 0x0 , VM_READ | VM_WRITE | VM_EXEC }
#define INIT_TSS { \
- 0, 0, 0, 0, 0, 0, \
- { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- { 0, 0, 0, 0, 0, 0, 0, 0, }, \
- 0, \
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, \
+ (long) &swapper_pg_dir, -1, \
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
* spill if things don't go right (bad user stack pointer). In reality
* it is not per-process per se, it just sits in the kernel stack while
* the current process is in a handler then it is basically forgotten
- * about.
+ * about the next time flow control goes back to that process.
*/
-struct thread_frame {
- unsigned int thr_psr;
- unsigned int thr_pc;
- unsigned int thr_npc;
- unsigned int thr_y;
- unsigned int thr_globals[8];
- unsigned int thr_outs[8];
+/* Sparc stack save area allocated for each save, not very exciting. */
+struct sparc_save_stack {
+ unsigned int locals[8];
+ unsigned int ins[8];
+ unsigned int padd[8];
};
/*
*/
extern inline void start_bh_atomic(void)
{
- unsigned long dummy, psr;
- __asm__ __volatile__("rd %%psr, %2\n\t"
- "wr %2, 0x20, %%psr\n\t" /* disable traps */
- "ld %1,%0\n\t"
- "add %0,1,%0\n\t"
- "st %0,%1\n\t"
- "wr %2, 0x0, %%psr\n\t" /* enable traps */
- : "=r" (dummy), "=m" (intr_count)
- : "0" (0), "r" (psr=0));
+ __asm__ __volatile__("rd %%psr, %%g2\n\t"
+ "wr %%g2, 0x20, %%psr\n\t" /* disable traps */
+ "ld %0,%%g3\n\t"
+ "add %%g3,1,%%g3\n\t"
+ "st %%g3,%0\n\t"
+ "wr %%g2, 0x0, %%psr\n\t" /* enable traps */
+ : "=m" (intr_count)
+ : : "g2", "g3", "memory");
}
extern inline void end_bh_atomic(void)
{
- unsigned long dummy, psr;
- __asm__ __volatile__("rd %%psr, %2\n\t"
- "wr %2, 0x20, %%psr\n\t"
- "ld %1,%0\n\t"
- "sub %0,1,%0\n\t"
- "st %0,%1\n\t"
- "wr %2, 0x0, %%psr\n\t"
- : "=r" (dummy), "=m" (intr_count)
- : "0" (0), "r" (psr=0));
+ __asm__ __volatile__("rd %%psr, %%g2\n\t"
+ "wr %%g2, 0x20, %%psr\n\t"
+ "ld %0,%%g3\n\t"
+ "sub %%g3,1,%%g3\n\t"
+ "st %%g3,%0\n\t"
+ "wr %%g2, 0x0, %%psr\n\t"
+ : "=m" (intr_count)
+ : : "g2", "g3", "memory");
+}
+
+/*
+ * Do necessary setup to start up a newly executed thread.
+ */
+static inline void start_thread(struct pt_regs * regs, unsigned long sp,
+ unsigned long fp)
+{
+ printk("start_thread called, halting..n");
+ halt();
}
#endif /* __ASM_SPARC_PROCESSOR_H */
#define PSR_VERS 0x0f000000 /* cpu-version field */
#define PSR_IMPL 0xf0000000 /* cpu-implementation field */
+#ifndef __ASSEMBLY__
+/* Get the %psr register. */
+extern inline unsigned int get_psr(void)
+{
+ unsigned int psr;
+ __asm__ __volatile__("rd %%psr, %0\n\t" :
+ "=r" (psr));
+ return psr;
+}
+
+extern inline void put_psr(unsigned int new_psr)
+{
+ __asm__("wr %0, 0x0, %%psr\n\t" : :
+ "r" (new_psr));
+}
+
+/* Get the %fsr register. Be careful, make sure the floating point
+ * enable bit is set in the %psr when you execute this or you will
+ * incur a trap.
+ */
+
+extern unsigned int fsr_storage;
+
+extern inline unsigned int get_fsr(void)
+{
+ unsigned int fsr = 0;
+
+ __asm__ __volatile__("st %%fsr, %1\n\t"
+ "ld %1, %0\n\t" :
+ "=r" (fsr) :
+ "m" (fsr_storage));
+ return fsr;
+}
+
+#endif /* !(__ASSEMBLY__) */
+
#endif /* !(__LINUX_SPARC_V8) */
#ifdef __LINUX_SPARC_V9
#define PSTATE_CLE 0x200 /* Current Little Endian */
+extern inline unsigned int get_v9_pstate(void)
+{
+ unsigned int pstate;
+ __asm__ __volatile__("rdpr %pstate, %0\n\t" :
+ "=r" (pstate));
+ return pstate;
+}
+
+extern inline void put_v9_pstate(unsigned int pstate)
+{
+ __asm__ __volatile__("wrpr %0, 0x0, %pstate\n\t" : :
+ "r" (pstate));
+ return;
+}
+
/* The Version Register holds vendor information for the chip:
---------------------------------------------------------------------------
#define VERS_MASK 0x0000000ff0000000 /* impl. dep. chip mask revision */
#define VERS_MANUF 0xffff000000000000 /* Manufacturer ID code */
+extern inline unsigned int get_v9_version(void)
+{
+ unsigned int vers;
+ __asm__ __volatile__("rdpr %ver, %0\n\t" :
+ "=r" (vers));
+ return vers;
+}
+
+extern inline unsigned int get_v9_tstate(void)
+{
+ unsigned int tstate;
+ __asm__ __volatile__("rdpr %tstate, %0\n\t" :
+ "=r" (pstate));
+ return tstate;
+}
+
+extern inline unsigned int get_v9_pil(void)
+{
+ unsigned int pil;
+ __asm__ __volatile__("rdpr %pil, %0\n\t" :
+ "=r" (pstate));
+ return pil;
+}
+
+extern inline void put_v9_pil(unsigned int pil)
+{
+ __asm__ __volatile__("wrpr %0, 0x0, %pil\n\t" : :
+ "r" (pil));
+ return;
+}
+
+
#endif /* !(__LINUX_SPARC_V9) */
#endif /* !(__LINUX_SPARC_PSR_H) */
stack during a system call. */
struct pt_regs {
- unsigned long ps; /* previous supervisor, same as alpha I believe */
+ unsigned long psr; /* for condition codes */
unsigned long pc; /* current and next program counter */
unsigned long npc;
- unsigned long sp; /* stack and frame pointer */
- unsigned long fp;
- unsigned long psr; /* for condition codes */
- unsigned long nuwin; /* number of user windows */
+ unsigned long y;
/* not sure yet whether all regs are necessary
* but this is how it is traditionally done on the sparc.
*/
- unsigned long u_regs[24*16];
- unsigned long f_regs[64]; /* yuck yuck yuck */
+ unsigned long u_regs[16]; /* globals and ins */
};
#ifdef __KERNEL__
-#define user_mode(regs) (0x0) /* if previous supervisor is 0, came from user */
+/* if previous supervisor is 0, came from user */
+#define user_mode(regs) (0x0)
extern void show_regs(struct pt_regs *);
#endif
--- /dev/null
+/* ross.h: Ross module specific definitions and defines.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#ifndef _SPARC_ROSS_H
+#define _SPARC_ROSS_H
+
+/* Ross made Hypersparcs have a %psr 'impl' field of '0001'. The 'vers'
+ * field has '1111'.
+ */
+
+/* The MMU control register fields on the HyperSparc.
+ *
+ * -----------------------------------------------------------------
+ * |implvers| RSV |CWR|SE|WBE| MID |BM| C|CS|MR|CM|RSV|CE|RSV|NF|ME|
+ * -----------------------------------------------------------------
+ * 31 24 23-22 21 20 19 18-15 14 13 12 11 10 9 8 7-2 1 0
+ *
+ * Phew, lots of fields there ;-)
+ *
+ * CWR: Cache Wrapping Enabled, if one cache wrapping is on.
+ * SE: Snoop Enable, turns on bus snooping for cache activity if one.
+ * WBE: Write Buffer Enable, one turns it on.
+ * MID: The ModuleID of the chip for MBus transactions.
+ * BM: Boot-Mode. One indicates the MMU is in boot mode.
+ * C: Indicates whether accesses are cachable while the MMU is
+ * disabled.
+ * CS: Cache Size -- 0 = 128k, 1 = 256k
+ * MR: Memory Reflection, one indicates that the memory bus connected
+ * to the MBus supports memory reflection.
+ * CM: Cache Mode -- 0 = write-through, 1 = copy-back
+ * CE: Cache Enable -- 0 = no caching, 1 = cache is on
+ * NF: No Fault -- 0 = faults trap the CPU from supervisor mode
+ * 1 = faults from supervisor mode do not generate traps
+ * ME: MMU Enable -- 0 = MMU is off, 1 = MMU is on
+ */
+
+#define HYPERSPARC_CWENABLE 0x00200000
+#define HYPERSPARC_SBENABLE 0x00100000
+#define HYPERSPARC_WBENABLE 0x00080000
+#define HYPERSPARC_MIDMASK 0x00078000
+#define HYPERSPARC_BMODE 0x00004000
+#define HYPERSPARC_ACENABLE 0x00002000
+#define HYPERSPARC_CSIZE 0x00001000
+#define HYPERSPARC_MRFLCT 0x00000800
+#define HYPERSPARC_CMODE 0x00000400
+#define HYPERSPARC_CENABLE 0x00000100
+#define HYPERSPARC_NFAULT 0x00000002
+#define HYPERSPARC_MENABLE 0x00000001
+
+/* Flushes which clear out only the on-chip Ross HyperSparc ICACHE. */
+extern inline void flush_i_page(unsigned int addr)
+{
+ __asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
+ "r" (addr), "i" (ASI_M_IFLUSH_PAGE) :
+ "memory");
+ return;
+}
+
+extern inline void flush_i_seg(unsigned int addr)
+{
+ __asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
+ "r" (addr), "i" (ASI_M_IFLUSH_SEG) :
+ "memory");
+ return;
+}
+
+extern inline void flush_i_region(unsigned int addr)
+{
+ __asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
+ "r" (addr), "i" (ASI_M_IFLUSH_REGION) :
+ "memory");
+ return;
+}
+
+extern inline void flush_i_ctx(unsigned int addr)
+{
+ __asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
+ "r" (addr), "i" (ASI_M_IFLUSH_CTX) :
+ "memory");
+ return;
+}
+
+extern inline void flush_i_user(unsigned int addr)
+{
+ __asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
+ "r" (addr), "i" (ASI_M_IFLUSH_USER) :
+ "memory");
+ return;
+}
+
+/* Finally, flush the entire ICACHE. */
+extern inline void flush_whole_icache(void)
+{
+ __asm__ __volatile__("sta %%g0, [%%g0] %0\n\t" : :
+ "i" (ASI_M_FLUSH_IWHOLE));
+ return;
+}
+
+
+/* The ICCR instruction cache register on the HyperSparc.
+ *
+ * -----------------------------------------------
+ * | | FTD | IDC |
+ * -----------------------------------------------
+ * 31 1 0
+ *
+ * This register is accessed using the V8 'wrasr' and 'rdasr'
+ * opcodes, since not all assemblers understand them and those
+ * that do use different semantics I will just hard code the
+ * instruction with a '.word' statement.
+ *
+ * FTD: If set to one flush instructions executed during an
+ * instruction cache hit occurs, the corresponding line
+ * for said cache-hit is invalidated. If FTD is zero,
+ * an unimplemented 'flush' trap will occur when any
+ * flush is executed by the processor.
+ *
+ * ICE: If set to one, the instruction cache is enabled. If
+ * zero, the cache will not be used for instruction fetches.
+ *
+ * All other bits are read as zeros, and writes to them have no
+ * effect.
+ */
+
+extern inline unsigned int get_ross_icr(void)
+{
+ unsigned int icreg;
+
+ __asm__ __volatile__(".word 0xbf402000\n\t" : /* rd %iccr, %g1 */
+ "=r" (icreg) : :
+ "g1", "memory");
+
+ return icreg;
+}
+
+extern inline void put_ross_icr(unsigned int icreg)
+{
+ __asm__ __volatile__("or %%g0, %0, %%g1\n\t"
+ ".word 0xbf802000\n\t" /* wr %g1, 0x0, %iccr */
+ "nop\n\t"
+ "nop\n\t"
+ "nop\n\t" : :
+ "r" (icreg) :
+ "g1", "memory");
+
+ return;
+}
+
+#endif /* !(_SPARC_ROSS_H) */
--- /dev/null
+/* sbus.h: Defines for the Sun SBus.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#ifndef _SPARC_SBUS_H
+#define _SPARC_SBUS_H
+
+#include <asm/openprom.h> /* For linux_prom_registers and linux_prom_irqs */
+
+/* We scan which devices are on the SBus using the PROM node device
+ * tree. SBus devices are described in two different ways. You can
+ * either get an absolute address at which to access the device, or
+ * you can get a SBus 'slot' number and an offset within that slot.
+ */
+
+/* The base address at which to calculate device OBIO addresses. */
+#define SUN_SBUS_BVADDR 0xf8000000
+#define SBUS_OFF_MASK 0x01ffffff
+
+/* These routines are used to calculate device address from slot
+ * numbers + offsets, and vice versa.
+ */
+
+extern inline unsigned long sbus_devaddr(int slotnum, unsigned long offset)
+{
+ return (unsigned long) (SUN_SBUS_BVADDR+((slotnum)<<25)+(offset));
+}
+
+extern inline int sbus_dev_slot(unsigned long dev_addr)
+{
+ return (int) (((dev_addr)-SUN_SBUS_BVADDR)>>25);
+}
+
+extern inline unsigned long sbus_dev_offset(unsigned long dev_addr)
+{
+ return (unsigned long) (((dev_addr)-SUN_SBUS_BVADDR)&SBUS_OFF_MASK);
+}
+
+/* Handy macro */
+#define STRUCT_ALIGN(addr) ((addr+7)&(~7))
+
+/* Linus SBUS device tables */
+struct linux_sbus_device {
+ struct linux_sbus_device *next; /* next device on this SBus or null */
+ int prom_node; /* PROM device tree node for this device */
+ char *prom_name; /* PROM device name */
+ char *linux_name; /* Name used internally by Linux */
+
+ /* device register addresses */
+ struct linux_prom_registers reg_addrs[PROMREG_MAX];
+ int num_registers;
+
+ /* List of IRQ's this device uses. */
+ struct linux_prom_irqs irqs[PROMINTR_MAX];
+ int num_irqs;
+
+ unsigned long sbus_addr; /* Absolute base address for device. */
+ unsigned long sbus_vaddrs[PROMVADDR_MAX];
+ unsigned long num_vaddrs;
+ unsigned long offset; /* Offset given by PROM */
+ int slot; /* Device slot number */
+};
+
+/* This struct describes the SBus-es found on this machine. */
+struct linux_sbus {
+ struct linux_sbus *next; /* next SBus, if more than one SBus */
+ struct linux_sbus_device *devices; /* Link to devices on this SBus */
+ int prom_node; /* PROM device tree node for this SBus */
+ char *prom_name; /* Usually "sbus" */
+ int clock_freq; /* Speed of this SBus */
+};
+
+extern struct linux_sbus Linux_SBus;
+
+#endif /* !(_SPARC_SBUS_H) */
static inline void set_fs(unsigned long val)
{
+ unsigned long foo;
+ foo = val;
+
+ return;
}
#endif /* _ASM_SEGMENT_H */
--- /dev/null
+/* smpprim.h: SMP locking primitives on the Sparc
+ *
+ * God knows we won't be actually using this code for some time
+ * but I thought I'd write it since I knew how.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#ifndef __SPARC_SMPPRIM_H
+#define __SPARC_SMPPRIM_H
+
+/* Test and set the unsigned byte at ADDR to 1. Returns the previous
+ * value. On the Sparc we use the ldstub instruction since it is
+ * atomic.
+ */
+
+extern inline volatile char test_and_set(void *addr)
+{
+ char state = 0;
+
+ __asm__ __volatile__("ldstub [%0], %1 ! test_and_set\n\t"
+ "=r" (addr), "=r" (state) :
+ "0" (addr), "1" (state) : "memory");
+
+ return state;
+}
+
+/* Initialize a spin-lock. */
+extern inline volatile smp_initlock(void *spinlock)
+{
+ /* Unset the lock. */
+ *((unsigned char *) spinlock) = 0;
+
+ return;
+}
+
+/* This routine spins until it acquires the lock at ADDR. */
+extern inline volatile smp_lock(void *addr)
+{
+ while(test_and_set(addr) == 0xff)
+ ;
+
+ /* We now have the lock */
+ return;
+}
+
+/* This routine releases the lock at ADDR. */
+extern inline volatile smp_unlock(void *addr)
+{
+ *((unsigned char *) addr) = 0;
+}
+
+#endif /* !(__SPARC_SMPPRIM_H) */
/* string.h: External definitions for optimized assembly string
- routines for the Linux Kernel.
+ * routines for the Linux Kernel.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
- Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
-*/
+#ifndef _SPARC_STRING_H
+#define _SPARC_STRING_H
-extern inline size_t strlen(const char * str)
+extern __inline__ size_t strlen(const char * str)
{
- register size_t retval = 0;
- register char tmp = 0;
- register char * lstr;
-
- lstr = (char *) str;
-
- __asm__("ldub [%1], %2\n\t"
- "or %%g0, %%g0, %0\n\t"
- "orcc %2, %%g0, %%g0\n\t"
- "be 2f\n\t"
- "add %1, 0x1, %1\n\t"
- "1: ldub [%1], %2\n\t"
- "add %0, 0x1, %0\n\t"
- "orcc %2, %%g0, %%g0\n\t"
- "bne 1b\n\t"
- "add %1, 0x1, %1\n\t"
- "2:" :
- "=r" (retval), "=r" (lstr), "=r" (tmp) :
- "0" (retval), "1" (lstr), "2" (tmp));
-
- return retval;
+ const char *sc;
+
+ for (sc = str; *sc != '\0'; ++sc)
+ /* nothing */;
+ return sc - str;
}
extern __inline__ int strcmp(const char* str1, const char* str2)
{
- register unsigned int tmp1=0, tmp2=0;
- register int retval=0;
-
- __asm__("ldub [%1], %3\n\t"
- "ldub [%2], %4\n\t"
- "1: add %2, 0x1, %2\n\t"
- "cmp %3, %4\n\t"
- "bne,a 2f\n\t"
- "sub %2, 0x1, %2\n\t"
- "ldub [%1], %3\n\t"
- "add %1, 0x1, %1\n\t"
- "cmp %3, 0x0\n\t"
- "bne,a 1b\n\t"
- "ldub [%2], %4\n\t"
- "b 3f\n\t"
- "or %%g0, %%g0, %0\n\t"
- "2: ldub [%1], %3\n\t"
- "ldub [%2], %4\n\t"
- "sub %3, %4, %0\n\t"
- "3: \n\t" :
- "=r" (retval), "=r" (str1), "=r" (str2), "=r" (tmp1), "=r" (tmp2) :
- "0" (retval), "1" (str1), "2" (str2),
- "3" (tmp1), "4" (tmp2));
-
- return retval;
+ register signed char __res;
+
+ while (1) {
+ if ((__res = *str1 - *str2++) != 0 || !*str1++)
+ break;
+ }
+
+ return __res;
}
extern __inline__ int strncmp(const char* str1, const char* str2, size_t strlen)
{
- register int retval=0;
-
- __asm__("cmp %3, 0x0\n\t"
- "be 2f\n\t"
- "ldub [%2], %%g3\n\t"
- "1: ldub [%1], %%g2\n\t"
- "sub %%g2, %%g3, %0\n\t"
- "cmp %0, 0x0\n\t"
- "bne 2f\n\t"
- "add %2, 0x1, %2\n\t"
- "cmp %%g2, 0x0\n\t"
- "be 2f\n\t"
- "add %1, 0x1, %1\n\t"
- "addcc %3, -1, %3\n\t"
- "bne,a 1b\n\t"
- "ldub [%2], %%g3\n\t"
- "2: " :
- "=r" (retval), "=r" (str1), "=r" (str2), "=r" (strlen) :
- "0" (retval), "1" (str1), "2" (str2), "3" (strlen) :
- "%g2", "%g3");
-
- return retval;
+ register signed char __res = 0;
+
+ while (strlen) {
+ if ((__res = *str1 - *str2++) != 0 || !*str1++)
+ break;
+ strlen--;
+ }
+
+ return __res;
}
extern __inline__ char *strcpy(char* dest, const char* source)
{
- register char tmp;
- register char *retval;
-
- __asm__("or %%g0, %2, %0\n\t"
- "ldub [%1], %3\n\t"
- "1: stb %3, [%2]\n\t"
- "cmp %3, 0x0\n\t"
- "bne,a 1b\n\t"
- "ldub [%1], %3\n\t" :
- "=r" (retval), "=r" (source), "=r" (dest), "=r" (tmp) :
- "0" (retval), "1" (source), "2" (dest), "3" (tmp));
-
- return retval;
+ char *tmp = dest;
+
+ while ((*dest++ = *source++) != '\0')
+ /* nothing */;
+ return tmp;
}
extern __inline__ char *strncpy(char *dest, const char *source, size_t cpylen)
{
- register char tmp;
- register char *retval;
-
- __asm__("or %%g0, %1, %0\n\t"
- "1: cmp %4, 0x0\n\t"
- "be 2f\n\t"
- "ldub [%1], %3\n\t"
- "add %1, 0x1, %1\n\t"
- "stb %3, [%2]\n\t"
- "sub %4, 0x1, %4\n\t"
- "ba 1\n\t"
- "add %2, 0x1, %2\n\t" :
- "=r" (retval), "=r" (dest), "=r" (source), "=r"(tmp), "=r" (cpylen) :
- "0" (retval), "1" (dest), "2" (source),
- "3" (tmp), "4" (cpylen));
-
- return retval;
+ char *tmp = dest;
+
+ while (cpylen-- && (*dest++ = *source++) != '\0')
+ /* nothing */;
+
+ return tmp;
}
extern __inline__ char *strcat(char *dest, const char *src)
{
- register char *retval;
- register char temp=0;
-
- __asm__("or %%g0, %1, %0\n\t"
- "1: ldub [%1], %3\n\t"
- "cmp %3, 0x0\n\t"
- "bne,a 1b\n\t"
- "add %1, 0x1, %1\n\t"
- "2: ldub [%2], %3\n\t"
- "stb %3, [%1]\n\t"
- "add %1, 0x1, %1\n\t"
- "cmp %3, 0x0\n\t"
- "bne 2b\n\t"
- "add %2, 0x1, %2\n\t" :
- "=r" (retval), "=r" (dest), "=r" (src), "=r" (temp) :
- "0" (retval), "1" (dest), "2" (src), "3" (temp));
-
- return retval;
+ char *tmp = dest;
+
+ while (*dest)
+ dest++;
+ while ((*dest++ = *src++) != '\0')
+ ;
+
+ return tmp;
}
extern __inline__ char *strncat(char *dest, const char *src, size_t len)
{
- register char *retval;
- register char temp=0;
-
- __asm__("or %%g0, %1, %0\n\t"
- "1: ldub [%1], %3\n\t"
- "cmp %3, 0x0\n\t"
- "bne,a 1b\n\t"
- "add %1, 0x1, %1\n\t"
- "2: ldub [%2], %3\n\t"
- "stb %3, [%1]\n\t"
- "add %1, 0x1, %1\n\t"
- "add %3, -1, %3\n\t"
- "cmp %3, 0x0\n\t"
- "bne 2b\n\t"
- "add %2, 0x1, %2\n\t" :
- "=r" (retval), "=r" (dest), "=r" (src), "=r" (len), "=r" (temp) :
- "0" (retval), "1" (dest), "2" (src), "3" (len), "4" (temp));
-
- return retval;
+ char *tmp = dest;
+
+ if (len) {
+ while (*dest)
+ dest++;
+ while ((*dest++ = *src++)) {
+ if (--len == 0)
+ break;
+ }
+ }
+
+ return tmp;
}
extern __inline__ char *strchr(const char *src, int c)
{
- register char temp=0;
- register char *trick=0;
-
- __asm__("1: ldub [%0], %2\n\t"
- "cmp %2, %1\n\t"
- "bne,a 1b\n\t"
- "add %0, 0x1, %0\n\t"
- "or %%g0, %0, %3\n\t" :
- "=r" (src), "=r" (c), "=r" (temp), "=r" (trick), "=r" (src) :
- "0" (src), "1" (c), "2" (temp), "3" (trick), "4" (src));
-
- return trick;
+ for(; *src != c; ++src)
+ if (*src == '\0')
+ return NULL;
+ return (char *) src;
}
extern __inline__ char *strpbrk(const char *cs, const char *ct)
{
- register char temp1, temp2;
- register char *scratch;
- register char *trick;
-
- __asm__("or %%g0, %1, %4\n\t"
- "1: ldub [%0], %2\n\t"
- "2: ldub [%1], %3\n\t"
- "cmp %3, %2\n\t"
- "be 3f\n\t"
- "nop\n\t"
- "cmp %3, 0x0\n\t"
- "bne 2b\n\t"
- "add %1, 0x1, %1\n\t"
- "or %%g0, %4, %1\n\t"
- "b 1b\n\t"
- "add %0, 0x1, %0\n\t"
- "or %%g0, %0, %5\n\t" :
- "=r" (cs) :
- "r" (ct), "r" (temp1), "r" (temp2), "r" (scratch), "r" (trick=0),
- "0" (cs), "1" (ct));
-
- return trick;
+ const char *sc1,*sc2;
+ for( sc1 = cs; *sc1 != '\0'; ++sc1) {
+ for( sc2 = ct; *sc2 != '\0'; ++sc2) {
+ if (*sc1 == *sc2)
+ return (char *) sc1;
+ }
+ }
+ return NULL;
}
extern __inline__ size_t strspn(const char *s, const char *accept)
{
- register char temp1, temp2;
- register char* scratch;
- register size_t trick;
-
- __asm__("or %%g0, %1, %4\n\t"
- "1: ldub [%0], %2\n\t"
- "2: ldub [%1], %3\n\t"
- "cmp %3, 0x0\n\t"
- "be 3f\n\t"
- "cmp %3, %2"
- "bne 2b\n\t"
- "add %1, 0x1, %1\n\t"
- "add %0, 0x1, %0\n\t"
- "b 1b\n\t"
- "add %5, 0x1, %5\n\t"
- "3: or %%g0, %0, %4\n\t" :
- "=r" (s) :
- "r" (accept), "r" (temp1), "r" (temp2),
- "r" (scratch), "r" (trick=0), "0" (s));
-
- return trick;
+ const char *p;
+ const char *a;
+ size_t count = 0;
+
+ for (p = s; *p != '\0'; ++p) {
+ for (a = accept; *a != '\0'; ++a) {
+ if (*p == *a)
+ break;
+ }
+ if (*a == '\0')
+ return count;
+ ++count;
+ }
+ return count;
}
extern __inline__ char *strtok(char *s, const char *ct)
{
- static char* old; /* frob this kludge for now */
- register char *tok;
-
- if (s == (char *) 0)
- {
- if (old == (char *) 0)
- {
- return (char *) 0;
- }
- else
- s = old;
- }
-
- s += strspn(s, ct);
- if(*s == '\0')
- {
- old = (char *) 0;
- return (char *) 0;
- }
-
- tok = s;
- s = strpbrk(tok, ct);
- if (s == (char *) 0)
- old = (char *) 0;
- else
- {
- *s = '\0';
- old = s + 1;
- }
- return tok;
+ char *sbegin, *send;
+
+ sbegin = s ? s : ___strtok;
+ if (!sbegin) {
+ return NULL;
+ }
+ sbegin += strspn(sbegin,ct);
+ if (*sbegin == '\0') {
+ ___strtok = NULL;
+ return( NULL );
+ }
+ send = strpbrk( sbegin, ct);
+ if (send && *send != '\0')
+ *send++ = '\0';
+ ___strtok = send;
+ return (sbegin);
}
extern __inline__ void *memset(void *src, int c, size_t count)
{
- register void *retval;
-
- __asm__("or %%g0, %1, %0\n\t"
- "1: add %1, 0x1, %1\n\t"
- "2: add %3, -1, %3\n\t"
- "cmp %3, -1\n\t"
- "bne,a 1b\n\t"
- "stb %2, [%1]\n\t" :
- "=r" (retval), "=r" (src), "=r" (c), "=r" (count) :
- "0" (retval), "1" (src), "2" (c), "3" (count));
-
- return retval;
+ char *xs = (char *) src;
+
+ while (count--)
+ *xs++ = c;
+
+ return src;
}
extern __inline__ void *memcpy(void *dest, const void *src, size_t count)
{
- register void *retval;
- register char tmp;
-
- __asm__("or %%g0, %1, %0\n\t"
- "add %3, -1, %3\n\t"
- "cmp %3, -1\n\t"
- "be 2f\n\t"
- "1: ldub [%2], %4\n\t"
- "add %2, 0x1, %2\n\t"
- "add %3, -1, %3\n\t"
- "cmp %3, -1\n\t"
- "stb %4, [%1]\n\t"
- "bne 1b\n\t"
- "add %1, 0x1, %1\n\t"
- "2: " :
- "=r" (retval), "=r" (dest), "=r" (src), "=r" (count), "=r" (tmp) :
- "0" (retval), "1" (dest), "2" (src), "3" (count), "4" (tmp));
-
- return retval;
+ char *tmp = (char *) dest, *s = (char *) src;
+
+ while (count--)
+ *tmp++ = *s++;
+
+ return dest;
}
extern __inline__ void *memmove(void *dest, const void *src, size_t count)
{
- register void *retval;
- register char tmp;
-
- __asm__("or %%g0, %1, %1\n\t"
- "add %3, -1, %3\n\t"
- "cmp %3, -1\n\t"
- "be 2f\n\t"
- "1: ldub [%2], %4\n\t"
- "add %2, 0x1, %2\n\t"
- "add %3, -1, %3\n\t"
- "cmp %3, -1\n\t"
- "stb %4, [%1]\n\t"
- "bne 1b\n\t"
- "add %1, 0x1, %1\n\t"
- "2: " :
- "=r" (retval), "=r" (dest), "=r" (src), "=r" (count), "=r" (tmp) :
- "0" (retval), "1" (dest), "2" (src), "3" (count), "4" (tmp));
-
- return retval;
+ char *tmp, *s;
+
+ if (dest <= src) {
+ tmp = (char *) dest;
+ s = (char *) src;
+ while (count--)
+ *tmp++ = *s++;
+ }
+ else {
+ tmp = (char *) dest + count;
+ s = (char *) src + count;
+ while (count--)
+ *--tmp = *--s;
+ }
+
+ return dest;
}
extern __inline__ int memcmp(const void *cs, const void *ct, size_t count)
{
- register int retval;
- register unsigned long tmp1, tmp2;
-
- __asm__("or %%g0, %1, %0\n\t"
- "cmp %3, 0x0\n\t"
- "ble,a 3f\n\t"
- "or %%g0, %%g0, %0\n\t"
- "1: ldub [%1], %4\n\t"
- "ldub [%2], %5\n\t"
- "cmp %4, %5\n\t"
- "be,a 2f\n\t"
- "add %1, 0x1, %1\n\t"
- "bgeu 3f\n\t"
- "or %%g0, 0x1, %0\n\t"
- "b 3f\n\t"
- "or %%g0, -1, %0\n\t"
- "2: add %3, -1, %3\n\t"
- "cmp %3, 0x0\n\t"
- "bg 1b\n\t"
- "add %2, 0x1, %2\n\t"
- "or %%g0, %%g0, %0\n\t"
- "3: " :
- "=r" (retval) :
- "r" (cs), "r" (ct), "r" (count), "r" (tmp1=0), "r" (tmp2=0));
-
- return retval;
+ const unsigned char *su1, *su2;
+ signed char res = 0;
+
+ for( su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--)
+ if ((res = *su1 - *su2) != 0)
+ break;
+ return res;
}
+#endif /* !(_SPARC_STRING_H) */
-#ifndef _SPARC_SYSEN_H
-#define _SPARC_SYSEN_H
-
/* sysen.h: Bit fields within the "System Enable" register accessed via
- the ASI_CONTROL address space at address AC_SYSENABLE.
+ * the ASI_CONTROL address space at address AC_SYSENABLE.
+ *
+ * Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
+ */
- Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
-*/
+#ifndef _SPARC_SYSEN_H
+#define _SPARC_SYSEN_H
#define SENABLE_DVMA 0x20 /* enable dvma transfers */
#define SENABLE_CACHE 0x10 /* enable VAC cache */
-#define SENABLE_RESET 0x04 /* reset the whole machine, danger Will Robinson */
+#define SENABLE_RESET 0x04 /* reset whole machine, danger Will Robinson */
#endif /* _SPARC_SYSEN_H */
#include <asm/segment.h>
-/*
- * System defines.. Note that this is included both from .c and .S
- * files, so it does only defines, not any C code.
- */
-
/*
* I wish the boot time image was as beautiful as the Alpha's
* but no such luck. The icky PROM loads us at 0x0, and jumps
- * to magic address 0x4000 to start thing going. This means that
- * I can stick the pcb and user/kernel stacks in the area from
- * 0x0-0x4000 and be reasonably sure that this is sane.
+ * to magic address 0x4000 to start things going.
*
* Sorry, I can't impress people with cool looking 64-bit values
- * yet. ;-)
+ * yet. Wait till V9 ;-)
*/
+#include <asm/page.h>
#include <asm/openprom.h>
#include <asm/psr.h>
-#define INIT_PCB 0x00011fe0
-#define INIT_STACK 0x00013fe0
-#define START_ADDR 0x00004000
-#define START_SIZE (32*1024)
-#define EMPTY_PGT 0x00001000
-#define EMPTY_PGE 0x00001000
-#define ZERO_PGE 0x00001000
-
-#define IRQ_ENA_ADR 0x2000 /* This is a bitmap of all activated IRQ's
- * which is mapped in head.S during boot.
- */
+#define START_ADDR (0x00004000)
+#define EMPTY_PGT (&empty_bad_page)
+#define EMPTY_PGE (&empty_bad_page_table)
+#define ZERO_PGE (&empty_zero_page)
#ifndef __ASSEMBLY__
+/*
+ * Sparc (general) CPU types
+ */
+enum sparc_cpu {
+ sun4 = 0x00,
+ sun4c = 0x01,
+ sun4m = 0x02,
+ sun4d = 0x03,
+ sun4e = 0x04,
+ sun4u = 0x05,
+ sun_unknown = 0x06,
+};
+
+extern enum sparc_cpu sparc_cpu_model;
+
+extern unsigned long empty_bad_page;
+extern unsigned long empty_bad_page_table;
+extern unsigned long empty_zero_page;
+
extern void wrent(void *, unsigned long);
extern void wrkgp(unsigned long);
extern struct linux_romvec *romvec;
-#define halt() { romvec->pv_halt(); }
+#define halt() do { \
+ printk("Entering monitor in file %s at line %d\n", __FILE__, __LINE__); \
+romvec->pv_halt(); } while(0)
+
#define move_to_user_mode() halt()
-#define switch_to(x) halt()
#ifndef stbar /* store barrier Sparc insn to synchronize stores in PSO */
#define stbar() __asm__ __volatile__("stbar": : :"memory")
#endif
+/* When a context switch happens we must flush all user windows so that
+ * the windows of the current process are flushed onto it's stack. This
+ * way the windows are all clean for the next process.
+ */
+
+#define flush_user_windows() \
+do { __asm__ __volatile__( \
+ "save %sp, -64, %sp\n\t" \
+ "save %sp, -64, %sp\n\t" \
+ "save %sp, -64, %sp\n\t" \
+ "save %sp, -64, %sp\n\t" \
+ "save %sp, -64, %sp\n\t" \
+ "save %sp, -64, %sp\n\t" \
+ "save %sp, -64, %sp\n\t" \
+ "restore\n\t" \
+ "restore\n\t" \
+ "restore\n\t" \
+ "restore\n\t" \
+ "restore\n\t" \
+ "restore\n\t" \
+ "restore\n\t"); } while(0)
+
+extern void sparc_switch_to(void *new_task);
+
+#define switch_to(p) sparc_switch_to(p)
+
/* Changing the PIL on the sparc is a bit hairy. I'll figure out some
* more optimized way of doing this soon. This is bletcherous code.
*/
#define iret() __asm__ __volatile__ ("jmp %%l1\n\t" \
"rett %%l2\n\t": : :"memory")
-#define _set_gate(gate_addr,type,dpl,addr) \
-__asm__ __volatile__ ("nop\n\t")
-
-#define set_intr_gate(n,addr) \
- _set_gate(&idt[n],14,0,addr)
-
-#define set_trap_gate(n,addr) \
- _set_gate(&idt[n],15,0,addr)
-
-#define set_system_gate(n,addr) \
- _set_gate(&idt[n],15,3,addr)
-
-#define set_call_gate(a,addr) \
- _set_gate(a,12,3,addr)
-
-
-extern inline unsigned int get_psr(void)
-{
- unsigned int ret_val;
- __asm__("rd %%psr, %0\n\t" :
- "=r" (ret_val));
- return ret_val;
-}
-
-extern inline void put_psr(unsigned int new_psr)
-{
- __asm__("wr %0, 0x0, %%psr\n\t" : :
- "r" (new_psr));
-}
-
/* Must this be atomic? */
extern inline void *xchg_u32(int * m, unsigned long val)
unsigned long dummy;
__asm__ __volatile__(
- "ld %1,%2 ! xchg_u32() is here\n\t"
+ "ld %1,%2\n\t"
"st %0, %1\n\t"
"or %%g0, %2, %0"
: "=r" (val), "=m" (*m), "=r" (dummy)
--- /dev/null
+/* timer.h: Definitions for the timer chips on the Sparc.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+#ifndef _SPARC_TIMER_H
+#define _SPARC_TIMER_H
+
+#include <asm/system.h> /* For NCPUS */
+
+/* Timer structures. The interrupt timer has two properties which
+ * are the counter (which is handled in do_timer in sched.c) and the limit.
+ * This limit is where the timer's counter 'wraps' around. Oddly enough,
+ * the sun4c timer when it hits the limit wraps back to 1 and not zero
+ * thus when calculating the value at which it will fire a microsecond you
+ * must adjust by one. Thanks SUN for designing such great hardware ;(
+ */
+
+/* Note that I am only going to use the timer that interrupts at
+ * Sparc IRQ 10. There is another one available that can fire at
+ * IRQ 14. Currently it is left untouched, we keep the PROM's limit
+ * register value and let the prom take these interrupts. This allows
+ * L1-A to work.
+ */
+
+struct sun4c_timer_info {
+ volatile unsigned int cur_count10;
+ volatile unsigned int timer_limit10;
+ volatile unsigned int cur_count14;
+ volatile unsigned int timer_limit14;
+};
+
+#define SUN4C_TIMER_PHYSADDR 0xf3000000
+
+/* All accesses to the sun4c timer registers should use this macro. */
+#define SUN4C_TIMER_STRUCT ((volatile struct sun4c_timer_info *) TIMER_VADDR)
+
+/* A sun4m has two blocks of registers which are probably of the same
+ * structure. LSI Logic's L64851 is told to _decrement_ from the limit
+ * value. Aurora behaves similarly but its limit value is compacted in
+ * other fashion (it's wider). Documented fields are defined here.
+ */
+
+/* As with the interrupt register, we have two classes of timer registers
+ * which are per-cpu and master. Per-cpu timers only hit that cpu and are
+ * only level 14 ticks, master timer hits all cpus and is level 10.
+ */
+
+#define SUN4M_PRM_CNT_L 0x80000000
+#define SUN4M_PRM_CNT_LVALUE 0x7FFFFC00
+
+struct sun4m_timer_percpu_info {
+ volatile unsigned int l14_timer_limit; /* Initial value is 0x009c4000 */
+ volatile unsigned int l14_cur_count;
+
+ /* This register appears to be write only and/or inaccessible
+ * on Uni-Processor sun4m machines.
+ */
+ volatile unsigned int l14_limit_noclear; /* Data access error is here */
+
+ volatile unsigned int cntrl; /* =1 after POST on Aurora */
+ volatile unsigned char space[PAGE_SIZE - 16];
+};
+
+struct sun4m_timer_regs {
+ struct sun4m_timer_percpu_info cpu_timers[NCPUS];
+ volatile unsigned int l10_timer_limit;
+ volatile unsigned int l10_cur_count;
+
+ /* Again, this appears to be write only and/or inaccessible
+ * on uni-processor sun4m machines.
+ */
+ volatile unsigned int l10_limit_noclear;
+
+ /* This register too, it must be magic. */
+ volatile unsigned int foobar;
+
+ volatile unsigned int cfg; /* equals zero at boot time... */
+};
+
+extern struct sun4m_timer_regs *sun4m_timers;
+extern volatile unsigned int *master_l10_limit;
+
+#endif /* !(_SPARC_TIMER_H) */
--- /dev/null
+/* traps.h: Format of entries for the Sparc trap table.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#ifndef _SPARC_TRAPS_H
+#define _SPARC_TRAPS_H
+
+#define NUM_SPARC_TRAPS 255
+
+/* This is for V8 and V9 compliant Sparc CPUS */
+struct tt_entry {
+ unsigned long inst_one;
+ unsigned long inst_two;
+ unsigned long inst_three;
+ unsigned long inst_four;
+};
+
+/* We set this to _start in system setup. */
+extern struct tt_entry *sparc_ttable;
+
+/* This for V9 compliant Sparc CPUS */
+struct tt_v9_entry {
+ unsigned long inst_one;
+ unsigned long inst_two;
+ unsigned long inst_three;
+ unsigned long inst_four;
+ unsigned long inst_five;
+ unsigned long inst_six;
+ unsigned long inst_seven;
+ unsigned long inst_eight;
+};
+
+/* V9 has multiple trap tables, which one is used depends
+ * upon how deep within multiple traps you are.
+ * I believe the UltraSparc supports two levels now.
+ */
+extern struct tt_v9_entry *sparc_v9_ttablel0;
+extern struct tt_v9_entry *sparc_v9_ttablel1;
+
+/* For patching the trap table at boot time, we need to know how to
+ * form various common Sparc instructions. Thus these macros...
+ */
+
+#define SPARC_MOV_CONST_L3(const) (0xa6102000 | (const&0xfff))
+
+/* The following assumes that the branch lies before the place we
+ * are branching to. This is the case for a trap vector...
+ * You have been warned.
+ */
+#define SPARC_BRANCH(dest_addr, inst_addr) \
+ (0x10800000 | (((dest_addr-inst_addr)>>2)&0x3fffff))
+
+#define SPARC_RD_PSR_L0 (0xa1480000)
+#define SPARC_NOP (0x01000000)
+
+/* Various interesting trap levels. */
+/* First, hardware traps. */
+#define SP_TRAP_TFLT 0x1 /* Text fault */
+#define SP_TRAP_II 0x2 /* Illegal Instruction */
+#define SP_TRAP_PI 0x3 /* Priviledged Instruction */
+#define SP_TRAP_FPD 0x4 /* Floating Point Disabled */
+#define SP_TRAP_WOVF 0x5 /* Window Overflow */
+#define SP_TRAP_WUNF 0x6 /* Window Underflow */
+#define SP_TRAP_MNA 0x7 /* Memory Address Unaligned */
+#define SP_TRAP_FPE 0x8 /* Floating Point Exception */
+#define SP_TRAP_DFLT 0x9 /* Data Fault */
+#define SP_TRAP_TOF 0xa /* Tag Overflow */
+#define SP_TRAP_WDOG 0xb /* Watchpoint Detected */
+#define SP_TRAP_IRQ1 0x11 /* IRQ level 1 */
+#define SP_TRAP_IRQ2 0x12 /* IRQ level 2 */
+#define SP_TRAP_IRQ3 0x13 /* IRQ level 3 */
+#define SP_TRAP_IRQ4 0x14 /* IRQ level 4 */
+#define SP_TRAP_IRQ5 0x15 /* IRQ level 5 */
+#define SP_TRAP_IRQ6 0x16 /* IRQ level 6 */
+#define SP_TRAP_IRQ7 0x17 /* IRQ level 7 */
+#define SP_TRAP_IRQ8 0x18 /* IRQ level 8 */
+#define SP_TRAP_IRQ9 0x19 /* IRQ level 9 */
+#define SP_TRAP_IRQ10 0x1a /* IRQ level 10 */
+#define SP_TRAP_IRQ11 0x1b /* IRQ level 11 */
+#define SP_TRAP_IRQ12 0x1c /* IRQ level 12 */
+#define SP_TRAP_IRQ13 0x1d /* IRQ level 13 */
+#define SP_TRAP_IRQ14 0x1e /* IRQ level 14 */
+#define SP_TRAP_IRQ15 0x1f /* IRQ level 15 Non-maskable */
+#define SP_TRAP_RACC 0x20 /* Register Access Error ??? */
+#define SP_TRAP_IACC 0x21 /* Instruction Access Error */
+#define SP_TRAP_CPDIS 0x24 /* Co-Processor Disabled */
+#define SP_TRAP_BADFL 0x25 /* Unimplemented Flush Instruction */
+#define SP_TRAP_CPEXP 0x28 /* Co-Processor Exception */
+#define SP_TRAP_DACC 0x29 /* Data Access Error */
+#define SP_TRAP_DIVZ 0x2a /* Divide By Zero */
+#define SP_TRAP_DSTORE 0x2b /* Data Store Error ??? */
+#define SP_TRAP_DMM 0x2c /* Data Access MMU Miss ??? */
+#define SP_TRAP_IMM 0x3c /* Instruction Access MMU Miss ??? */
+
+/* Now the Software Traps... */
+#define SP_TRAP_SUNOS 0x80 /* SunOS System Call */
+#define SP_TRAP_SBPT 0x81 /* Software Breakpoint */
+#define SP_TRAP_SDIVZ 0x82 /* Software Divide-by-Zero trap */
+#define SP_TRAP_FWIN 0x83 /* Flush Windows */
+#define SP_TRAP_CWIN 0x84 /* Clean Windows */
+#define SP_TRAP_RCHK 0x85 /* Range Check */
+#define SP_TRAP_FUNA 0x86 /* Fix Unaligned Access */
+#define SP_TRAP_IOWFL 0x87 /* Integer Overflow */
+#define SP_TRAP_SOLARIS 0x88 /* Solaris System Call */
+#define SP_TRAP_NETBSD 0x89 /* NetBSD System Call */
+#define SP_TRAP_LINUX 0x90 /* Linux System Call */
+
+/* Special traps... */
+#define SP_TRAP_KBPT1 0xfe /* KADB/PROM Breakpoint one */
+#define SP_TRAP_KBPT2 0xff /* KADB/PROM Breakpoint two */
+
+/* Handy Macros */
+/* Is this a trap we never expect to get? */
+#define BAD_TRAP_P(level) \
+ ((level > SP_TRAP_WDOG && level < SP_TRAP_IRQ1) || \
+ (level > SP_TRAP_IACC && level < SP_TRAP_CPDIS) || \
+ (level > SP_TRAP_BADFL && level < SP_TRAP_CPEXP) || \
+ (level > SP_TRAP_DMM && level < SP_TRAP_IMM) || \
+ (level > SP_TRAP_IMM && level < SP_TRAP_SUNOS) || \
+ (level > SP_TRAP_LINUX && level < SP_TRAP_KBPT1))
+
+/* Is this a Hardware trap? */
+#define HW_TRAP_P(level) ((level > 0) && (level < SP_TRAP_SUNOS))
+
+/* Is this a Software trap? */
+#define SW_TRAP_P(level) ((level >= SP_TRAP_SUNOS) && (level <= SP_TRAP_KBPT2))
+
+/* Is this a system call for some OS we know about? */
+#define SCALL_TRAP_P(level) ((level == SP_TRAP_SUNOS) || \
+ (level == SP_TRAP_SOLARIS) || \
+ (level == SP_TRAP_NETBSD) || \
+ (level == SP_TRAP_LINUX))
+
+#endif /* !(_SPARC_TRAPS_H) */
--- /dev/null
+/* tsunami.h: Module specific definitions for Tsunami V8 Sparcs
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#ifndef _SPARC_TSUNAMI_H
+#define _SPARC_TSUNAMI_H
+
+/* The MMU control register on the Tsunami:
+ *
+ * -----------------------------------------------------------------------
+ * | implvers |SW|AV|DV|MV| RSV |PC|ITD|ALC| RSV |PE| RC |IE|DE|RSV|NF|ME|
+ * -----------------------------------------------------------------------
+ * 31 24 23 22 21 20 19-18 17 16 14 13-12 11 10-9 8 7 6-2 1 0
+ *
+ * SW: Enable Software Table Walks 0=off 1=on
+ * AV: Address View bit
+ * DV: Data View bit
+ * MV: Memory View bit
+ * PC: Parity Control
+ * ITD: ITBR disable
+ * ALC: Alternate Cacheable
+ * PE: Parity Enable 0=off 1=on
+ * RC: Refresh Control
+ * IE: Instruction cache Enable 0=off 1=on
+ * DE: Data cache Enable 0=off 1=on
+ * NF: No Fault, same as all other SRMMUs
+ * ME: MMU Enable, same as all other SRMMUs
+ */
+
+#define TSUNAMI_SW 0x00800000
+#define TSUNAMI_AV 0x00400000
+#define TSUNAMI_DV 0x00200000
+#define TSUNAMI_MV 0x00100000
+#define TSUNAMI_PC 0x00020000
+#define TSUNAMI_ITD 0x00010000
+#define TSUNAMI_ALC 0x00008000
+#define TSUNAMI_PE 0x00001000
+#define TSUNAMI_RCMASK 0x00000C00
+#define TSUNAMI_IENAB 0x00000200
+#define TSUNAMI_DENAB 0x00000100
+#define TSUNAMI_NF 0x00000002
+#define TSUNAMI_ME 0x00000001
+
+#endif /* !(_SPARC_TSUNAMI_H) */
--- /dev/null
+/* ultra.h: Definitions and defines for the TI V9 UltraSparc.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#ifndef _SPARC_ULTRA_H
+#define _SPARC_ULTRA_H
+
+/* Spitfire MMU control register:
+ *
+ * ----------------------------------------------------------
+ * | | IMPL | VERS | | MID | |
+ * ----------------------------------------------------------
+ * 64 31-28 27-24 23-22 21-17 16 0
+ *
+ * IMPL: Implementation of this Spitfire.
+ * VERS: Version of this Spitfire.
+ * MID: Module ID of this processor.
+ */
+
+#define SPITFIRE_MIDMASK 0x00000000003e0000
+
+/* Spitfire Load Store Unit control register:
+ *
+ * ---------------------------------------------------------------------
+ * | RSV | PWR | PWW | VWR | VWW | RSV | PMASK | DME | IME | DCE | ICE |
+ * ---------------------------------------------------------------------
+ * 63-25 24 23 22 21 20 19-4 3 2 1 0
+ *
+ * PWR: Physical Watchpoint Read enable: 0=off 1=on
+ * PWW: Physical Watchpoint Write enable: 0=off 1=on
+ * VWR: Virtual Watchpoint Read enable: 0=off 1=on
+ * VWW: Virtual Watchpoint Write enable: 0=off 1=on
+ * PMASK: Parity MASK ???
+ * DME: Data MMU Enable: 0=off 1=on
+ * IME: Instruction MMU Enable: 0=off 1=on
+ * DCE: Data Cache Enable: 0=off 1=on
+ * ICE: Instruction Cache Enable: 0=off 1=on
+ */
+
+#define SPITFIRE_LSU_PWR 0x01000000
+#define SPITFIRE_LSU_PWW 0x00800000
+#define SPITFIRE_LSU_VWR 0x00400000
+#define SPITFIRE_LSU_VWW 0x00200000
+#define SPITFIRE_LSU_PMASK 0x000ffff0
+#define SPITFIRE_LSU_DME 0x00000008
+#define SPITFIRE_LSU_IME 0x00000004
+#define SPITFIRE_LSU_DCE 0x00000002
+#define SPITFIRE_LSU_ICE 0x00000001
+
+#endif /* !(_SPARC_ULTRA_H) */
* think of right now to force the arguments into fixed registers
* before the trap into the system call with gcc 'asm' statements.
*
- * Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
*/
/* XXX - _foo needs to be __foo, while __NR_bar could be _NR_bar. */
{ \
long __res; \
__asm__ volatile ("or %%g0, %0, %%o0\n\t" \
- "t 0xa\n\t" \
+ "t 0x10\n\t" \
+ "nop\n\t" \
+ "or %%g0, %%o0, %0\n\t" \
: "=r" (__res) \
: "0" (__NR_##name) \
: "o0"); \
long __res; \
__asm__ volatile ("or %%g0, %0, %%o0\n\t" \
"or %%g0, %1, %%o1\n\t" \
- "t 0xa\n\t" \
+ "t 0x10\n\t" \
+ "nop\n\t" \
+ "or %%g0, %%o0, %0\n\t" \
: "=r" (__res), "=r" ((long)(arg1)) \
: "0" (__NR_##name),"1" ((long)(arg1)) \
: "o0", "o1"); \
__asm__ volatile ("or %%g0, %0, %%o0\n\t" \
"or %%g0, %1, %%o1\n\t" \
"or %%g0, %2, %%o2\n\t" \
- "t 0xa\n\t" \
+ "t 0x10\n\t" \
+ "nop\n\t" \
+ "or %%g0, %%o0, %0\n\t" \
: "=r" (__res), "=r" ((long)(arg1)), "=r" ((long)(args)) \
: "0" (__NR_##name),"1" ((long)(arg1)),"2" ((long)(arg2)) \
: "o0", "o1", "o2"); \
"or %%g0, %1, %%o1\n\t" \
"or %%g0, %2, %%o2\n\t" \
"or %%g0, %3, %%o3\n\t" \
- "t 0xa\n\t" \
+ "t 0x10\n\t" \
+ "nop\n\t" \
+ "or %%g0, %%o0, %0\n\t" \
: "=r" (__res), "=r" ((long)(arg1)), "=r" ((long)(arg2)), \
"=r" ((long)(arg3)) \
: "0" (__NR_##name), "1" ((long)(arg1)), "2" ((long)(arg2)), \
"or %%g0, %2, %%o2\n\t" \
"or %%g0, %3, %%o3\n\t" \
"or %%g0, %4, %%o4\n\t" \
- "t 0xa\n\t" \
+ "t 0x10\n\t" \
+ "nop\n\t" \
+ "or %%g0, %%o0, %0\n\t" \
: "=r" (__res), "=r" ((long)(arg1)), "=r" ((long)(arg2)), \
"=r" ((long)(arg3)), "=r" ((long)(arg4)) \
: "0" (__NR_##name),"1" ((long)(arg1)),"2" ((long)(arg2)), \
"or %%g0, %3, %%o3\n\t" \
"or %%g0, %4, %%o4\n\t" \
"or %%g0, %5, %%o5\n\t" \
- "t 0xa\n\t" \
+ "t 0x10\n\t" \
+ "nop\n\t" \
+ "or %%g0, %%o0, %0\n\t" \
: "=r" (__res), "=r" ((long)(arg1)), "=r" ((long)(arg2)), \
"=r" ((long)(arg3)), "=r" ((long)(arg4)), "=r" ((long)(arg5)) \
: "0" (__NR_##name),"1" ((long)(arg1)),"2" ((long)(arg2)), \
* some others too.
*/
#define __NR__exit __NR_exit
-static inline _syscall0(int,idle)
+/* static inline _syscall0(int,idle) */
static inline _syscall0(int,fork)
static inline _syscall0(int,pause)
-static inline _syscall0(int,setup)
+/* static inline _syscall0(int,setup) */
static inline _syscall0(int,sync)
static inline _syscall0(pid_t,setsid)
static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count)
static inline _syscall1(int,_exit,int,exitcode)
static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options)
+extern void sys_idle(void);
+static inline void idle(void)
+{
+ printk("[%d]idle()\n",current->pid);
+ sys_idle();
+ for(;;);
+}
+
+extern int sys_setup(void);
+static inline int setup(void)
+{
+ int retval;
+
+ printk("[%d]setup()\n",current->pid);
+ retval = sys_setup();
+ printk("[%d]setup() returned %d\n",current->pid, retval);
+}
+
+extern int sys_waitpid(int, int *, int);
static inline pid_t wait(int * wait_stat)
{
- return waitpid(-1,wait_stat,0);
+ long retval, i;
+ printk("[%d]wait(%p)\n", current->pid, wait_stat);
+ retval = waitpid(-1,wait_stat,0);
+ printk("[%d]wait(%p) returned %ld\n", current->pid, wait_stat, retval);
+ for (i = 0; i < 1000000000; i++);
+ return retval;
}
#endif
*/
extern unsigned long *trapbase;
-extern char end, etext, msgbuf;
+extern char end, etext, edata;
extern void flush_vac_context(void);
extern void flush_vac_segment(unsigned int foo_segment);
#ifndef _SPARC_VADDRS_H
#define _SPARC_VADDRS_H
+#include <asm/head.h>
+
/* asm-sparc/vaddrs.h: Here will be define the virtual addresses at
* which important I/O addresses will be mapped.
* For instance the timer register virtual address
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
*/
-#define TIMER_VADDR 0x3000 /* Next page after where the interrupt enable
- * register gets mapped at boot.
- */
+/* I can see only one reason why we should have statically defined
+ * mappings for devices and is the speedup improvements of not loading
+ * a pointer and then the value in the assembly code
+ */
+
+#define AUXIO_VADDR (KERNBASE+0x6000) /* Auxiliary IO register */
+#define TIMER_VADDR (AUXIO_VADDR+0x1000) /* One page after AUXIO, length is
+ * 5 pages which is needed on sun4m.
+ */
+#define INTREG_VADDR (TIMER_VADDR+0x5000)
+
+#define IOBASE_VADDR 0xfe000000 /* Base for mapping pages */
+#define IOBASE_LEN 0x00400000 /* Length of the IO area */
+#define IOBASE_SUN4C_SEGMAP 100
+#define DVMA_VADDR 0xfff00000 /* Base area of the DVMA on the 4c */
+#define DVMA_LEN 0x000c0000 /* Size of the DVMA address space */
+
+/* On sun4m machines we need per-cpu virtual areas */
+#define PERCPU_VADDR 0xff000000 /* Base for per-cpu virtual mappings */
+#define PERCPU_ENTSIZE 0x00100000
+#define PERCPU_LEN ((PERCPU_ENTSIZE*NCPUS))
+
+/* per-cpu offsets */
+#define PERCPU_TBR_OFFSET 0x00000 /* %tbr, mainly used for identification. */
+#define PERCPU_KSTACK_OFFSET 0x01000 /* Beginning of kernel stack for this cpu */
+#define PERCPU_MBOX_OFFSET 0x02000 /* Prom SMP Mailbox */
+#define PERCPU_CPUID_OFFSET 0x03000 /* Per-cpu ID number. */
+#define PERCPU_ISALIVE_OFFSET 0x03004 /* Has CPU been initted yet? */
+#define PERCPU_ISIDLING_OFFSET 0x03008 /* Is CPU in idle loop spinning? */
#endif /* !(_SPARC_VADDRS_H) */
--- /dev/null
+/* viking.h: Defines specific to the TI Viking MBUS module.
+ * This is SRMMU stuff.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+#ifndef _SPARC_VIKING_H
+#define _SPARC_VIKING_H
+
+/* Bits in the SRMMU control register for TI Viking modules.
+ *
+ * -------------------------------------------------------------
+ * |implvers| RSV |DP|RSV|TC|AC|SP|BM|PC|MBM|SB|IC|DC|RSV|NF|ME|
+ * -------------------------------------------------------------
+ * 31 24 23-20 19 18 17 16 15 14 13 12 11 10 9 8-2 1 0
+ *
+ * DP: Data Prefetcher Enable -- 0 = DP is off, 1 = DP is on
+ * TC: Tablewalk Cacheable -- 0 = Twalks are not cacheable
+ * 1 = Twalks are cacheable
+ * AC: Alternate Cacheable -- 0 = Direct physical accesses not cacheable
+ * 1 = Direct physical accesses are cacheable
+ * SP: SnooP Enable -- 0 = bus snooping off, 1 = bus snooping on
+ * BM: Boot Mode -- 0 = not in boot mode, 1 = in boot mode
+ * MBM: MBus Mode -- 0 = not in MBus mode, 1 = in MBus mode
+ * SB: StoreBuffer enable -- 0 = store buffer off, 1 = store buffer on
+ * IC: Instruction Cache -- 0 = off, 1 = on
+ * DC: Data Cache -- 0 = off, 1 = 0n
+ * NF: No Fault -- 0 = faults generate traps, 1 = faults don't trap
+ * ME: MMU enable -- 0 = mmu not translating, 1 = mmu translating
+ *
+ */
+
+#define VIKING_DCENABLE 0x00000100 /* Enable data cache */
+#define VIKING_ICENABLE 0x00000200 /* Enable instruction cache */
+#define VIKING_SBENABLE 0x00000400 /* Enable store buffer */
+#define VIKING_MMODE 0x00000800 /* MBUS mode */
+#define VIKING_PCENABLE 0x00001000 /* Enable parity checking */
+
+/* Boot mode, 0 at boot-time, 1 after prom initializes the MMU. */
+#define VIKING_BMODE 0x00002000
+#define VIKING_SPENABLE 0x00004000 /* Enable bus cache snooping */
+
+/* The deal with this AC bit is that if you are going to modify the
+ * contents of physical ram using the MMU bypass, you had better set
+ * this bit or things will get unsynchronized. This is only applicable
+ * if an E-cache (ie. a PAC) is around and the Viking is not in MBUS mode.
+ */
+#define VIKING_ACENABLE 0x00008000 /* Enable alternate caching */
+#define VIKING_TCENABLE 0x00010000 /* Enable table-walks to be cached */
+#define VIKING_DPENABLE 0x00040000 /* Enable the data prefetcher */
+
+#endif
512 : 128 << FD_SIZECODE(floppy) )
#define FD_PERP 0x40
+#define FD_STRETCH 1
+#define FD_SWAPSIDES 2
+
#ifndef __ASSEMBLY__
/* the following structure is used by FDSETPRM, FDDEFPRM and FDGETPRM */
struct floppy_struct {
int fd_device;
int last_checked; /* when was the drive last checked for a disk change? */
-
+ char *dmabuf;
+ int bufblocks;
};
struct floppy_write_errors {
* This allows the kernel to read directories into kernel space or
* to have different dirent layouts depending on the binary type.
*/
-typedef int (*filldir_t)(void *, char *, int, off_t, ino_t);
+typedef int (*filldir_t)(void *, const char *, int, off_t, ino_t);
struct file_operations {
int (*lseek) (struct inode *, struct file *, off_t, int);
extern int nr_files;
extern struct super_block super_blocks[NR_SUPER];
-extern int shrink_buffers(unsigned int priority);
+extern int shrink_buffers(unsigned int priority, unsigned long limit);
extern void refile_buffer(struct buffer_head * buf);
extern void set_writetime(struct buffer_head * buf, int flag);
extern void refill_freelist(int size);
* goes to clearing the page. If you want a page without the clearing
* overhead, just use __get_free_page() directly..
*/
-#define __get_free_page(priority) __get_free_pages((priority),0)
-extern unsigned long __get_free_pages(int priority, unsigned long gfporder);
-extern unsigned long __get_dma_pages(int priority, unsigned long gfporder);
+#define __get_free_page(priority) __get_free_pages((priority),0,~0UL)
+#define __get_dma_pages(priority, order) __get_free_pages((priority),(order),MAX_DMA_ADDRESS)
+extern unsigned long __get_free_pages(int priority, unsigned long gfporder, unsigned long max_addr);
+
extern inline unsigned long get_free_page(int priority)
{
unsigned long page;
/*
* The first word of the module contains the use count.
*/
-#define GET_USE_COUNT(module) (* (int *) (module)->addr)
+#define GET_USE_COUNT(module) (* (long *) (module)->addr)
/*
* define the count variable, and usage macros.
*/
#define MT_ISSCSI1 0x71 /* Generic ANSI SCSI-1 tape unit */
#define MT_ISSCSI2 0x72 /* Generic ANSI SCSI-2 tape unit */
-/* QIC-40/QIC-80 ftape supported drives.
- * 20bit vendor ID + 0x800000
+/* QIC-40/80/3010/3020 ftape supported drives.
+ * 20bit vendor ID + 0x800000 (see vendors.h in ftape distribution)
*/
-#define MT_ISFTAPE_UNKNOWN 0x800000
-#define MT_ISCMSDJ10_DJ20 0x800047
-#define MT_ISCMSDJ10_DJ20_NEW 0x8011c4
-#define MT_ISARCHIVE_5580I 0x800005
-#define MT_ISARCHIVE_XL9250I 0x80014a
-#define MT_ISARCHIVE_31250Q 0x800146
-#define MT_ISINSIGHT_80 0x810005
-#define MT_ISCONNER_C250MQT 0x80014c
-#define MT_ISWANGTEK_2040F 0x8001c1
-#define MT_ISWANGTEK_2080F 0x8001c8
-#define MT_ISIOMEGA_250 0x808880
-#define MT_ISSUMMIT_SE150 0x800180
-#define MT_ISSUMMIT_SE250 0x800181
-#define MT_ISESCOM_IDTBU120E 0x800140
+#define MT_ISFTAPE_UNKNOWN 0x800000 /* obsolete */
+#define MT_ISFTAPE_FLAG 0x800000
struct mt_tape_info {
long t_type; /* device type id (mt_type) */
{MT_ISEVEREX_FT40A, "Everex FT40A, QIC-40"}, \
{MT_ISSCSI1, "Generic SCSI-1 tape"}, \
{MT_ISSCSI2, "Generic SCSI-2 tape"}, \
- {MT_ISFTAPE_UNKNOWN, "Unknown floppy interface tape drive"},\
- {MT_ISCMSDJ10_DJ20, "Colorado DJ-10/DJ-20"},\
- {MT_ISCMSDJ10_DJ20_NEW, "Colorado DJ-10/DJ-20 (new)"},\
- {MT_ISARCHIVE_5580I, "Archive 5580i"},\
- {MT_ISARCHIVE_XL9250I, "Archive XL9250i [Conner/Escom]"},\
- {MT_ISARCHIVE_31250Q, "Escom/Archive 31250Q"},\
- {MT_ISINSIGHT_80, "Insight 80 Mb"},\
- {MT_ISCONNER_C250MQT, "Conner C250MQT"},\
- {MT_ISWANGTEK_2040F, "Wangtek 3040F"},\
- {MT_ISWANGTEK_2080F, "Wangtek 3080F"},\
- {MT_ISIOMEGA_250, "Iomega 250"},\
- {MT_ISSUMMIT_SE150, "Summit SE 150"},\
- {MT_ISSUMMIT_SE250, "Summit SE 250/Mountain FS8000"},\
- {MT_ISESCOM_IDTBU120E, "Identity IDTBU120E, Escom?"},\
{0, NULL} \
}
#define _LINUX_SBPCD_H
/*==========================================================================*/
/*==========================================================================*/
-#define LONG_TIMING 0 /* test against timeouts with "gold" CDs on CR-521 */
+#define LONG_TIMING 1 /* test against timeouts with "gold" CDs on CR-521 */
#undef FUTURE
#define TEST_UPC 0
-/* check.c 30/01/95 22.05.32 */
+/* check.c 23/01/95 03.38.30 */
void check_page_tables (void);
-/* dir.c 18/03/95 00.30.50 */
+/* dir.c 22/06/95 00.22.12 */
int UMSDOS_dir_read (struct inode *inode,
struct file *filp,
char *buf,
int len,
struct inode **result);
int umsdos_hlink2inode (struct inode *hlink, struct inode **result);
-/* emd.c 30/01/95 22.05.32 */
-int umsdos_readdir_kmem (struct inode *inode,
- struct file *filp,
- struct dirent *dirent,
- int count);
+/* emd.c 22/06/95 00.22.04 */
int umsdos_file_read_kmem (struct inode *inode,
struct file *filp,
char *buf,
int umsdos_findentry (struct inode *dir,
struct umsdos_info *info,
int expect);
-/* file.c 30/01/95 22.05.56 */
-/* inode.c 25/02/95 09.21.46 */
+/* file.c 25/01/95 02.25.38 */
+/* inode.c 12/06/95 09.49.40 */
void UMSDOS_put_inode (struct inode *inode);
void UMSDOS_put_super (struct super_block *sb);
-void UMSDOS_statfs (struct super_block *sb, struct statfs *buf, int);
+void UMSDOS_statfs (struct super_block *sb,
+ struct statfs *buf,
+ int bufsiz);
int umsdos_real_lookup (struct inode *dir,
const char *name,
int len,
struct super_block *UMSDOS_read_super (struct super_block *s,
void *data,
int silent);
-/* ioctl.c 21/02/95 20.58.22 */
+/* ioctl.c 22/06/95 00.22.08 */
int UMSDOS_ioctl_dir (struct inode *dir,
struct file *filp,
unsigned int cmd,
unsigned long data);
-/* mangle.c 30/01/95 22.05.56 */
+/* mangle.c 25/01/95 02.25.38 */
void umsdos_manglename (struct umsdos_info *info);
int umsdos_evalrecsize (int len);
int umsdos_parse (const char *fname, int len, struct umsdos_info *info);
-/* namei.c 30/01/95 22.05.56 */
+/* namei.c 25/01/95 02.25.38 */
void umsdos_lockcreate (struct inode *dir);
void umsdos_startlookup (struct inode *dir);
void umsdos_unlockcreate (struct inode *dir);
struct inode *new_dir,
const char *new_name,
int new_len);
-/* rdir.c 18/03/95 00.30.18 */
+/* rdir.c 22/03/95 03.31.42 */
int umsdos_rlookup_x (struct inode *dir,
const char *name,
int len,
const char *name,
int len,
struct inode **result);
-/* symlink.c 30/01/95 22.05.32 */
+/* symlink.c 23/01/95 03.38.30 */
struct options *opt;
volatile unsigned long wmem_alloc;
volatile unsigned long rmem_alloc;
- unsigned long write_seq;
- unsigned long sent_seq;
- unsigned long acked_seq;
- unsigned long copied_seq;
- unsigned long rcv_ack_seq;
- unsigned long window_seq;
- unsigned long fin_seq;
- unsigned long urg_seq;
- unsigned long urg_data;
+ __u32 write_seq;
+ __u32 sent_seq;
+ __u32 acked_seq;
+ __u32 copied_seq;
+ __u32 rcv_ack_seq;
+ __u32 window_seq;
+ __u32 fin_seq;
+ __u32 urg_seq;
+ __u32 urg_data;
/*
* Not all are volatile, but some are, so we
* and worry about wraparound (automatic with unsigned arithmetic).
*/
-extern __inline int before(unsigned long seq1, unsigned long seq2)
+extern __inline int before(__u32 seq1, __u32 seq2)
{
- return (long)(seq1-seq2) < 0;
+ return (__s32)(seq1-seq2) < 0;
}
-extern __inline int after(unsigned long seq1, unsigned long seq2)
+extern __inline int after(__u32 seq1, __u32 seq2)
{
- return (long)(seq1-seq2) > 0;
+ return (__s32)(seq1-seq2) > 0;
}
/* is s2<=s1<=s3 ? */
-extern __inline int between(unsigned long seq1, unsigned long seq2, unsigned long seq3)
+extern __inline int between(__u32 seq1, __u32 seq2, __u32 seq3)
{
return (after(seq1+1, seq2) && before(seq1, seq3+1));
}
extern void generic_NCR5380_setup(char *str, int *intr);
extern void aha152x_setup(char *str, int *ints);
extern void aha1542_setup(char *str, int *ints);
-extern void aha274x_setup(char *str, int *ints);
+extern void aic7xxx_setup(char *str, int *ints);
extern void buslogic_setup(char *str, int *ints);
extern void scsi_luns_setup(char *str, int *ints);
extern void sound_setup(char *str, int *ints);
#ifdef CONFIG_SCSI_AHA1542
{ "aha1542=", aha1542_setup},
#endif
-#ifdef CONFIG_SCSI_AHA274X
- { "aha274x=", aha274x_setup},
+#ifdef CONFIG_SCSI_AIC7XXX
+ { "aic7xxx=", aic7xxx_setup},
#endif
#ifdef CONFIG_SCSI_BUSLOGIC
{ "buslogic=", buslogic_setup},
err = verify_area (VERIFY_READ, msgp->mtext, msgsz);
if (err)
return err;
- if ((mtype = get_fs_long (&msgp->mtype)) < 1)
+ if ((mtype = get_user (&msgp->mtype)) < 1)
return -EINVAL;
id = (unsigned int) msqid % MSGMNI;
msq = msgque [id];
msq->msg_cbytes -= nmsg->msg_ts;
if (msq->wwait)
wake_up (&msq->wwait);
- put_fs_long (nmsg->msg_type, &msgp->mtype);
+ put_user (nmsg->msg_type, &msgp->mtype);
memcpy_tofs (msgp->mtext, nmsg->msg_spot, msgsz);
kfree(nmsg);
return msgsz;
static unsigned long swap_id = 0; /* currently being swapped */
static unsigned long swap_idx = 0; /* next to swap */
-int shm_swap (int prio)
+int shm_swap (int prio, unsigned long limit)
{
pte_t page;
struct shmid_ds *shp;
pte_val(page) = shp->shm_pages[idx];
if (!pte_present(page))
goto check_table;
+ if (pte_page(page) >= limit)
+ goto check_table;
swap_attempts++;
if (--counter < 0) { /* failed */
pte_t *page_table, pte;
unsigned long tmp;
- if (SWP_OFFSET(shmd->vm_pte) & SHM_ID_MASK != id) {
+ if ((SWP_OFFSET(shmd->vm_pte) & SHM_ID_MASK) != id) {
printk ("shm_swap: id=%ld does not match shmd->vm_pte.id=%ld\n",
id, SWP_OFFSET(shmd->vm_pte) & SHM_ID_MASK);
continue;
return;
}
-int shm_swap (int prio)
+int shm_swap (int prio, unsigned long limit)
{
return 0;
}
* Copyright (C) 1991, 1992 Linus Torvalds
*/
-#define DEBUG_PROC_TREE
+#undef DEBUG_PROC_TREE
#include <linux/wait.h>
#include <linux/errno.h>
if (!(options & WUNTRACED) && !(p->flags & PF_PTRACED))
continue;
if (stat_addr)
- put_fs_long((p->exit_code << 8) | 0x7f,
+ put_user((p->exit_code << 8) | 0x7f,
stat_addr);
p->exit_code = 0;
if (ru != NULL)
getrusage(p, RUSAGE_BOTH, ru);
flag = p->pid;
if (stat_addr)
- put_fs_long(p->exit_code, stat_addr);
+ put_user(p->exit_code, stat_addr);
if (p->p_opptr != p->p_pptr) {
REMOVE_LINKS(p);
p->p_pptr = p->p_opptr;
#endif
#include <asm/irq.h>
-extern char floppy_track_buffer[];
+extern char *get_options(char *str, int *ints);
extern void set_device_ro(int dev,int flag);
extern struct file_operations * get_blkfops(unsigned int);
/* stackable module support */
X(rename_module_symbol),
X(register_symtab),
+ X(get_options),
/* system info variables */
/* These check that they aren't defines (0/1) */
/* The next labels are needed for ftape driver. */
X(ftape_big_buffer),
#endif
- X(floppy_track_buffer),
#ifdef CONFIG_INET
/* support for loadable net drivers */
X(register_netdev),
}
strcpy((char *)(mp + 1), name); /* why not? */
- npages = (size + sizeof (int) + 4095) / 4096;
- if ((addr = vmalloc(npages * 4096)) == 0) {
+ npages = (size + sizeof (long) + PAGE_SIZE - 1) / PAGE_SIZE;
+ if ((addr = vmalloc(npages * PAGE_SIZE)) == 0) {
kfree_s(mp, sspace);
return -ENOMEM;
}
mp->state = MOD_UNINITIALIZED;
mp->cleanup = NULL;
- * (int *) addr = 0; /* set use count to zero */
+ * (long *) addr = 0; /* set use count to zero */
module_list = mp; /* link it in */
PRINTK(("module `%s' (%lu pages @ 0x%08lx) created\n",
if (!suser())
return -EPERM;
+#ifdef __i386__
/* A little bit of protection... we "know" where the user stack is... */
+
if (symtab && ((unsigned long)symtab > 0xb0000000)) {
printk("warning: you are using an old insmod, no symbols will be inserted!\n");
symtab = NULL;
}
+#endif
/*
* First reclaim any memory from dead modules that where not
memcpy_fromfs(&rt, routines, sizeof rt);
if ((mp = find_module(name)) == NULL)
return -ENOENT;
- if ((codesize + sizeof (int) + 4095) / 4096 > mp->size)
+ if ((codesize + sizeof (long) + PAGE_SIZE - 1) / PAGE_SIZE > mp->size)
return -EINVAL;
- memcpy_fromfs((char *)mp->addr + sizeof (int), code, codesize);
- memset((char *)mp->addr + sizeof (int) + codesize, 0,
- mp->size * 4096 - (codesize + sizeof (int)));
+ memcpy_fromfs((char *)mp->addr + sizeof (long), code, codesize);
+ memset((char *)mp->addr + sizeof (long) + codesize, 0,
+ mp->size * PAGE_SIZE - (codesize + sizeof (long)));
PRINTK(( "module init entry = 0x%08lx, cleanup entry = 0x%08lx\n",
(unsigned long) rt.init, (unsigned long) rt.cleanup));
mp->cleanup = rt.cleanup;
int i;
int legal_start;
- if ((error = verify_area(VERIFY_READ, symtab, sizeof(int))))
+ if ((error = verify_area(VERIFY_READ, &symtab->size, sizeof(symtab->size))))
return error;
- memcpy_fromfs((char *)(&(size)), symtab, sizeof(int));
+ size = get_user(&symtab->size);
if ((newtab = (struct symbol_table*) kmalloc(size, GFP_KERNEL)) == NULL) {
return -ENOMEM;
int i;
i = 0;
- for (i = 0 ; (buf[i] = get_fs_byte(user_name + i)) != '\0' ; ) {
+ for (i = 0 ; (buf[i] = get_user(user_name + i)) != '\0' ; ) {
if (++i >= MOD_MAX_NAME)
return -E2BIG;
}
* - For a loadable module, the function should only be called in the
* context of init_module
*
- * Those are the only restrictions! (apart from not being reenterable...)
+ * Those are the only restrictions! (apart from not being reentrant...)
*
* If you want to remove a symbol table for a loadable module,
* the call looks like: "register_symtab(0)".
log_size--;
log_start &= LOG_BUF_LEN-1;
sti();
- put_fs_byte(c,buf);
+ put_user(c,buf);
buf++;
i++;
cli();
j = log_start + log_size - count;
for (i = 0; i < count; i++) {
c = *((char *) log_buf+(j++ & (LOG_BUF_LEN-1)));
- put_fs_byte(c, buf++);
+ put_user(c, buf++);
}
if (do_clear)
logged_chars = 0;
error = verify_area(VERIFY_READ, set, sizeof(sigset_t));
if (error)
return error;
- new_set = get_fs_long((unsigned long *) set) & _BLOCKABLE;
+ new_set = get_user(set) & _BLOCKABLE;
switch (how) {
case SIG_BLOCK:
current->blocked |= new_set;
error = verify_area(VERIFY_WRITE, oset, sizeof(sigset_t));
if (error)
return error;
- put_fs_long(old_set, (unsigned long *) oset);
+ put_user(old_set, oset);
}
return 0;
}
/* fill in "set" with signals pending but blocked. */
error = verify_area(VERIFY_WRITE, set, 4);
if (!error)
- put_fs_long(current->blocked & current->signal, (unsigned long *)set);
+ put_user(current->blocked & current->signal, set);
return error;
}
int error = verify_area(VERIFY_WRITE,tbuf,sizeof *tbuf);
if (error)
return error;
- put_fs_long(current->utime,(unsigned long *)&tbuf->tms_utime);
- put_fs_long(current->stime,(unsigned long *)&tbuf->tms_stime);
- put_fs_long(current->cutime,(unsigned long *)&tbuf->tms_cutime);
- put_fs_long(current->cstime,(unsigned long *)&tbuf->tms_cstime);
+ 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);
}
return jiffies;
}
if (gidsetsize > NGROUPS)
return -EINVAL;
for (i = 0; i < gidsetsize; i++, grouplist++) {
- current->groups[i] = get_fs_word((unsigned short *) grouplist);
+ current->groups[i] = get_user(grouplist);
}
if (i < NGROUPS)
current->groups[i] = NOGROUP;
if (error)
return error;
memcpy_tofs(&name->sysname,&system_utsname.sysname,__OLD_UTS_LEN);
- put_fs_byte(0,name->sysname+__OLD_UTS_LEN);
+ put_user(0,name->sysname+__OLD_UTS_LEN);
memcpy_tofs(&name->nodename,&system_utsname.nodename,__OLD_UTS_LEN);
- put_fs_byte(0,name->nodename+__OLD_UTS_LEN);
+ put_user(0,name->nodename+__OLD_UTS_LEN);
memcpy_tofs(&name->release,&system_utsname.release,__OLD_UTS_LEN);
- put_fs_byte(0,name->release+__OLD_UTS_LEN);
+ put_user(0,name->release+__OLD_UTS_LEN);
memcpy_tofs(&name->version,&system_utsname.version,__OLD_UTS_LEN);
- put_fs_byte(0,name->version+__OLD_UTS_LEN);
+ put_user(0,name->version+__OLD_UTS_LEN);
memcpy_tofs(&name->machine,&system_utsname.machine,__OLD_UTS_LEN);
- put_fs_byte(0,name->machine+__OLD_UTS_LEN);
+ put_user(0,name->machine+__OLD_UTS_LEN);
return 0;
}
if (len > __NEW_UTS_LEN)
return -EINVAL;
for (i=0; i < len; i++) {
- if ((system_utsname.domainname[i] = get_fs_byte(name+i)) == 0)
+ if ((system_utsname.domainname[i] = get_user(name+i)) == 0)
return 0;
}
system_utsname.domainname[i] = 0;
*/
struct timezone sys_tz = { 0, 0};
-asmlinkage int sys_time(long * tloc)
+asmlinkage int sys_time(int * tloc)
{
int i, error;
i = CURRENT_TIME;
if (tloc) {
- error = verify_area(VERIFY_WRITE, tloc, 4);
+ error = verify_area(VERIFY_WRITE, tloc, sizeof(*tloc));
if (error)
return error;
- put_fs_long(i,(unsigned long *)tloc);
+ put_user(i,tloc);
}
return i;
}
-asmlinkage int sys_stime(unsigned long * tptr)
+asmlinkage int sys_stime(int * tptr)
{
- int error;
- unsigned long value;
+ int error, value;
if (!suser())
return -EPERM;
error = verify_area(VERIFY_READ, tptr, sizeof(*tptr));
if (error)
return error;
- value = get_fs_long(tptr);
+ value = get_user(tptr);
cli();
xtime.tv_sec = value;
xtime.tv_usec = 0;
*/
#include <linux/mm.h>
-#include <asm/system.h>
#include <linux/delay.h>
+#include <asm/system.h>
+#include <asm/dma.h>
#define GFP_LEVEL_MASK 0xf
sz = BLOCKSIZE(order); /* sz is the size of the blocks we're dealing with */
/* This can be done with ints on: This is private to this invocation */
- if (dma_flag)
- page = (struct page_descriptor *) __get_dma_pages (priority & GFP_LEVEL_MASK, sizes[order].gfporder);
- else
- page = (struct page_descriptor *) __get_free_pages (priority & GFP_LEVEL_MASK, sizes[order].gfporder);
+ {
+ unsigned long max_addr = ~0UL;
+ if (dma_flag)
+ max_addr = MAX_DMA_ADDRESS;
+ page = (struct page_descriptor *) __get_free_pages (priority & GFP_LEVEL_MASK,
+ sizes[order].gfporder, max_addr);
+ }
if (!page) {
static unsigned long last = 0;
}
}
-asmlinkage int sys_mmap(unsigned long *buffer)
-{
- int error;
- unsigned long flags;
- struct file * file = NULL;
-
- error = verify_area(VERIFY_READ, buffer, 6*sizeof(long));
- if (error)
- return error;
- flags = get_fs_long(buffer+3);
- if (!(flags & MAP_ANONYMOUS)) {
- unsigned long fd = get_fs_long(buffer+4);
- if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
- return -EBADF;
- }
- return do_mmap(file, get_fs_long(buffer), get_fs_long(buffer+1),
- get_fs_long(buffer+2), flags, get_fs_long(buffer+5));
-}
-
-
/*
* Searching a VMA in the linear list task->mm->mmap is horribly slow.
* Use an AVL (Adelson-Velskii and Landis) tree to speed up this search
int next; /* next entry on swap list */
} swap_info[MAX_SWAPFILES];
-extern int shm_swap (int);
+extern int shm_swap (int, unsigned long);
unsigned long *swap_cache;
* using a process that no longer actually exists (it might
* have died while we slept).
*/
-static inline int try_to_swap_out(struct vm_area_struct* vma, unsigned long address, pte_t * page_table)
+static inline int try_to_swap_out(struct vm_area_struct* vma, unsigned long address, pte_t * page_table, unsigned long limit)
{
pte_t pte;
unsigned long entry;
page = pte_page(pte);
if (page >= high_memory)
return 0;
+ if (page >= limit)
+ return 0;
if (mem_map[MAP_NR(page)] & MAP_PAGE_RESERVED)
return 0;
if ((pte_dirty(pte) && delete_from_swap_cache(page)) || pte_young(pte)) {
#define SWAP_RATIO 128
static inline int swap_out_pmd(struct vm_area_struct * vma, pmd_t *dir,
- unsigned long address, unsigned long end)
+ unsigned long address, unsigned long end, unsigned long limit)
{
pte_t * pte;
unsigned long pmd_end;
do {
int result;
vma->vm_task->mm->swap_address = address + PAGE_SIZE;
- result = try_to_swap_out(vma, address, pte);
+ result = try_to_swap_out(vma, address, pte, limit);
if (result)
return result;
address += PAGE_SIZE;
}
static inline int swap_out_pgd(struct vm_area_struct * vma, pgd_t *dir,
- unsigned long address, unsigned long end)
+ unsigned long address, unsigned long end, unsigned long limit)
{
pmd_t * pmd;
unsigned long pgd_end;
end = pgd_end;
do {
- int result = swap_out_pmd(vma, pmd, address, end);
+ int result = swap_out_pmd(vma, pmd, address, end, limit);
if (result)
return result;
address = (address + PMD_SIZE) & PMD_MASK;
}
static int swap_out_vma(struct vm_area_struct * vma, pgd_t *pgdir,
- unsigned long start)
+ unsigned long start, unsigned long limit)
{
unsigned long end;
end = vma->vm_end;
while (start < end) {
- int result = swap_out_pgd(vma, pgdir, start, end);
+ int result = swap_out_pgd(vma, pgdir, start, end, limit);
if (result)
return result;
start = (start + PGDIR_SIZE) & PGDIR_MASK;
return 0;
}
-static int swap_out_process(struct task_struct * p)
+static int swap_out_process(struct task_struct * p, unsigned long limit)
{
unsigned long address;
struct vm_area_struct* vma;
address = vma->vm_start;
for (;;) {
- int result = swap_out_vma(vma, pgd_offset(p, address), address);
+ int result = swap_out_vma(vma, pgd_offset(p, address), address, limit);
if (result)
return result;
vma = vma->vm_next;
return 0;
}
-static int swap_out(unsigned int priority)
+static int swap_out(unsigned int priority, unsigned long limit)
{
static int swap_task;
int loop, counter;
}
if (!--p->mm->swap_cnt)
swap_task++;
- switch (swap_out_process(p)) {
+ switch (swap_out_process(p, limit)) {
case 0:
if (p->mm->swap_cnt)
swap_task++;
* free'd, so hopefully we'll get reasonable behaviour even under very
* different circumstances.
*/
-static int try_to_free_page(int priority)
+static int try_to_free_page(int priority, unsigned long limit)
{
static int state = 0;
int i=6;
switch (state) {
do {
case 0:
- if (priority != GFP_NOBUFFER && shrink_buffers(i))
+ if (priority != GFP_NOBUFFER && shrink_buffers(i, limit))
return 1;
state = 1;
case 1:
- if (shm_swap(i))
+ if (shm_swap(i, limit))
return 1;
state = 2;
default:
- if (swap_out(i))
+ if (swap_out(i, limit))
return 1;
state = 0;
} while(i--);
/*
* Some ugly macros to speed up __get_free_pages()..
*/
-#define RMQUEUE(order) \
+#define RMQUEUE(order, limit) \
do { struct mem_list * queue = free_area_list+order; \
unsigned long new_order = order; \
- do { struct mem_list *next = queue->next; \
- if (queue != next) { \
- (queue->next = next->next)->prev = queue; \
- mark_used((unsigned long) next, new_order); \
- nr_free_pages -= 1 << order; \
- restore_flags(flags); \
- EXPAND(next, order, new_order); \
- return (unsigned long) next; \
- } new_order++; queue++; \
+ do { struct mem_list *prev = queue, *ret; \
+ while (queue != (ret = prev->next)) { \
+ if ((unsigned long) ret < (limit)) { \
+ (prev->next = ret->next)->prev = prev; \
+ mark_used((unsigned long) ret, new_order); \
+ nr_free_pages -= 1 << order; \
+ restore_flags(flags); \
+ EXPAND(ret, order, new_order); \
+ return (unsigned long) ret; \
+ } \
+ prev = ret; \
+ } \
+ new_order++; queue++; \
} while (new_order < NR_MEM_LISTS); \
} while (0)
} mem_map[MAP_NR((unsigned long) addr)] = 1; \
} while (0)
-unsigned long __get_free_pages(int priority, unsigned long order)
+unsigned long __get_free_pages(int priority, unsigned long order, unsigned long limit)
{
unsigned long flags;
int reserved_pages;
repeat:
cli();
if ((priority==GFP_ATOMIC) || nr_free_pages > reserved_pages) {
- RMQUEUE(order);
+ RMQUEUE(order, limit);
restore_flags(flags);
return 0;
}
restore_flags(flags);
- if (priority != GFP_BUFFER && try_to_free_page(priority))
+ if (priority != GFP_BUFFER && try_to_free_page(priority, limit))
goto repeat;
return 0;
}
-/*
- * Yes, I know this is ugly. Don't tell me.
- */
-unsigned long __get_dma_pages(int priority, unsigned long order)
-{
- unsigned long list = 0;
- unsigned long result;
- unsigned long limit = MAX_DMA_ADDRESS;
-
- /* if (EISA_bus) limit = ~0UL; */
- if (priority != GFP_ATOMIC)
- priority = GFP_BUFFER;
- for (;;) {
- result = __get_free_pages(priority, order);
- if (result < limit) /* covers failure as well */
- break;
- *(unsigned long *) result = list;
- list = result;
- }
- while (list) {
- unsigned long tmp = list;
- list = *(unsigned long *) list;
- free_pages(tmp, order);
- }
- return result;
-}
-
/*
* Show free area list (used inside shift_scroll-lock stuff)
* We also calculate the percentage fragmentation. We do this by counting the
while (addr < vaddr) {
if (count == 0)
goto finished;
- put_fs_byte('\0', buf++), addr++, count--;
+ put_user('\0', buf++), addr++, count--;
}
n = tmp->size - PAGE_SIZE;
if (addr > vaddr)
while (--n >= 0) {
if (count == 0)
goto finished;
- put_fs_byte(*addr++, buf++), count--;
+ put_user(*addr++, buf++), count--;
}
}
finished:
err=verify_area(VERIFY_WRITE,optlen,sizeof(int));
if(err)
return err;
- put_fs_long(sizeof(int),(unsigned long *)optlen);
+ put_user(sizeof(int),optlen);
err=verify_area(VERIFY_WRITE,optval,sizeof(int));
- put_fs_long(val,(unsigned long *)optval);
+ put_user(val,optval);
return(0);
}
if ((err = verify_area(VERIFY_WRITE, optlen, sizeof(int))) != 0)
return err;
- put_fs_long(sizeof(int), (unsigned long *)optlen);
+ put_user(sizeof(int), optlen);
if ((err = verify_area(VERIFY_WRITE, optval, sizeof(int))) != 0)
return err;
- put_fs_long(val, (unsigned long *)optval);
+ put_user(val, optval);
return 0;
}
return;
/* This is meaningless in raw sockets. */
- if (err & 0xff00 == (ICMP_SOURCE_QUENCH << 8))
+ if ((err & 0xff00) == (ICMP_SOURCE_QUENCH << 8))
{
if (sk->cong_window > 1) sk->cong_window = sk->cong_window/2;
return;
* This routine sends an ack and also updates the window.
*/
-static void tcp_send_ack(unsigned long sequence, unsigned long ack,
+static void tcp_send_ack(u32 sequence, u32 ack,
struct sock *sk,
struct tcphdr *th, unsigned long daddr)
{
t1->doff = sizeof(*t1)/4;
tcp_send_check(t1, sk->saddr, daddr, sizeof(*t1), sk);
if (sk->debug)
- printk("\rtcp_ack: seq %lx ack %lx\n", sequence, ack);
+ printk("\rtcp_ack: seq %x ack %x\n", sequence, ack);
tcp_statistics.TcpOutSegs++;
sk->prot->queue_xmit(sk, dev, buff, 1);
}
{
struct wait_queue wait = { current, NULL };
int copied = 0;
- unsigned long peek_seq;
- volatile unsigned long *seq; /* So gcc doesn't overoptimise */
+ u32 peek_seq;
+ volatile u32 *seq; /* So gcc doesn't overoptimise */
unsigned long used;
/*
* That's funny, Linux has one built in! Use it!
*/
-extern inline unsigned long tcp_init_seq(void)
+extern inline u32 tcp_init_seq(void)
{
struct timeval tv;
do_gettimeofday(&tv);
static void tcp_conn_request(struct sock *sk, struct sk_buff *skb,
unsigned long daddr, unsigned long saddr,
- struct options *opt, struct device *dev, unsigned long seq)
+ struct options *opt, struct device *dev, u32 seq)
{
struct sk_buff *buff;
struct tcphdr *t1;
extern __inline__ int tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len)
{
- unsigned long ack;
+ u32 ack;
int flag = 0;
/*
if (after(ack, sk->sent_seq) || before(ack, sk->rcv_ack_seq))
{
if(sk->debug)
- printk("Ack ignored %lu %lu\n",ack,sk->sent_seq);
+ printk("Ack ignored %u %u\n",ack,sk->sent_seq);
/*
* Keepalive processing.
if (!sk->dead)
sk->state_change(sk);
if(sk->debug)
- printk("rcv_ack_seq: %lX==%lX, acked_seq: %lX==%lX\n",
+ printk("rcv_ack_seq: %X==%X, acked_seq: %X==%X\n",
sk->rcv_ack_seq,sk->write_seq,sk->acked_seq,sk->fin_seq);
if (sk->rcv_ack_seq == sk->write_seq /*&& sk->acked_seq == sk->fin_seq*/)
{
struct sk_buff *skb1, *skb2;
struct tcphdr *th;
int dup_dumped=0;
- unsigned long new_seq;
- unsigned long shut_seq;
+ u32 new_seq, shut_seq;
th = skb->h.th;
skb->len = len -(th->doff*4);
if(after(new_seq,shut_seq))
{
if(sk->debug)
- printk("Data arrived on %p after close [Data right edge %lX, Socket shut on %lX] %d\n",
+ printk("Data arrived on %p after close [Data right edge %X, Socket shut on %X] %d\n",
sk, new_seq, shut_seq, sk->blog);
if(sk->dead)
{
if(sk->debug)
{
printk("skb1=%p :", skb1);
- printk("skb1->h.th->seq = %ld: ", skb1->h.th->seq);
- printk("skb->h.th->seq = %ld\n",skb->h.th->seq);
- printk("copied_seq = %ld acked_seq = %ld\n", sk->copied_seq,
+ printk("skb1->h.th->seq = %d: ", skb1->h.th->seq);
+ printk("skb->h.th->seq = %d\n",skb->h.th->seq);
+ printk("copied_seq = %d acked_seq = %d\n", sk->copied_seq,
sk->acked_seq);
}
extern __inline__ int tcp_sequence(struct sock *sk, struct tcphdr *th, short len,
struct options *opt, unsigned long saddr, struct device *dev)
{
- unsigned long next_seq;
+ u32 next_seq;
next_seq = len - 4*th->doff;
if (th->fin)
if (sk->state == TCP_TIME_WAIT && th->syn && sk->dead &&
after(th->seq, sk->acked_seq) && !th->rst)
{
- long seq=sk->write_seq;
+ u32 seq = sk->write_seq;
if(sk->debug)
printk("Doing a BSD time wait\n");
tcp_statistics.TcpEstabResets++;
if (sk == NULL)
return; /* No socket for error */
- if (err & 0xff00 ==(ICMP_SOURCE_QUENCH << 8))
+ if ((err & 0xff00) == (ICMP_SOURCE_QUENCH << 8))
{ /* Slow down! */
if (sk->cong_window > 1)
sk->cong_window = sk->cong_window/2;
if((err=verify_area(VERIFY_WRITE,ulen,sizeof(*ulen)))<0)
return err;
- len=get_fs_long(ulen);
+ len=get_user(ulen);
if(len>klen)
len=klen;
if(len<0 || len> MAX_SOCK_ADDR)
return err;
memcpy_tofs(uaddr,kaddr,len);
}
- put_fs_long(len,ulen);
+ put_user(len,ulen);
return 0;
}
* Create a pair of connected sockets.
*/
-asmlinkage int sys_socketpair(int family, int type, int protocol, unsigned long usockvec[2])
+asmlinkage int sys_socketpair(int family, int type, int protocol, int usockvec[2])
{
int fd1, fd2, i;
struct socket *sock1, *sock2;
sock1->state = SS_CONNECTED;
sock2->state = SS_CONNECTED;
- er=verify_area(VERIFY_WRITE, usockvec, 2 * sizeof(int));
+ er=verify_area(VERIFY_WRITE, usockvec, sizeof(usockvec));
if(er)
{
sys_close(fd1);
sys_close(fd2);
return er;
}
- put_fs_long(fd1, &usockvec[0]);
- put_fs_long(fd2, &usockvec[1]);
+ put_user(fd1, &usockvec[0]);
+ put_user(fd2, &usockvec[1]);
return(0);
}
if(er)
return er;
- a0=get_fs_long(args);
- a1=get_fs_long(args+1);
+ a0=get_user(args);
+ a1=get_user(args+1);
switch(call)
{
case SYS_SOCKET:
- return(sys_socket(a0,a1,get_fs_long(args+2)));
+ return(sys_socket(a0,a1,get_user(args+2)));
case SYS_BIND:
return(sys_bind(a0,(struct sockaddr *)a1,
- get_fs_long(args+2)));
+ get_user(args+2)));
case SYS_CONNECT:
return(sys_connect(a0, (struct sockaddr *)a1,
- get_fs_long(args+2)));
+ get_user(args+2)));
case SYS_LISTEN:
return(sys_listen(a0,a1));
case SYS_ACCEPT:
return(sys_accept(a0,(struct sockaddr *)a1,
- (int *)get_fs_long(args+2)));
+ (int *)get_user(args+2)));
case SYS_GETSOCKNAME:
return(sys_getsockname(a0,(struct sockaddr *)a1,
- (int *)get_fs_long(args+2)));
+ (int *)get_user(args+2)));
case SYS_GETPEERNAME:
return(sys_getpeername(a0, (struct sockaddr *)a1,
- (int *)get_fs_long(args+2)));
+ (int *)get_user(args+2)));
case SYS_SOCKETPAIR:
return(sys_socketpair(a0,a1,
- get_fs_long(args+2),
- (unsigned long *)get_fs_long(args+3)));
+ get_user(args+2),
+ (int *)get_user(args+3)));
case SYS_SEND:
return(sys_send(a0,
(void *)a1,
- get_fs_long(args+2),
- get_fs_long(args+3)));
+ get_user(args+2),
+ get_user(args+3)));
case SYS_SENDTO:
return(sys_sendto(a0,(void *)a1,
- get_fs_long(args+2),
- get_fs_long(args+3),
- (struct sockaddr *)get_fs_long(args+4),
- get_fs_long(args+5)));
+ get_user(args+2),
+ get_user(args+3),
+ (struct sockaddr *)get_user(args+4),
+ get_user(args+5)));
case SYS_RECV:
return(sys_recv(a0,
(void *)a1,
- get_fs_long(args+2),
- get_fs_long(args+3)));
+ get_user(args+2),
+ get_user(args+3)));
case SYS_RECVFROM:
return(sys_recvfrom(a0,
(void *)a1,
- get_fs_long(args+2),
- get_fs_long(args+3),
- (struct sockaddr *)get_fs_long(args+4),
- (int *)get_fs_long(args+5)));
+ get_user(args+2),
+ get_user(args+3),
+ (struct sockaddr *)get_user(args+4),
+ (int *)get_user(args+5)));
case SYS_SHUTDOWN:
return(sys_shutdown(a0,a1));
case SYS_SETSOCKOPT:
return(sys_setsockopt(a0,
a1,
- get_fs_long(args+2),
- (char *)get_fs_long(args+3),
- get_fs_long(args+4)));
+ get_user(args+2),
+ (char *)get_user(args+3),
+ get_user(args+4)));
case SYS_GETSOCKOPT:
return(sys_getsockopt(a0,
a1,
- get_fs_long(args+2),
- (char *)get_fs_long(args+3),
- (int *)get_fs_long(args+4)));
+ get_user(args+2),
+ (char *)get_user(args+3),
+ (int *)get_user(args+4)));
}
return -EINVAL; /* to keep gcc happy */
}