diff -u --recursive --new-file v2.2.3/linux/CREDITS linux/CREDITS --- v2.2.3/linux/CREDITS Wed Mar 10 15:29:44 1999 +++ linux/CREDITS Wed Mar 10 21:49:10 1999 @@ -779,6 +779,15 @@ S: Mountain View, California 94043 S: USA +N: Benjamin Herrenschmidt +E: bh40@calva.net +E: benh@mipsys.com +D: PowerMac booter (BootX) +D: Additional PowerBook support +S: 22, rue des Marguettes +S: 75012 Paris +S: France + N: Sebastian Hetze E: she@lunetix.de D: German Linux Documentation, diff -u --recursive --new-file v2.2.3/linux/Documentation/Changes linux/Documentation/Changes --- v2.2.3/linux/Documentation/Changes Wed Jan 20 23:14:03 1999 +++ linux/Documentation/Changes Wed Mar 17 09:15:05 1999 @@ -579,7 +579,7 @@ ========== The 2.9 release: -ftp://ftp.win.tue.nl/pub/linux/util/util-linux-2.9g.tar.gz +ftp://ftp.win.tue.nl/pub/linux/utils/util-linux/util-linux-2.9g.tar.gz Autofs ====== diff -u --recursive --new-file v2.2.3/linux/Documentation/Configure.help linux/Documentation/Configure.help --- v2.2.3/linux/Documentation/Configure.help Wed Mar 10 15:29:44 1999 +++ linux/Documentation/Configure.help Sun Mar 21 07:22:00 1999 @@ -1399,6 +1399,10 @@ PC boards and vice versa. See Documentation/sgi-visws.txt for more. +SGI Visual Workstation framebuffer support +CONFIG_FB_SGIVW + SGI Visual Workstation support for framebuffer graphics. + System V IPC CONFIG_SYSVIPC Inter Process Communication is a suite of library functions and @@ -2247,6 +2251,12 @@ you say Y here, you will be able to specify different routes for packets with different TOS values. +IP: use FWMARK value as routing key +CONFIG_IP_ROUTE_FWMARK + If you say Y here, you will be able to specify different routes for + packets with different FWMARK ("firewalling mark") values + (see ipchains(8), "-m" argument). + IP: verbose route monitoring CONFIG_IP_ROUTE_VERBOSE If you say Y here, which is recommended, then the kernel will print @@ -5099,7 +5109,7 @@ *** networking options: especially CONFIG*FIREWALL. *** However, it will work with all options in CONFIG_IP_ADVANCED_ROUTER - section (except for CONFIG_IP_ROUTE_TOS). At the moment, few devices + section (except for CONFIG_IP_ROUTE_TOS&FWMARK). At the moment, few devices support fast switching (tulip is one of them, modified 8390 can be found at ftp://ftp.inr.ac.ru/ip-routing/fastroute-8390.tar.gz). diff -u --recursive --new-file v2.2.3/linux/Documentation/cdrom/cdrom-standard.tex linux/Documentation/cdrom/cdrom-standard.tex --- v2.2.3/linux/Documentation/cdrom/cdrom-standard.tex Tue Jan 19 11:32:50 1999 +++ linux/Documentation/cdrom/cdrom-standard.tex Sun Mar 21 18:37:56 1999 @@ -25,7 +25,7 @@ \author{David van Leeuwen\\{\normalsize\tt david@ElseWare.cistron.nl} \\{\footnotesize updated by Erik Andersen {\tt(andersee@debian.org)}} \\{\footnotesize updated by Jens Axboe {\tt(axboe@image.dk)}}} -\date{11 January 1999} +\date{12 March 1999} \maketitle @@ -549,7 +549,9 @@ CDROMREADAUDIO, CDROMREADRAW, CDROMREADCOOKED, CDROMSEEK, CDROMPLAY\-BLK and CDROM\-READALL}. + \subsection{\cdrom\ capabilities} +\label{capability} Instead of just implementing some $ioctl$ calls, the interface in \cdromc\ supplies the possibility to indicate the {\em capabilities\/} @@ -944,6 +946,13 @@ \item[CDROM_CHANGER_NSLOTS] Returns the number of slots in a juke-box. \item[CDROMRESET] Reset the drive. +\item[CDROM_GET_CAPABILITY] Returns the $capability$ flags for the + drive. Refer to section \ref{capability} for more information on + these flags. +\item[CDROM_LOCKDOOR] Locks the door of the drive. $arg == \rm0$ + unlocks the door, any other value locks it. +\item[CDROM_DEBUG] Turns on debugging info. Only root is allowed + to do this. Same semantics as CDROM_LOCKDOOR. \end{description} \subsubsection{Device dependent $ioctl$s} diff -u --recursive --new-file v2.2.3/linux/Documentation/filesystems/affs.txt linux/Documentation/filesystems/affs.txt --- v2.2.3/linux/Documentation/filesystems/affs.txt Wed Jun 24 22:54:01 1998 +++ linux/Documentation/filesystems/affs.txt Sun Mar 21 07:11:36 1999 @@ -151,6 +151,28 @@ /etc/fstab entry: /dev/sdb5 /amiga/Workbench affs noauto,user,exec,verbose 0 0 +IMPORTANT NOTE +============== + +If you boot Windows 95 (don't know about 3.x, 98 and NT) while you +have an Amiga harddisk connected to your PC, it will overwrite +the bytes 0x00dc..0x00df of block 0 with garbage, thus invalidating +the Rigid Disk Block. Sheer luck has it that this is an unused +area of the RDB, so only the checksum doesn's match anymore. +Linux will ignore this garbage and recognize the RDB anyway, but +before you connect that drive to your Amiga again, you must +restore or repair your RDB. So please do make a backup copy of it +before booting Windows! + +If the damage is already done, the following should fix the RDB +(where is the device name). +DO AT YOUR OWN RISK: + + dd if=/dev/ of=rdb.tmp count=1 + cp rdb.tmp rdb.fixed + dd if=/dev/zero of=rdb.fixed bs=1 seek=220 count=4 + dd if=rdb.fixed of=/dev/ + Bugs, Restrictions, Caveats =========================== @@ -185,9 +207,8 @@ no way to fix a garbled filesystem without an Amiga (disk validator) or manually (who would do this?). Maybe later. -A fsck.affs and mkfs.affs will probably be available in the future. -If you mount them on system startup, you may want to tell fsck -that the fs should not be checked (place a '0' in the sixth field +If you mount affs partitions on system startup, you may want to tell +fsck that the fs should not be checked (place a '0' in the sixth field of /etc/fstab). It's not possible to read floppy disks with a normal PC or workstation diff -u --recursive --new-file v2.2.3/linux/Documentation/powerpc/ppc_htab.txt linux/Documentation/powerpc/ppc_htab.txt --- v2.2.3/linux/Documentation/powerpc/ppc_htab.txt Thu Apr 23 20:21:27 1998 +++ linux/Documentation/powerpc/ppc_htab.txt Wed Mar 10 21:49:10 1999 @@ -114,10 +114,5 @@ 3. Bugs - Doing a 'less' or 'more' on ppc_htab results in a segmentation violation. - I'm not sure of the cause but in the mean time 'cat' works adequately for - reading the file. - The PMC1 and PMC2 counters can overflow and give no indication of that in /proc/ppc_htab. - diff -u --recursive --new-file v2.2.3/linux/MAINTAINERS linux/MAINTAINERS --- v2.2.3/linux/MAINTAINERS Wed Mar 10 15:29:44 1999 +++ linux/MAINTAINERS Wed Mar 10 18:11:11 1999 @@ -685,7 +685,7 @@ SPARC: P: David S. Miller -M: davem@dm.cobaltmicro.com +M: davem@redhat.com P: Eddie C. Dost M: ecd@skynet.be P: Jakub Jelinek diff -u --recursive --new-file v2.2.3/linux/Makefile linux/Makefile --- v2.2.3/linux/Makefile Wed Mar 10 15:29:44 1999 +++ linux/Makefile Fri Mar 12 16:22:27 1999 @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 2 -SUBLEVEL = 3 +SUBLEVEL = 4 EXTRAVERSION = ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/) diff -u --recursive --new-file v2.2.3/linux/arch/alpha/kernel/time.c linux/arch/alpha/kernel/time.c --- v2.2.3/linux/arch/alpha/kernel/time.c Wed Jan 20 23:14:04 1999 +++ linux/arch/alpha/kernel/time.c Thu Mar 11 23:24:50 1999 @@ -357,7 +357,6 @@ xtime = *tv; time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; - time_state = TIME_ERROR; /* p. 24, (a) */ time_maxerror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT; sti(); diff -u --recursive --new-file v2.2.3/linux/arch/arm/kernel/time.c linux/arch/arm/kernel/time.c --- v2.2.3/linux/arch/arm/kernel/time.c Wed Jan 20 23:14:04 1999 +++ linux/arch/arm/kernel/time.c Thu Mar 11 23:24:55 1999 @@ -127,7 +127,6 @@ xtime = *tv; time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; - time_state = TIME_ERROR; /* p. 24, (a) */ time_maxerror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT; sti (); diff -u --recursive --new-file v2.2.3/linux/arch/i386/kernel/bios32.c linux/arch/i386/kernel/bios32.c --- v2.2.3/linux/arch/i386/kernel/bios32.c Wed Mar 10 15:29:45 1999 +++ linux/arch/i386/kernel/bios32.c Sun Mar 21 07:24:54 1999 @@ -997,15 +997,15 @@ l != 0x0000 && l != 0xffff) { #ifdef CONFIG_PCI_BIOS if (pci_bios_present) { - int succ, idx = 0; + int err, idx = 0; u8 bios_bus, bios_dfn; u16 d; pcibios_read_config_word(n, i, PCI_DEVICE_ID, &d); DBG("BIOS test for %02x:%02x (%04x:%04x)\n", n, i, l, d); - while ((succ = pci_bios_find_device(l, d, idx, &bios_bus, &bios_dfn)) && + while (!(err = pci_bios_find_device(l, d, idx, &bios_bus, &bios_dfn)) && (bios_bus != n || bios_dfn != i)) idx++; - if (!succ) + if (err) break; } #endif diff -u --recursive --new-file v2.2.3/linux/arch/i386/kernel/i386_ksyms.c linux/arch/i386/kernel/i386_ksyms.c --- v2.2.3/linux/arch/i386/kernel/i386_ksyms.c Tue Feb 23 15:21:32 1999 +++ linux/arch/i386/kernel/i386_ksyms.c Wed Mar 10 15:07:19 1999 @@ -75,8 +75,11 @@ EXPORT_SYMBOL(cpu_data); EXPORT_SYMBOL(kernel_flag); EXPORT_SYMBOL(smp_invalidate_needed); +EXPORT_SYMBOL(cpu_number_map); EXPORT_SYMBOL(__cpu_logical_map); EXPORT_SYMBOL(smp_num_cpus); +EXPORT_SYMBOL(cpu_present_map); +EXPORT_SYMBOL(cpu_online_map); /* Global SMP irq stuff */ EXPORT_SYMBOL(synchronize_irq); diff -u --recursive --new-file v2.2.3/linux/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c --- v2.2.3/linux/arch/i386/kernel/process.c Wed Jan 20 23:14:04 1999 +++ linux/arch/i386/kernel/process.c Wed Mar 10 16:52:44 1999 @@ -316,7 +316,7 @@ /* Make sure the first page is mapped to the start of physical memory. It is normally not mapped, to trap kernel NULL pointer dereferences. */ - pg0[0] = 7; + pg0[0] = _PAGE_RW | _PAGE_PRESENT; /* * Use `swapper_pg_dir' as our page directory. We bother with diff -u --recursive --new-file v2.2.3/linux/arch/i386/kernel/ptrace.c linux/arch/i386/kernel/ptrace.c --- v2.2.3/linux/arch/i386/kernel/ptrace.c Wed Jan 13 15:00:41 1999 +++ linux/arch/i386/kernel/ptrace.c Wed Mar 10 16:51:34 1999 @@ -354,6 +354,7 @@ { struct task_struct *child; struct user * dummy = NULL; + unsigned long flags; int i, ret; lock_kernel(); @@ -385,21 +386,22 @@ (current->uid != child->uid) || (current->gid != child->egid) || (current->gid != child->sgid) || + (cap_issubset(child->cap_permitted, current->cap_permitted)) || (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE)) goto out; /* the same process cannot be attached many times */ if (child->flags & PF_PTRACED) goto out; child->flags |= PF_PTRACED; - if (child->p_pptr != current) { - unsigned long flags; - write_lock_irqsave(&tasklist_lock, flags); + write_lock_irqsave(&tasklist_lock, flags); + if (child->p_pptr != current) { REMOVE_LINKS(child); child->p_pptr = current; SET_LINKS(child); - write_unlock_irqrestore(&tasklist_lock, flags); } + write_unlock_irqrestore(&tasklist_lock, flags); + send_sig(SIGSTOP, child, 1); ret = 0; goto out; @@ -559,7 +561,6 @@ } case PTRACE_DETACH: { /* detach a process that was attached. */ - unsigned long flags; long tmp; ret = -EIO; diff -u --recursive --new-file v2.2.3/linux/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c --- v2.2.3/linux/arch/i386/kernel/setup.c Tue Feb 23 15:21:32 1999 +++ linux/arch/i386/kernel/setup.c Wed Mar 10 16:51:34 1999 @@ -381,7 +381,17 @@ } -__initfunc(static int amd_model(struct cpuinfo_x86 *c)) +#define rdmsr(msr,val1,val2) \ + __asm__ __volatile__("rdmsr" \ + : "=a" (val1), "=d" (val2) \ + : "c" (msr)) + +#define wrmsr(msr,val1,val2) \ + __asm__ __volatile__("wrmsr" \ + : /* no outputs */ \ + : "c" (msr), "a" (val1), "d" (val2)) + +__initfunc(static int get_model_name(struct cpuinfo_x86 *c)) { unsigned int n, dummy, *v; @@ -401,6 +411,77 @@ return 1; } +__initfunc(static int amd_model(struct cpuinfo_x86 *c)) +{ + u32 l, h; + unsigned long flags; + int mbytes = max_mapnr >> (20-PAGE_SHIFT); + + int r=get_model_name(c); + + /* + * Now do the cache operations. + */ + + switch(c->x86) + { + case 5: + if( c->x86_model < 6 ) + { + /* Anyone with a K5 want to fill this in */ + break; + } + + /* K6 with old style WHCR */ + if( c->x86_model < 8 || + (c->x86_model== 8 && c->x86_mask < 8)) + { + /* We can only write allocate on the low 508Mb */ + if(mbytes>508) + mbytes=508; + + rdmsr(0xC0000082, l, h); + if((l&0x0000FFFF)==0) + { + l=(1<<0)|(mbytes/4); + save_flags(flags); + __cli(); + __asm__ __volatile__ ("wbinvd": : :"memory"); + wrmsr(0xC0000082, l, h); + restore_flags(flags); + printk(KERN_INFO "Enabling old style K6 write allocation for %d Mb\n", + mbytes); + + } + break; + } + if (c->x86_model == 8 || c->x86_model == 9) + { + /* The more serious chips .. */ + + if(mbytes>4092) + mbytes=4092; + rdmsr(0xC0000082, l, h); + if((l&0xFFFF0000)==0) + { + mbytes>>=2; + l=(mbytes<<22)|(1<<16); + save_flags(flags); + __cli(); + __asm__ __volatile__ ("wbinvd": : :"memory"); + wrmsr(0xC0000082, l, h); + restore_flags(flags); + printk(KERN_INFO "Enabling new style K6 write allocation for %d Mb\n", + mbytes); + } + break; + } + break; + } + return r; +} + + /* * Read Cyrix DEVID registers (DIR) to get more detailed info. about the CPU */ @@ -517,7 +598,7 @@ /* GXm supports extended cpuid levels 'ala' AMD */ if (c->cpuid_level == 2) { - amd_model(c); /* get CPU marketing name */ + get_model_name(c); /* get CPU marketing name */ c->x86_capability&=~X86_FEATURE_TSC; return; } @@ -642,6 +723,20 @@ if (c->x86_vendor == X86_VENDOR_AMD && amd_model(c)) return; + + if (c->cpuid_level > 0 && c->x86_vendor == X86_VENDOR_INTEL) + { + if(c->x86_capability&(1<<18)) + { + /* Disable processor serial number on Intel Pentium III + from code by Phil Karn */ + unsigned long lo,hi; + rdmsr(0x119,lo,hi); + lo |= 0x200000; + wrmsr(0x119,lo,hi); + printk(KERN_INFO "Pentium-III serial number disabled.\n"); + } + } for (i = 0; i < sizeof(cpu_models)/sizeof(struct cpu_model_info); i++) { if (c->cpuid_level > 1) { @@ -726,15 +821,6 @@ } -#define rdmsr(msr,val1,val2) \ - __asm__ __volatile__("rdmsr" \ - : "=a" (val1), "=d" (val2) \ - : "c" (msr)) - -#define wrmsr(msr,val1,val2) \ - __asm__ __volatile__("wrmsr" \ - : /* no outputs */ \ - : "c" (msr), "a" (val1), "d" (val2)) static char *cpu_vendor_names[] __initdata = { "Intel", "Cyrix", "AMD", "UMC", "NexGen", "Centaur" }; @@ -785,8 +871,8 @@ static char *x86_cap_flags[] = { "fpu", "vme", "de", "pse", "tsc", "msr", "6", "mce", "cx8", "9", "10", "sep", "12", "pge", "14", "cmov", - "16", "17", "18", "19", "20", "21", "22", "mmx", - "24", "25", "26", "27", "28", "29", "30", "31" + "16", "17", "psn", "19", "20", "21", "22", "mmx", + "24", "kni", "26", "27", "28", "29", "30", "31" }; struct cpuinfo_x86 *c = cpu_data; int i, n; @@ -836,6 +922,7 @@ x86_cap_flags[14] = "mca"; x86_cap_flags[16] = "pat"; x86_cap_flags[17] = "pse36"; + x86_cap_flags[18] = "psn"; x86_cap_flags[24] = "osfxsr"; } diff -u --recursive --new-file v2.2.3/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c --- v2.2.3/linux/arch/i386/kernel/smp.c Tue Feb 23 15:21:32 1999 +++ linux/arch/i386/kernel/smp.c Mon Mar 15 11:09:47 1999 @@ -474,7 +474,7 @@ */ cfg=pg0[0]; - pg0[0] = (mp_lapic_addr | 7); + pg0[0] = (mp_lapic_addr | _PAGE_RW | _PAGE_PRESENT); local_flush_tlb(); boot_cpu_id = GET_APIC_ID(*((volatile unsigned long *) APIC_ID)); @@ -1329,7 +1329,7 @@ * Install writable page 0 entry. */ cfg = pg0[0]; - pg0[0] = 3; /* writeable, present, addr 0 */ + pg0[0] = _PAGE_RW | _PAGE_PRESENT; /* writeable, present, addr 0 */ local_flush_tlb(); /* diff -u --recursive --new-file v2.2.3/linux/arch/i386/kernel/time.c linux/arch/i386/kernel/time.c --- v2.2.3/linux/arch/i386/kernel/time.c Wed Mar 10 15:29:45 1999 +++ linux/arch/i386/kernel/time.c Thu Mar 11 23:25:05 1999 @@ -279,7 +279,6 @@ xtime = *tv; time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; - time_state = TIME_ERROR; /* p. 24, (a) */ time_maxerror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT; write_unlock_irq(&xtime_lock); diff -u --recursive --new-file v2.2.3/linux/arch/m68k/kernel/time.c linux/arch/m68k/kernel/time.c --- v2.2.3/linux/arch/m68k/kernel/time.c Wed Jan 20 23:14:04 1999 +++ linux/arch/m68k/kernel/time.c Thu Mar 11 23:25:10 1999 @@ -198,7 +198,6 @@ xtime = *tv; time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; - time_state = TIME_ERROR; /* p. 24, (a) */ time_maxerror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT; sti(); diff -u --recursive --new-file v2.2.3/linux/arch/mips/kernel/sysirix.c linux/arch/mips/kernel/sysirix.c --- v2.2.3/linux/arch/mips/kernel/sysirix.c Sun Nov 8 14:02:43 1998 +++ linux/arch/mips/kernel/sysirix.c Thu Mar 11 23:25:14 1999 @@ -627,7 +627,6 @@ cli(); xtime.tv_sec = value; xtime.tv_usec = 0; - time_state = TIME_ERROR; time_maxerror = MAXPHASE; time_esterror = MAXPHASE; sti(); diff -u --recursive --new-file v2.2.3/linux/arch/mips/kernel/time.c linux/arch/mips/kernel/time.c --- v2.2.3/linux/arch/mips/kernel/time.c Wed Jan 20 23:14:04 1999 +++ linux/arch/mips/kernel/time.c Thu Mar 11 23:25:19 1999 @@ -260,7 +260,6 @@ xtime = *tv; time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; - time_state = TIME_ERROR; /* p. 24, (a) */ time_maxerror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT; sti(); diff -u --recursive --new-file v2.2.3/linux/arch/mips/sgi/kernel/indy_timer.c linux/arch/mips/sgi/kernel/indy_timer.c --- v2.2.3/linux/arch/mips/sgi/kernel/indy_timer.c Fri Oct 23 22:01:20 1998 +++ linux/arch/mips/sgi/kernel/indy_timer.c Thu Mar 11 23:25:24 1999 @@ -104,9 +104,10 @@ * absolutely sure we do this update within 500ms before the * next second starts, thus the following code. */ - if (time_state != TIME_BAD && xtime.tv_sec > last_rtc_update + 660 && - xtime.tv_usec > 500000 - (tick >> 1) && - xtime.tv_usec < 500000 + (tick >> 1)) + if ((time_status & STA_UNSYNC) == 0 && + xtime.tv_sec > last_rtc_update + 660 && + xtime.tv_usec >= 500000 - (tick >> 1) && + xtime.tv_usec <= 500000 + (tick >> 1)) if (set_rtc_mmss(xtime.tv_sec) == 0) last_rtc_update = xtime.tv_sec; else diff -u --recursive --new-file v2.2.3/linux/arch/ppc/8xx_io/commproc.c linux/arch/ppc/8xx_io/commproc.c --- v2.2.3/linux/arch/ppc/8xx_io/commproc.c Mon Oct 5 13:13:35 1998 +++ linux/arch/ppc/8xx_io/commproc.c Fri Mar 19 10:50:03 1999 @@ -111,7 +111,12 @@ */ ((immap_t *)IMAP_ADDR)->im_cpic.cpic_cicr = (CICR_SCD_SCC4 | CICR_SCC_SCC3 | CICR_SCB_SCC2 | CICR_SCA_SCC1) | - ((CPM_INTERRUPT/2) << 13) | CICR_HP_MASK; + (((5)/2) << 13) | CICR_HP_MASK; + /* I hard coded the CPM interrupt to 5 above + * since the CPM_INTERRUPT define is relative to + * the linux irq structure not what the hardware + * belives. -- Cort + */ ((immap_t *)IMAP_ADDR)->im_cpic.cpic_cimr = 0; /* Set our interrupt handler with the core CPU. */ diff -u --recursive --new-file v2.2.3/linux/arch/ppc/8xx_io/enet.c linux/arch/ppc/8xx_io/enet.c --- v2.2.3/linux/arch/ppc/8xx_io/enet.c Tue Dec 22 14:16:54 1998 +++ linux/arch/ppc/8xx_io/enet.c Fri Mar 19 10:50:03 1999 @@ -1,5 +1,4 @@ /* - * $Id: enet.c,v 1.8 1998/11/15 19:58:07 cort Exp $ * Ethernet driver for Motorola MPC8xx. * Copyright (c) 1997 Dan Malek (dmalek@jlc.net) * @@ -39,7 +38,7 @@ #include #include -#include +#include #include #include #include "commproc.h" @@ -49,7 +48,7 @@ * * The MPC8xx CPM performs the Ethernet processing on SCC1. It can use * an aribtrary number of buffers on byte boundaries, but must have at - * least two receive buffers to prevent constand overrun conditions. + * least two receive buffers to prevent constant overrun conditions. * * The buffer descriptors are allocated from the CPM dual port memory * with the data buffers allocated from host memory, just like all other @@ -94,6 +93,17 @@ * Port C, 15 - SCC1 Ethernet Tx Enable * Port C, 11 - SCC1 Ethernet Collision * Port C, 10 - SCC1 Ethernet Rx Enable + * + * The RPX-Lite (that I had :-), was the MPC850SAR. It has a control + * register to enable Ethernet functions in the 68160, and the Ethernet + * was controlled by SCC2. So, the pin I/O was like this: + * Port A, 13 - SCC2 Ethernet Rx + * Port A, 12 - SCC2 Ethernet Tx + * Port A, 6 (CLK2) - Ethernet Tx Clk + * Port A, 4 (CLK4) - Ethernet Rx Clk + * Port B, 18 (RTS2) - Ethernet Tx Enable + * Port C, 8 (CD2) - Ethernet Rx Enable + * Port C, 9 (CTS2) - SCC Ethernet Collision */ /* The number of Tx and Rx buffers. These are allocated from the page @@ -149,10 +159,26 @@ static struct net_device_stats *cpm_enet_get_stats(struct device *dev); static void set_multicast_list(struct device *dev); -/* GET THIS FROM THE VPD!!!! +/* Get this from various configuration locations (depends on board). */ /*static ushort my_enet_addr[] = { 0x0800, 0x3e26, 0x1559 };*/ +/* Right now, only the boards with an 860 use SCC1 for the Ethernet. + * All others use SCC2. We may need to make this board specific someday. + */ +#ifndef CONFIG_MPC860 +/*static ushort my_enet_addr[] = { 0x2700, 0x00ec, 0x1000 };*/ +#define CPM_CR_ENET CPM_CR_CH_SCC2 +#define PROFF_ENET PROFF_SCC2 +#define SCC_ENET 1 /* Index, not number! */ +#define CPMVEC_ENET CPMVEC_SCC2 +#else +#define CPM_CR_ENET CPM_CR_CH_SCC1 +#define PROFF_ENET PROFF_SCC1 +#define SCC_ENET 0 +#define CPMVEC_ENET CPMVEC_SCC1 +#endif + static int cpm_enet_open(struct device *dev) { @@ -178,7 +204,7 @@ /* Transmitter timeout, serious problems. */ if (dev->tbusy) { int tickssofar = jiffies - dev->trans_start; - if (tickssofar < 20) + if (tickssofar < 200) return 1; printk("%s: transmit timed out.\n", dev->name); cep->stats.tx_errors++; @@ -186,17 +212,17 @@ { int i; cbd_t *bdp; - printk(" Ring data dump: cur_tx %x%s cur_rx %x.\n", + printk(" Ring data dump: cur_tx %p%s cur_rx %p.\n", cep->cur_tx, cep->tx_full ? " (full)" : "", cep->cur_rx); bdp = cep->tx_bd_base; - for (i = 0 ; i < TX_RING_SIZE; i++) + for (i = 0 ; i < TX_RING_SIZE; i++, bdp++) printk("%04x %04x %08x\n", bdp->cbd_sc, bdp->cbd_datlen, bdp->cbd_bufaddr); bdp = cep->rx_bd_base; - for (i = 0 ; i < RX_RING_SIZE; i++) + for (i = 0 ; i < RX_RING_SIZE; i++, bdp++) printk("%04x %04x %08x\n", bdp->cbd_sc, bdp->cbd_datlen, @@ -263,7 +289,7 @@ /* Push the data cache so the CPM does not get stale memory * data. */ - /*flush_dcache_range(skb->data, skb->data + skb->len);*/ + flush_dcache_range(skb->data, skb->data + skb->len); /* Send it on its way. Tell CPM its ready, interrupt when done, * its the last BD of the frame, and to put the CRC on the end. @@ -300,7 +326,7 @@ cpm_enet_interrupt(void *dev_id) { struct device *dev = dev_id; - struct cpm_enet_private *cep; + volatile struct cpm_enet_private *cep; volatile cbd_t *bdp; ushort int_events; int must_restart; @@ -314,6 +340,7 @@ /* Get the interrupt events that caused us to be here. */ int_events = cep->sccp->scc_scce; + cep->sccp->scc_scce = int_events; must_restart = 0; /* Handle receive event in its own function. @@ -329,6 +356,7 @@ * I don't know if "normally" implies TXB is set when the buffer * descriptor is closed.....trial and error :-). */ +#if 0 if (int_events & SCCE_ENET_TXE) { /* Transmission errors. @@ -359,16 +387,46 @@ (BD_ENET_TX_LC | BD_ENET_TX_RL | BD_ENET_TX_UN)) must_restart = 1; } +#endif /* Transmit OK, or non-fatal error. Update the buffer descriptors. */ if (int_events & (SCCE_ENET_TXE | SCCE_ENET_TXB)) { +#if 1 + bdp = cep->dirty_tx; + while ((bdp->cbd_sc&BD_ENET_TX_READY)==0) { + if ((bdp==cep->cur_tx) && (cep->tx_full == 0)) + break; + + if (bdp->cbd_sc & BD_ENET_TX_HB) /* No heartbeat */ + cep->stats.tx_heartbeat_errors++; + if (bdp->cbd_sc & BD_ENET_TX_LC) /* Late collision */ + cep->stats.tx_window_errors++; + if (bdp->cbd_sc & BD_ENET_TX_RL) /* Retrans limit */ + cep->stats.tx_aborted_errors++; + if (bdp->cbd_sc & BD_ENET_TX_UN) /* Underrun */ + cep->stats.tx_fifo_errors++; + if (bdp->cbd_sc & BD_ENET_TX_CSL) /* Carrier lost */ + cep->stats.tx_carrier_errors++; + + + /* No heartbeat or Lost carrier are not really bad errors. + * The others require a restart transmit command. + */ + if (bdp->cbd_sc & + (BD_ENET_TX_LC | BD_ENET_TX_RL | BD_ENET_TX_UN)) { + must_restart = 1; + cep->stats.tx_errors++; + } + cep->stats.tx_packets++; +#else bdp = cep->dirty_tx; -#ifndef final_version +#if 1 if (bdp->cbd_sc & BD_ENET_TX_READY) printk("HEY! Enet xmit interrupt and TX_READY.\n"); #endif +#endif /* Deferred means some collisions occurred during transmit, * but we eventually sent the packet OK. */ @@ -406,9 +464,9 @@ } cep->dirty_tx = (cbd_t *)bdp; - } + } - if (must_restart) { + if (must_restart) { volatile cpm8xx_t *cp; /* Some transmit errors cause the transmitter to shut @@ -419,8 +477,9 @@ */ cp = cpmp; cp->cp_cpcr = - mk_cr_cmd(CPM_CR_CH_SCC1, CPM_CR_RESTART_TX) | CPM_CR_FLG; + mk_cr_cmd(CPM_CR_ENET, CPM_CR_RESTART_TX) | CPM_CR_FLG; while (cp->cp_cpcr & CPM_CR_FLG); + } } /* Check for receive busy, i.e. packets coming but no place to @@ -432,11 +491,6 @@ printk("CPM ENET: BSY can't happen.\n"); } - /* Write the SCC event register with the events we have handled - * to clear them. Maybe we should do this sooner? - */ - cep->sccp->scc_scce = int_events; - dev->interrupt = 0; return; @@ -576,7 +630,7 @@ int i, j; cep = (struct cpm_enet_private *)dev->priv; - /* Get pointer to SCC1 area in parameter RAM. + /* Get pointer to SCC area in parameter RAM. */ ep = (scc_enet_t *)dev->base_addr; @@ -627,7 +681,7 @@ /* Ask CPM to run CRC and set bit in * filter mask. */ - cpmp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SCC1, CPM_CR_SET_GADDR) | CPM_CR_FLG; + cpmp->cp_cpcr = mk_cr_cmd(CPM_CR_ENET, CPM_CR_SET_GADDR) | CPM_CR_FLG; /* this delay is necessary here -- Cort */ udelay(10); while (cpmp->cp_cpcr & CPM_CR_FLG); @@ -636,13 +690,15 @@ } } -/* Initialize the CPM Ethernet on SCC1. If EPPC-Bug loaded us, or performed +/* Initialize the CPM Ethernet on SCC. If EPPC-Bug loaded us, or performed * some other network I/O, a whole bunch of this has already been set up. * It is no big deal if we do it again, we just have to disable the * transmit and receive to make sure we don't catch the CPM with some * inconsistent control information. */ -__initfunc(int cpm_enet_init(void)) +/* until this gets cleared up -- Cort */ +int __init cpm_enet_init() { m8xx_enet_init(); } +int __init m8xx_enet_init(void) { struct device *dev; struct cpm_enet_private *cep; @@ -650,6 +706,7 @@ unsigned char *eap; unsigned long mem_addr; pte_t *pte; + bd_t *bd; volatile cbd_t *bdp; volatile cpm8xx_t *cp; volatile scc_t *sccp; @@ -660,6 +717,8 @@ immap = (immap_t *)IMAP_ADDR; /* and to internal registers */ + bd = (bd_t *)res; + /* Allocate some private information. */ cep = (struct cpm_enet_private *)kmalloc(sizeof(*cep), GFP_KERNEL); @@ -670,13 +729,13 @@ */ dev = init_etherdev(0, 0); - /* Get pointer to SCC1 area in parameter RAM. + /* Get pointer to SCC area in parameter RAM. */ - ep = (scc_enet_t *)(&cp->cp_dparam[PROFF_SCC1]); + ep = (scc_enet_t *)(&cp->cp_dparam[PROFF_ENET]); /* And another to the SCC register area. */ - sccp = (volatile scc_t *)(&cp->cp_scc[0]); + sccp = (volatile scc_t *)(&cp->cp_scc[SCC_ENET]); cep->sccp = (scc_t *)sccp; /* Keep the pointer handy */ /* Disable receive and transmit in case EPPC-Bug started it. @@ -686,6 +745,9 @@ /* Cookbook style from the MPC860 manual..... * Not all of this is necessary if EPPC-Bug has initialized * the network. + * So far we are lucky, all board configurations use the same + * pins, or at least the same I/O Port for these functions..... + * It can't last though...... */ /* Configure port A pins for Txd and Rxd. @@ -706,7 +768,7 @@ immap->im_ioport.iop_padir &= ~(PA_ENET_TCLK | PA_ENET_RCLK); /* Configure Serial Interface clock routing. - * First, clear all SCC1 bits to zero, then set the ones we want. + * First, clear all SCC bits to zero, then set the ones we want. */ cp->cp_sicr &= ~SICR_ENET_MASK; cp->cp_sicr |= SICR_ENET_CLKRT; @@ -731,13 +793,13 @@ cep->dirty_tx = cep->cur_tx = cep->tx_bd_base; cep->cur_rx = cep->rx_bd_base; - /* Issue init Rx BD command for SCC1. + /* Issue init Rx BD command for SCC. * Manual says to perform an Init Rx parameters here. We have * to perform both Rx and Tx because the SCC may have been * already running. * In addition, we have to do it later because we don't yet have * all of the BD control/status set properly. - cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SCC1, CPM_CR_INIT_RX) | CPM_CR_FLG; + cp->cp_cpcr = mk_cr_cmd(CPM_CR_ENET, CPM_CR_INIT_RX) | CPM_CR_FLG; while (cp->cp_cpcr & CPM_CR_FLG); */ @@ -781,20 +843,17 @@ ep->sen_iaddr3 = 0; ep->sen_iaddr4 = 0; - /* Set Ethernet station address. This must come from the - * Vital Product Data (VPD) EEPROM.....as soon as I get the - * I2C interface working..... + /* Set Ethernet station address. * - * Since we performed a diskless boot, the Ethernet controller + * If we performed a MBX diskless boot, the Ethernet controller * has been initialized and we copy the address out into our * own structure. */ -#ifdef notdef - ep->sen_paddrh = my_enet_addr[0]; - ep->sen_paddrm = my_enet_addr[1]; - ep->sen_paddrl = my_enet_addr[2]; -#else eap = (unsigned char *)&(ep->sen_paddrh); +#ifndef CONFIG_MBX + for (i=5; i>=0; i--) + *eap++ = dev->dev_addr[i] = bd->bi_enetaddr[i]; +#else for (i=5; i>=0; i--) dev->dev_addr[i] = *eap++; #endif @@ -854,7 +913,7 @@ * than the manual describes because we have just now finished * the BD initialization. */ - cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SCC1, CPM_CR_INIT_TRX) | CPM_CR_FLG; + cp->cp_cpcr = mk_cr_cmd(CPM_CR_ENET, CPM_CR_INIT_TRX) | CPM_CR_FLG; while (cp->cp_cpcr & CPM_CR_FLG); cep->skb_cur = cep->skb_dirty = 0; @@ -869,7 +928,7 @@ /* Install our interrupt handler. */ - cpm_install_handler(CPMVEC_SCC1, cpm_enet_interrupt, dev); + cpm_install_handler(CPMVEC_ENET, cpm_enet_interrupt, dev); /* Set GSMR_H to enable all normal operating modes. * Set GSMR_L to enable Ethernet to MC68160. @@ -884,12 +943,42 @@ /* Set processing mode. Use Ethernet CRC, catch broadcast, and * start frame search 22 bit times after RENA. */ - sccp->scc_pmsr = (SCC_PMSR_ENCRC | SCC_PMSR_BRO | SCC_PMSR_NIB22); + sccp->scc_pmsr = (SCC_PMSR_ENCRC | SCC_PMSR_NIB22); /* It is now OK to enable the Ethernet transmitter. - */ + * Unfortunately, there are board implementation differences here. + */ +#ifdef CONFIG_MBX immap->im_ioport.iop_pcpar |= PC_ENET_TENA; immap->im_ioport.iop_pcdir &= ~PC_ENET_TENA; +#endif + +#if defined(CONFIG_RPXLITE) || defined(CONFIG_RPXCLASSIC) + cp->cp_pbpar |= PB_ENET_TENA; + cp->cp_pbdir |= PB_ENET_TENA; + + /* And while we are here, set the configuration to enable ethernet. + */ + *((volatile uint *)RPX_CSR_ADDR) &= ~BCSR0_ETHLPBK; + *((volatile uint *)RPX_CSR_ADDR) |= + (BCSR0_ETHEN | BCSR0_COLTESTDIS | BCSR0_FULLDPLXDIS); +#endif + +#ifdef CONFIG_BSEIP + cp->cp_pbpar |= PB_ENET_TENA; + cp->cp_pbdir |= PB_ENET_TENA; + + /* BSE uses port B and C for PHY control. + */ + cp->cp_pbpar &= ~(PB_BSE_POWERUP | PB_BSE_FDXDIS); + cp->cp_pbdir |= (PB_BSE_POWERUP | PB_BSE_FDXDIS); + cp->cp_pbdat |= (PB_BSE_POWERUP | PB_BSE_FDXDIS); + + immap->im_ioport.iop_pcpar &= ~PC_BSE_LOOPBACK; + immap->im_ioport.iop_pcdir |= PC_BSE_LOOPBACK; + immap->im_ioport.iop_pcso &= ~PC_BSE_LOOPBACK; + immap->im_ioport.iop_pcdat &= ~PC_BSE_LOOPBACK; +#endif dev->base_addr = (unsigned long)ep; dev->priv = cep; @@ -913,3 +1002,4 @@ return 0; } + diff -u --recursive --new-file v2.2.3/linux/arch/ppc/Makefile linux/arch/ppc/Makefile --- v2.2.3/linux/arch/ppc/Makefile Tue Dec 22 14:16:54 1998 +++ linux/arch/ppc/Makefile Fri Mar 19 10:50:03 1999 @@ -45,6 +45,7 @@ MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot MAKECOFFBOOT = $(MAKE) -C arch/$(ARCH)/coffboot MAKECHRPBOOT = $(MAKE) -C arch/$(ARCH)/chrpboot +MAKEMBXBOOT = $(MAKE) -C arch/$(ARCH)/mbxboot ifdef CONFIG_8xx SUBDIRS += arch/ppc/8xx_io @@ -63,10 +64,16 @@ BOOT_TARGETS = netboot znetboot zImage floppy install \ vmlinux.coff znetboot.initrd zImage.initrd vmlinux.coff.initrd +ifdef CONFIG_MBX +$(BOOT_TARGETS): $(CHECKS) vmlinux + @$(MAKECOFFBOOT) $@ + @$(MAKEMBXBOOT) $@ +else $(BOOT_TARGETS): $(CHECKS) vmlinux @$(MAKECOFFBOOT) $@ @$(MAKEBOOT) $@ @$(MAKECHRPBOOT) $@ +endif pmac_config: rm -f .config arch/ppc/defconfig @@ -100,10 +107,10 @@ @$(MAKECOFFBOOT) clean @$(MAKEBOOT) clean @$(MAKECHRPBOOT) clean + @$(MAKEMBXBOOT) clean archmrproper: archdep: $(MAKEBOOT) fastdep $(MAKECHRPBOOT) fastdep - diff -u --recursive --new-file v2.2.3/linux/arch/ppc/boot/Makefile linux/arch/ppc/boot/Makefile --- v2.2.3/linux/arch/ppc/boot/Makefile Tue Dec 22 14:16:54 1998 +++ linux/arch/ppc/boot/Makefile Fri Mar 19 10:50:03 1999 @@ -25,17 +25,14 @@ IOFF = 0 ISZ = 0 -ifeq ($(CONFIG_ALL_PPC),y) -# yes, we want to build prep stuff -CONFIG_PREP = y -endif - -ifeq ($(CONFIG_MBX),y) -ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00100000 +ifeq ($(CONFIG_SMP),y) +TFTPIMAGE=/tftpboot/zImage.prep.smp else -ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00600000 +TFTPIMAGE=/tftpboot/zImage.prep endif +ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00800000 + GZIP_FLAGS = -v9 OBJECTS := head.o misc.o ../coffboot/zlib.o @@ -43,19 +40,13 @@ OBJCOPY = $(CROSS_COMPILE)objcopy OBJCOPY_ARGS = -O elf32-powerpc -ifeq ($(CONFIG_MBX),y) -OBJECTS += mbxtty.o -CFLAGS += -DCONFIG_MBX -else -OBJECTS += vreset.o kbd.o +OBJECTS += vreset.o kbd.o of1275.o ifeq ($(CONFIG_SERIAL_CONSOLE),y) OBJECTS += ns16550.o endif -endif all: zImage -ifeq ($(CONFIG_PREP),y) zvmlinux.initrd: zvmlinux $(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS) $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ @@ -73,47 +64,12 @@ --add-section=image=../coffboot/vmlinux.gz \ zvmlinux.initrd.tmp $@ rm zvmlinux.initrd.tmp -endif -ifeq ($(CONFIG_MBX),y) -zvmlinux.initrd: zvmlinux - $(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS) - $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ - --add-section=initrd=ramdisk.image.gz \ - --add-section=image=../coffboot/vmlinux.gz \ - zvmlinux.initrd.tmp zvmlinux.initrd - $(CC) $(CFLAGS) -DINITRD_OFFSET=`sh offset $(OBJDUMP) zvmlinux.initrd initrd` \ - -DINITRD_SIZE=`sh size $(OBJDUMP) zvmlinux.initrd initrd` \ - -DZIMAGE_OFFSET=`sh offset $(OBJDUMP) zvmlinux.initrd image` \ - -DZIMAGE_SIZE=`sh size $(OBJDUMP) zvmlinux.initrd image` \ - -DKERNELBASE=$(KERNELBASE) -c -o misc.o misc.c - $(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS) - $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ - --add-section=initrd=ramdisk.image.gz \ - --add-section=image=../coffboot/vmlinux.gz \ - zvmlinux.initrd.tmp $@ - rm zvmlinux.initrd.tmp -endif -ifeq ($(CONFIG_PREP),y) zImage: zvmlinux mkprep ./mkprep -pbp zvmlinux zImage -else -ifeq ($(CONFIG_MBX),y) -zImage: zvmlinux - ln -sf zvmlinux zImage -else -zImage: -endif -endif -ifeq ($(CONFIG_PREP),y) zImage.initrd: zvmlinux.initrd mkprep ./mkprep -pbp zvmlinux.initrd zImage.initrd -endif -ifeq ($(CONFIG_MBX),y) -zImage.initrd: zvmlinux.initrd - ln -sf zvmlinux.initrd zImage.initrd -endif zvmlinux: $(OBJECTS) ../coffboot/vmlinux.gz # @@ -136,34 +92,16 @@ rm zvmlinux.tmp floppy: $(TOPDIR)/vmlinux zImage -ifeq ($(CONFIG_PREP),y) dd if=zImage of=/dev/fd0H1440 bs=64b -endif -ifeq ($(CONFIG_PREP),y) mkprep : mkprep.c $(HOSTCC) -DKERNELBASE=$(KERNELBASE) -o mkprep mkprep.c -endif -ifeq ($(CONFIG_PREP),y) znetboot : zImage - cp zImage /tftpboot/zImage.prep -else -ifeq ($(CONFIG_MBX),y) -znetboot : zImage - cp zImage /tftpboot/zImage.mbx -else -znetboot : -endif -endif + cp zImage $(TFTPIMAGE) znetboot.initrd : zImage.initrd -ifeq ($(CONFIG_PREP),y) - cp zImage.initrd /tftpboot/zImage.prep -endif -ifeq ($(CONFIG_MBX),y) - cp zImage.initrd /tftpboot/zImage.mbx -endif + cp zImage.initrd $(TFTPIMAGE) clean: rm -f vmlinux* zvmlinux* mkprep zImage* diff -u --recursive --new-file v2.2.3/linux/arch/ppc/boot/head.S linux/arch/ppc/boot/head.S --- v2.2.3/linux/arch/ppc/boot/head.S Mon Oct 5 13:13:36 1998 +++ linux/arch/ppc/boot/head.S Fri Mar 19 10:50:03 1999 @@ -7,39 +7,24 @@ .text /* - * $Id: head.S,v 1.26 1998/09/19 01:21:20 cort Exp $ + * $Id: head.S,v 1.29 1999/03/08 23:41:17 cort Exp $ * - * This code is loaded by the ROM loader at some arbitrary location. - * Move it to high memory so that it can load the kernel at 0x0000. - * - * The MBX EPPC-Bug understands ELF, so it loads us into the location - * specified in the header. This is a two step process. First, EPPC-Bug - * loads the file into the intermediate buffer memory location specified - * by the environment parameters. When it discovers this is an ELF - * binary, it relocates to the link address for us. Unfortunately, the - * header does not move with the file, so we have to find the - * intermediate load location and read the header from there. From - * information provided by Motorola (thank you), we know this intermediate - * location can be found from the NVRAM environment. - * All of these addresses must be somewhat carefully chosen to make sure - * we don't overlap the regions. I chose to load the kernel at 0, the - * compressed image loads at 0x00100000, and the MBX intermediate buffer - * was set to 0x00200000. Provided the loaded kernel image never grows - * over one megabyte (which I am going to ensure never happens :-), these - * will work fine. When we get called from EPPC-Bug, registers are: - * R1 - Stack pointer at a high memory address. - * R3 - Pointer to Board Information Block. - * R4 - Pointer to argument string. - * Interrupts masked, cache and MMU disabled. + * Boot loader philosophy: + * ROM loads us to some arbitrary location + * Move the boot code to the link address (8M) + * Call decompress_kernel() + * Relocate the initrd, zimage and residual data to 8M + * Decompress the kernel to 0 + * Jump to the kernel entry + * -- Cort */ - .globl start start: bl start_ start_: mr r11,r3 /* Save pointer to residual/board data */ - -#ifndef CONFIG_MBX + mr r25,r5 /* Save OFW pointer */ + mfmsr r3 /* Turn off interrupts */ li r4,0 ori r4,r4,MSR_EE @@ -68,25 +53,6 @@ mr r7,r5 b start_ldr 1010: -#if 0 -/* Copy relocation code down to location 0x0100 (where we hope it's safe!) */ - mflr r3 - addi r5,r3,start_ldr-start_ - addi r3,r3,relocate-start_ - li r4,0x0100 - mtctr r4 - subi r3,r3,4 - subi r4,r4,4 -00: lwzu r6,4(r3) - stwu r6,4(r4) - cmp 0,r3,r5 - bne 00b - mflr r21 - mfctr r22 - mtlr r21 - mtctr r22 - bctr /* Jump to code */ -#endif /* * no matter where we're loaded, move ourselves to -Ttext address */ @@ -96,13 +62,8 @@ mr r8,r3 lis r4,start@h ori r4,r4,start@l -#if 0 - lis r5,edata@h - ori r5,r5,edata@l -#else lis r5,end@h ori r5,r5,end@l -#endif addi r5,r5,3 /* Round up - just in case */ sub r5,r5,r4 /* Compute # longwords to move */ srwi r5,r5,2 @@ -120,7 +81,6 @@ mtlr r3 /* Easiest way to do an absolute jump */ blr start_ldr: -#endif /* ndef CONFIG_MBX */ /* Clear all of BSS */ lis r3,edata@h ori r3,r3,edata@l @@ -140,31 +100,11 @@ li r2,0x000F /* Mask pointer to 16-byte boundary */ andc r1,r1,r2 /* Run loader */ -#ifdef CONFIG_MBX - mr r3, r11 - mr r21, r11 - bl serial_init /* Init MBX serial port */ - - lis r8, 0xfa200000@h /* Disable Ethernet SCC */ - li r0, 0 - stw r0, 0x0a00(r8) - - mr r11, r21 - lis r8,start@h - ori r8,r8,start@l - li r9,end@h - ori r9,r9,end@l - sub r7,r8,r9 - srwi r7,r7,2 -#define ILAP_ADDRESS 0xfa000020 - lis r8, ILAP_ADDRESS@h - lwz r8, ILAP_ADDRESS@l(r8) - addis r8, r8, 1 /* Add 64K */ -#endif mr r3,r8 /* Load point */ mr r4,r7 /* Program length */ mr r5,r6 /* Checksum */ mr r6,r11 /* Residual data */ + mr r7,r25 /* OFW interfaces */ bl decompress_kernel /* changed to use r3 (as firmware does) for kernel @@ -193,12 +133,10 @@ li r9,0x0 lwz r9,0(r9) mtlr r9 -#ifndef CONFIG_MBX li r9,0 lis r10,0xdeadc0de@h ori r10,r10,0xdeadc0de@l stw r10,0(r9) -#endif blr hang: b hang @@ -269,7 +207,6 @@ _GLOBAL(flush_instruction_cache) mflr r5 bl flush_data_cache -#ifndef CONFIG_MBX mfspr r3,HID0 /* Caches are controlled by this register */ li r4,0 ori r4,r4,(HID0_ICE|HID0_ICFI) @@ -278,18 +215,12 @@ andc r3,r3,r4 ori r3,r3,HID0_ICE /* Enable cache */ mtspr HID0,r3 -#endif mtlr r5 blr #define NUM_CACHE_LINES 128*8 #define CACHE_LINE_SIZE 32 -#if 0 -cache_flush_buffer: - .space NUM_CACHE_LINES*CACHE_LINE_SIZE /* CAUTION! these need to match hardware */ -#else #define cache_flush_buffer 0x1000 -#endif /* * Flush data cache @@ -300,11 +231,7 @@ ori r3,r3,cache_flush_buffer@l li r4,NUM_CACHE_LINES mtctr r4 -#if 0 -00: dcbz 0,r3 /* Flush cache line with minimal BUS traffic */ -#else 00: lwz r4,0(r3) -#endif addi r3,r3,CACHE_LINE_SIZE /* Next line, please */ bdnz 00b 10: blr diff -u --recursive --new-file v2.2.3/linux/arch/ppc/boot/kbd.c linux/arch/ppc/boot/kbd.c --- v2.2.3/linux/arch/ppc/boot/kbd.c Mon Jan 12 15:18:13 1998 +++ linux/arch/ppc/boot/kbd.c Fri Mar 19 10:50:03 1999 @@ -1,7 +1,6 @@ - #include -#include <../drivers/char/defkeymap.c> /* yeah I know it's bad */ +#include <../drivers/char/defkeymap.c> /* yeah I know it's bad -- Cort */ unsigned char shfts, ctls, alts, caps; @@ -119,7 +118,7 @@ } break; } - if (brk) return (0); /* Ignore initial 'key up' codes */ + if (brk) return (-1); /* Ignore initial 'key up' codes */ goto loop; } @@ -144,22 +143,63 @@ while (inb(KBSTATP) & KBOUTRDY) ; outb(KBDATAP,0x45); for (i = 0; i < 10000; i++) udelay(1); + + while (inb(KBSTATP) & KBOUTRDY) ; + outb(KBSTATP,0x20); + while ((inb(KBSTATP) & KBINRDY) == 0) ; /* wait input ready */ + if (! (inb(KBDATAP) & 0x40)) { + /* + * Quote from PS/2 System Reference Manual: + * + * "Address hex 0060 and address hex 0064 should be + * written only when the input-buffer-full bit and + * output-buffer-full bit in the Controller Status + * register are set 0." (KBINRDY and KBOUTRDY) + */ + + while (inb(KBSTATP) & (KBINRDY | KBOUTRDY)) ; + outb(KBDATAP,0xF0); + while (inb(KBSTATP) & (KBINRDY | KBOUTRDY)) ; + outb(KBDATAP,0x01); + } + while (inb(KBSTATP) & KBOUTRDY) ; outb(KBSTATP,0xAE); } +/* We have to actually read the keyboard when CRT_tstc is called, + * since the pending data might be a key release code, and therefore + * not valid data. In this case, kbd() will return -1, even though there's + * data to be read. Of course, we might actually read a valid key press, + * in which case it gets queued into key_pending for use by CRT_getc. + */ + static int kbd_reset = 0; +static int key_pending = -1; + int CRT_getc(void) { int c; if (!kbd_reset) {kbdreset(); kbd_reset++; } + + if (key_pending != -1) { + c = key_pending; + key_pending = -1; + return c; + } else { while ((c = kbd(0)) == 0) ; - return(c); + return c; + } } int CRT_tstc(void) { if (!kbd_reset) {kbdreset(); kbd_reset++; } - return ((inb(KBSTATP) & KBINRDY) != 0); + + while (key_pending == -1 && ((inb(KBSTATP) & KBINRDY) != 0)) { + key_pending = kbd(1); + } + + return (key_pending != -1); } diff -u --recursive --new-file v2.2.3/linux/arch/ppc/boot/mbxtty.c linux/arch/ppc/boot/mbxtty.c --- v2.2.3/linux/arch/ppc/boot/mbxtty.c Mon Oct 5 13:13:36 1998 +++ linux/arch/ppc/boot/mbxtty.c Wed Dec 31 16:00:00 1969 @@ -1,201 +0,0 @@ - - -/* Minimal serial functions needed to send messages out the serial - * port on the MBX console. - * - * The MBX uxes SMC1 for the serial port. We reset the port and use - * only the first BD that EPPC-Bug set up as a character FIFO. - * - * Later versions (at least 1.4, maybe earlier) of the MBX EPPC-Bug - * use COM1 instead of SMC1 as the console port. This kinda sucks - * for the rest of the kernel, so here we force the use of SMC1 again. - * I f**ked around for a day trying to figure out how to make EPPC-Bug - * use SMC1, but gave up and decided to fix it here. - */ -#include -#include -#ifdef CONFIG_MBX -#include -#endif -#ifdef CONFIG_FADS -#include -#endif -#include "../8xx_io/commproc.h" - -#ifdef CONFIG_MBX -#define MBX_CSR1 ((volatile u_char *)0xfa100000) -#define CSR1_COMEN (u_char)0x02 -#endif - -static cpm8xx_t *cpmp = (cpm8xx_t *)&(((immap_t *)IMAP_ADDR)->im_cpm); - -void -serial_init(bd_t *bd) -{ - volatile smc_t *sp; - volatile smc_uart_t *up; - volatile cbd_t *tbdf, *rbdf; - volatile cpm8xx_t *cp; - uint dpaddr, memaddr; - - cp = cpmp; - sp = (smc_t*)&(cp->cp_smc[0]); - up = (smc_uart_t *)&cp->cp_dparam[PROFF_SMC1]; - - /* Disable transmitter/receiver. - */ - sp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); - -#ifdef CONFIG_MBX - if (*MBX_CSR1 & CSR1_COMEN) { - /* COM1 is enabled. Initialize SMC1 and use it for - * the console port. - */ - - /* Enable SDMA. - */ - ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sdcr = 1; - - /* Use Port B for SMCs instead of other functions. - */ - cp->cp_pbpar |= 0x00000cc0; - cp->cp_pbdir &= ~0x00000cc0; - cp->cp_pbodr &= ~0x00000cc0; - - /* Allocate space for two buffer descriptors in the DP ram. - * For now, this address seems OK, but it may have to - * change with newer versions of the firmware. - */ - dpaddr = 0x0800; - - /* Grab a few bytes from the top of memory. EPPC-Bug isn't - * running any more, so we can do this. - */ - memaddr = (bd->bi_memsize - 32) & ~15; - - /* Set the physical address of the host memory buffers in - * the buffer descriptors. - */ - rbdf = (cbd_t *)&cp->cp_dpmem[dpaddr]; - rbdf->cbd_bufaddr = memaddr; - rbdf->cbd_sc = 0; - tbdf = rbdf + 1; - tbdf->cbd_bufaddr = memaddr+4; - tbdf->cbd_sc = 0; - - /* Set up the uart parameters in the parameter ram. - */ - up->smc_rbase = dpaddr; - up->smc_tbase = dpaddr+sizeof(cbd_t); - up->smc_rfcr = SMC_EB; - up->smc_tfcr = SMC_EB; - - /* Set UART mode, 8 bit, no parity, one stop. - * Enable receive and transmit. - */ - sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART; - - /* Mask all interrupts and remove anything pending. - */ - sp->smc_smcm = 0; - sp->smc_smce = 0xff; - - /* Set up the baud rate generator. - * See 8xx_io/commproc.c for details. - */ - cp->cp_simode = 0x10000000; - cp->cp_brgc1 = - ((((bd->bi_intfreq * 1000000)/16) / 9600) << 1) | CPM_BRG_EN; - - /* Enable SMC1 for console output. - */ - *MBX_CSR1 &= ~CSR1_COMEN; - } - else { -#endif - /* SMC1 is used as console port. - */ - tbdf = (cbd_t *)&cp->cp_dpmem[up->smc_tbase]; - rbdf = (cbd_t *)&cp->cp_dpmem[up->smc_rbase]; - - /* Issue a stop transmit, and wait for it. - */ - cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC1, - CPM_CR_STOP_TX) | CPM_CR_FLG; - while (cp->cp_cpcr & CPM_CR_FLG); -#ifdef CONFIG_MBX - } -#endif - - /* Make the first buffer the only buffer. - */ - tbdf->cbd_sc |= BD_SC_WRAP; - rbdf->cbd_sc |= BD_SC_EMPTY | BD_SC_WRAP; - - /* Single character receive. - */ - up->smc_mrblr = 1; - up->smc_maxidl = 0; - - /* Initialize Tx/Rx parameters. - */ - cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC1, CPM_CR_INIT_TRX) | CPM_CR_FLG; - while (cp->cp_cpcr & CPM_CR_FLG); - - /* Enable transmitter/receiver. - */ - sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN; -} - -void -serial_putchar(const char c) -{ - volatile cbd_t *tbdf; - volatile char *buf; - volatile smc_uart_t *up; - - up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC1]; - tbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_tbase]; - - /* Wait for last character to go. - */ - buf = (char *)tbdf->cbd_bufaddr; - while (tbdf->cbd_sc & BD_SC_READY); - - *buf = c; - tbdf->cbd_datlen = 1; - tbdf->cbd_sc |= BD_SC_READY; -} - -char -serial_getc() -{ - volatile cbd_t *rbdf; - volatile char *buf; - volatile smc_uart_t *up; - char c; - - up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC1]; - rbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase]; - - /* Wait for character to show up. - */ - buf = (char *)rbdf->cbd_bufaddr; - while (rbdf->cbd_sc & BD_SC_EMPTY); - c = *buf; - rbdf->cbd_sc |= BD_SC_EMPTY; - - return(c); -} - -int -serial_tstc() -{ - volatile cbd_t *rbdf; - volatile smc_uart_t *up; - - up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC1]; - rbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase]; - - return(!(rbdf->cbd_sc & BD_SC_EMPTY)); -} diff -u --recursive --new-file v2.2.3/linux/arch/ppc/boot/misc.c linux/arch/ppc/boot/misc.c --- v2.2.3/linux/arch/ppc/boot/misc.c Tue Dec 22 14:16:54 1998 +++ linux/arch/ppc/boot/misc.c Fri Mar 19 10:50:03 1999 @@ -1,7 +1,7 @@ /* * misc.c * - * $Id: misc.c,v 1.53 1998/12/15 17:40:15 cort Exp $ + * $Id: misc.c,v 1.61 1999/03/08 23:51:02 cort Exp $ * * Adapted for PowerPC by Gary Thomas * @@ -17,13 +17,7 @@ #include #include #include -#ifdef CONFIG_MBX -#include -#endif -#ifdef CONFIG_FADS -#include -#endif -#if defined(CONFIG_SERIAL_CONSOLE) && !defined(CONFIG_MBX) +#if defined(CONFIG_SERIAL_CONSOLE) #include "ns16550.h" struct NS16550 *com_port; #endif /* CONFIG_SERIAL_CONSOLE */ @@ -37,30 +31,16 @@ */ char *avail_ram; char *end_avail; +extern char _end[]; -/* Because of the limited amount of memory on the MBX, it presents - * loading problems. The biggest is that we load this boot program - * into a relatively low memory address, and the Linux kernel Bss often - * extends into this space when it get loaded. When the kernel starts - * and zeros the BSS space, it also writes over the information we - * save here and pass to the kernel (command line and board info). - * On the MBX we grab some known memory holes to hold this information. - */ -char cmd_preset[] = "console=tty0 console=ttyS0,9600n8"; +#if defined(CONFIG_SERIAL_CONSOLE) +char cmd_preset[] = "console=ttyS0,9600n8"; +#else +char cmd_preset[] = ""; +#endif char cmd_buf[256]; char *cmd_line = cmd_buf; -#if defined(CONFIG_MBX) || defined(CONFIG_FADS) -char *root_string = "root=/dev/nfs"; -char *nfsaddrs_string = "nfsaddrs="; -char *nfsroot_string = "nfsroot="; -char *defroot_string = "/sys/mbxroot"; -int do_ipaddrs(char **cmd_cp, int echo); -void do_nfsroot(char **cmd_cp, char *dp); -int strncmp(const char * cs,const char * ct,size_t count); -char *strrchr(const char * s, int c); -#endif - RESIDUAL hold_resid_buf; RESIDUAL *hold_residual = &hold_resid_buf; unsigned long initrd_start = 0, initrd_end = 0; @@ -78,6 +58,7 @@ void * memcpy(void * __dest, __const void * __src, int __n); void gunzip(void *, int, unsigned char *, int *); +int _cvt(unsigned long val, char *buf, long radix, char *digits); void pause() { @@ -90,7 +71,6 @@ while(1); } -#if !defined(CONFIG_MBX) && !defined(CONFIG_FADS) static void clear_screen() { int i, j; @@ -113,17 +93,17 @@ tstc(void) { -#if defined(CONFIG_SERIAL_CONSOLE) && !defined(CONFIG_MBX) - return (CRT_tstc() || NS16550_tstc(com_port)); -#else - return (CRT_tstc() ); + return ( +#if defined(CONFIG_SERIAL_CONSOLE) + NS16550_tstc(com_port) || #endif /* CONFIG_SERIAL_CONSOLE */ + CRT_tstc()); } getc(void) { while (1) { -#if defined(CONFIG_SERIAL_CONSOLE) && !defined(CONFIG_MBX) +#if defined(CONFIG_SERIAL_CONSOLE) if (NS16550_tstc(com_port)) return (NS16550_getc(com_port)); #endif /* CONFIG_SERIAL_CONSOLE */ if (CRT_tstc()) return (CRT_getc()); @@ -135,7 +115,7 @@ { int x,y; -#if defined(CONFIG_SERIAL_CONSOLE) && !defined(CONFIG_MBX) +#if defined(CONFIG_SERIAL_CONSOLE) NS16550_putc(com_port, c); if ( c == '\n' ) NS16550_putc(com_port, '\r'); #endif /* CONFIG_SERIAL_CONSOLE */ @@ -149,6 +129,8 @@ scroll(); y--; } + } else if (c == '\r') { + x = 0; } else if (c == '\b') { if (x > 0) { x--; @@ -179,7 +161,7 @@ y = orig_y; while ( ( c = *s++ ) != '\0' ) { -#if defined(CONFIG_SERIAL_CONSOLE) && !defined(CONFIG_MBX) +#if defined(CONFIG_SERIAL_CONSOLE) NS16550_putc(com_port, c); if ( c == '\n' ) NS16550_putc(com_port, '\r'); #endif /* CONFIG_SERIAL_CONSOLE */ @@ -209,39 +191,6 @@ orig_x = x; orig_y = y; } -#else -/* The MBX is just the serial port. -*/ -tstc(void) -{ - return (serial_tstc()); -} - -getc(void) -{ - while (1) { - if (serial_tstc()) return (serial_getc()); - } -} - -void -putc(const char c) -{ - serial_putchar(c); -} - -void puts(const char *s) -{ - char c; - - while ( ( c = *s++ ) != '\0' ) { - serial_putchar(c); - if ( c == '\n' ) - serial_putchar('\r'); - } -} - -#endif /* CONFIG_MBX */ void * memcpy(void * __dest, __const void * __src, int __n) @@ -354,7 +303,8 @@ unsigned char sanity[0x2000]; unsigned long -decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, RESIDUAL *residual) +decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, + RESIDUAL *residual, void *OFW_interface) { int timer; extern unsigned long start; @@ -362,9 +312,11 @@ unsigned long i; BATU *u; BATL *l; -#if defined(CONFIG_MBX) || defined(CONFIG_KB) - char *dp; -#endif + unsigned long TotalMemory; + unsigned long orig_MSR; + int dev_handle; + int mem_info[2]; + int res, size; lines = 25; cols = 80; @@ -372,7 +324,6 @@ orig_y = 24; -#if !defined(CONFIG_MBX) && !defined(CONFIG_FADS) /* * IBM's have the MMU on, so we have to disable it or * things get really unhappy in the kernel when @@ -381,42 +332,62 @@ */ flush_instruction_cache(); _put_HID0(_get_HID0() & ~0x0000C000); - _put_MSR(_get_MSR() & ~0x0030); + _put_MSR((orig_MSR = _get_MSR()) & ~0x0030); vga_init(0xC0000000); -#if defined(CONFIG_SERIAL_CONSOLE) && !defined(CONFIG_MBX) - com_port = (struct NS16550 *)NS16550_init(0); +#if defined(CONFIG_SERIAL_CONSOLE) + com_port = (struct NS16550 *)NS16550_init(1); #endif /* CONFIG_SERIAL_CONSOLE */ if (residual) + { memcpy(hold_residual,residual,sizeof(RESIDUAL)); -#else /* CONFIG_MBX */ - - /* Grab some space for the command line and board info. Since - * we no longer use the ELF header, but it was loaded, grab - * that space. - */ - cmd_line = (char *)(load_addr - 0x10000); - hold_residual = (RESIDUAL *)(cmd_line + sizeof(cmd_buf)); - /* copy board data */ - if (residual) - memcpy(hold_residual,residual,sizeof(bd_t)); -#endif /* CONFIG_MBX */ - - /* MBX/prep sometimes put the residual/board info at the end of mem - * assume 16M for now -- Cort - * To boot on standard MBX boards with 4M, we can't use initrd, - * and we have to assume less memory. -- Dan - */ - if ( INITRD_OFFSET ) - end_avail = (char *)0x01000000; - else - end_avail = (char *)0x00400000; + } else { + /* Assume 32M in the absence of more info... */ + TotalMemory = 0x02000000; + /* + * This is a 'best guess' check. We want to make sure + * we don't try this on a PReP box without OF + * -- Cort + */ + while (OFW_interface && ((unsigned long)OFW_interface < 0x10000000) ) + { + /* The MMU needs to be on when we call OFW */ + _put_MSR(orig_MSR); + of_init(OFW_interface); + + /* get handle to memory description */ + res = of_finddevice("/memory@0", + &dev_handle); + // puthex(res); puts("\n"); + if (res) break; + + /* get the info */ + // puts("get info = "); + res = of_getprop(dev_handle, + "reg", + mem_info, + sizeof(mem_info), + &size); + // puthex(res); puts(", info = "); puthex(mem_info[0]); + // puts(" "); puthex(mem_info[1]); puts("\n"); + if (res) break; + + TotalMemory = mem_info[1]; + break; + } + hold_residual->TotalMemory = TotalMemory; + residual = hold_residual; + /* Turn MMU back off */ + _put_MSR(orig_MSR & ~0x0030); + } - /* let residual data tell us it's higher */ - if ( (unsigned long)residual > 0x00800000 ) - end_avail = (char *)PAGE_ALIGN((unsigned long)residual); + /* assume the chunk below 8M is free */ + end_avail = (char *)0x00800000; + /* tell the user where we were loaded at and where we + * were relocated to for debugging this process + */ puts("loaded at: "); puthex(load_addr); puts(" "); puthex((unsigned long)(load_addr + (4*num_words))); puts("\n"); if ( (unsigned long)load_addr != (unsigned long)&start ) @@ -431,20 +402,12 @@ { puts("board data at: "); puthex((unsigned long)residual); puts(" "); -#if defined(CONFIG_MBX) || defined(CONFIG_FADS) - puthex((unsigned long)((unsigned long)residual + sizeof(bd_t))); -#else puthex((unsigned long)((unsigned long)residual + sizeof(RESIDUAL))); -#endif puts("\n"); puts("relocated to: "); puthex((unsigned long)hold_residual); puts(" "); -#if defined(CONFIG_MBX) || defined(CONFIG_FADS) - puthex((unsigned long)((unsigned long)hold_residual + sizeof(bd_t))); -#else puthex((unsigned long)((unsigned long)hold_residual + sizeof(RESIDUAL))); -#endif puts("\n"); } @@ -460,33 +423,16 @@ initrd_end = INITRD_SIZE + initrd_start; /* - * setup avail_ram - this is the first part of ram usable - * by the uncompress code. -- Cort + * Find a place to stick the zimage and initrd and + * relocate them if we have to. -- Cort */ - avail_ram = (char *)PAGE_ALIGN((unsigned long)zimage_start+zimage_size); - if ( ((load_addr+(num_words*4)) > (unsigned long) avail_ram) - && (load_addr <= 0x01000000) ) - avail_ram = (char *)(load_addr+(num_words*4)); - if ( (((unsigned long)&start+(num_words*4)) > (unsigned long) avail_ram) - && (load_addr <= 0x01000000) ) - avail_ram = (char *)((unsigned long)&start+(num_words*4)); - - /* relocate zimage */ + avail_ram = (char *)PAGE_ALIGN((unsigned long)_end); puts("zimage at: "); puthex((unsigned long)zimage_start); puts(" "); puthex((unsigned long)(zimage_size+zimage_start)); puts("\n"); - /* - * don't relocate the zimage if it was loaded above 16M since - * things get weird if we try to relocate -- Cort - * We don't relocate zimage on a base MBX board because of - * insufficient memory. In this case we don't have initrd either, - * so use that as an indicator. -- Dan - */ - if (( (unsigned long)zimage_start <= 0x01000000 ) && initrd_start) + if ( (unsigned long)zimage_start <= 0x008000000 ) { - memcpy ((void *)PAGE_ALIGN(-PAGE_SIZE+(unsigned long)end_avail-zimage_size), - (void *)zimage_start, zimage_size ); - zimage_start = (char *)PAGE_ALIGN(-PAGE_SIZE+(unsigned long)end_avail-zimage_size); - end_avail = (char *)zimage_start; + memcpy( (void *)avail_ram, (void *)zimage_start, zimage_size ); + zimage_start = (char *)avail_ram; puts("relocated to: "); puthex((unsigned long)zimage_start); puts(" "); puthex((unsigned long)zimage_size+(unsigned long)zimage_start); @@ -498,42 +444,21 @@ { puts("initrd at: "); puthex(initrd_start); puts(" "); puthex(initrd_end); puts("\n"); - /* - * Memory is really tight on the MBX (we can assume 4M) - * so put the initrd at the TOP of ram, and set end_avail - * to right after that. - * - * I should do something like this for prep, too and keep - * a variable end_of_DRAM to keep track of what we think the - * max ram is. - * -- Cort - */ -#if 0 - memcpy ((void *)PAGE_ALIGN(-PAGE_SIZE+(unsigned long)end_avail-INITRD_SIZE), - (void *)initrd_start, - INITRD_SIZE ); - initrd_start = PAGE_ALIGN(-PAGE_SIZE+(unsigned long)end_avail-INITRD_SIZE); + avail_ram = (char *)PAGE_ALIGN( + (unsigned long)zimage_size+(unsigned long)zimage_start); + memcpy ((void *)avail_ram, (void *)initrd_start, INITRD_SIZE ); + initrd_start = (unsigned long)avail_ram; initrd_end = initrd_start + INITRD_SIZE; - end_avail = (char *)initrd_start; puts("relocated to: "); puthex(initrd_start); puts(" "); puthex(initrd_end); puts("\n"); -#endif } -#ifndef CONFIG_MBX - /* this is safe, just use it */ - /* I don't know why it didn't work for me on the MBX with 20 MB - * memory. I guess something was saved up there, but I can't - * figure it out......we are running on luck. -- Dan. - */ avail_ram = (char *)0x00400000; - end_avail = (char *)0x00600000; -#endif + end_avail = (char *)0x00800000; puts("avail ram: "); puthex((unsigned long)avail_ram); puts(" "); puthex((unsigned long)end_avail); puts("\n"); - -#if !defined(CONFIG_MBX) && !defined(CONFIG_FADS) +#if !defined(CONFIG_SERIAL_CONSOLE) CRT_tstc(); /* Forces keyboard to be initialized */ #endif @@ -550,13 +475,6 @@ cp--; puts("\b \b"); } -#ifdef CONFIG_MBX - } else if (ch == '?') { - if (!do_ipaddrs(&cp, 1)) { - *cp++ = ch; - putc(ch); - } -#endif } else { *cp++ = ch; putc(ch); @@ -567,32 +485,6 @@ udelay(1000); /* 1 msec */ } *cp = 0; -#ifdef CONFIG_MBX - /* The MBX does not currently have any default boot strategy. - * If the command line is not filled in, we will automatically - * create the default network boot. - */ - if (cmd_line[0] == 0) { - dp = root_string; - while (*dp != 0) - *cp++ = *dp++; - *cp++ = ' '; - - dp = nfsaddrs_string; - while (*dp != 0) - *cp++ = *dp++; - dp = cp; - do_ipaddrs(&cp, 0); - *cp++ = ' '; - - /* Add the server address to the root file system path. - */ - dp = strrchr(dp, ':'); - dp++; - do_nfsroot(&cp, dp); - *cp = 0; - } -#endif puts("\n"); /* mappings on early boot can only handle 16M */ @@ -611,150 +503,6 @@ return (unsigned long)hold_residual; } -#ifdef CONFIG_MBX -int -do_ipaddrs(char **cmd_cp, int echo) -{ - char *cp, *ip, ch; - unsigned char ipd; - int i, j, retval; - - /* We need to create the string: - * : - */ - cp = *cmd_cp; - retval = 0; - - if ((cp - 9) >= cmd_line) { - if (strncmp(cp - 9, "nfsaddrs=", 9) == 0) { - ip = (char *)0xfa000060; - retval = 1; - for (j=0; j<2; j++) { - for (i=0; i<4; i++) { - ipd = *ip++; - - ch = ipd/100; - if (ch) { - ch += '0'; - if (echo) - putc(ch); - *cp++ = ch; - ipd -= 100 * (ch - '0'); - } - - ch = ipd/10; - if (ch) { - ch += '0'; - if (echo) - putc(ch); - *cp++ = ch; - ipd -= 10 * (ch - '0'); - } - - ch = ipd + '0'; - if (echo) - putc(ch); - *cp++ = ch; - - ch = '.'; - if (echo) - putc(ch); - *cp++ = ch; - } - - /* At the end of the string, remove the - * '.' and replace it with a ':'. - */ - *(cp - 1) = ':'; - if (echo) { - putc('\b'); putc(':'); - } - } - - /* At the end of the second string, remove the - * '.' from both the command line and the - * screen. - */ - --cp; - putc('\b'); putc(' '); putc('\b'); - } - } - *cmd_cp = cp; - return(retval); -} - -void -do_nfsroot(char **cmd_cp, char *dp) -{ - char *cp, *rp, *ep; - - /* The boot argument (i.e /sys/mbxroot/zImage) is stored - * at offset 0x0078 in NVRAM. We use this path name to - * construct the root file system path. - */ - cp = *cmd_cp; - - /* build command string. - */ - rp = nfsroot_string; - while (*rp != 0) - *cp++ = *rp++; - - /* Add the server address to the path. - */ - while (*dp != ' ') - *cp++ = *dp++; - *cp++ = ':'; - - rp = (char *)0xfa000078; - ep = strrchr(rp, '/'); - - if (ep != 0) { - while (rp < ep) - *cp++ = *rp++; - } - else { - rp = defroot_string; - while (*rp != 0) - *cp++ = *rp++; - } - - *cmd_cp = cp; -} - -size_t strlen(const char * s) -{ - const char *sc; - - for (sc = s; *sc != '\0'; ++sc) - /* nothing */; - return sc - s; -} - -int strncmp(const char * cs,const char * ct,size_t count) -{ - register signed char __res = 0; - - while (count) { - if ((__res = *cs - *ct++) != 0 || !*cs++) - break; - count--; - } - - return __res; -} - -char * strrchr(const char * s, int c) -{ - const char *p = s + strlen(s); - do { - if (*p == (char)c) - return (char *)p; - } while (--p >= s); - return NULL; -} -#endif - void puthex(unsigned long val) { unsigned char buf[10]; @@ -801,4 +549,244 @@ _bcopy(char *src, char *dst, int len) { while (len--) *dst++ = *src++; +} + + +#define FALSE 0 +#define TRUE 1 +#include + +int +strlen(char *s) +{ + int len = 0; + while (*s++) len++; + return len; +} + +_printk(char const *fmt, ...) +{ + int ret; + va_list ap; + + va_start(ap, fmt); + ret = _vprintk(putc, fmt, ap); + va_end(ap); + return (ret); +} + +#define is_digit(c) ((c >= '0') && (c <= '9')) + +int +_vprintk(putc, fmt0, ap) +int (*putc)(); +const char *fmt0; +va_list ap; +{ + char c, sign, *cp; + int left_prec, right_prec, zero_fill, length, pad, pad_on_right; + char buf[32]; + long val; + while (c = *fmt0++) + { + if (c == '%') + { + c = *fmt0++; + left_prec = right_prec = pad_on_right = 0; + if (c == '-') + { + c = *fmt0++; + pad_on_right++; + } + if (c == '0') + { + zero_fill = TRUE; + c = *fmt0++; + } else + { + zero_fill = FALSE; + } + while (is_digit(c)) + { + left_prec = (left_prec * 10) + (c - '0'); + c = *fmt0++; + } + if (c == '.') + { + c = *fmt0++; + zero_fill++; + while (is_digit(c)) + { + right_prec = (right_prec * 10) + (c - '0'); + c = *fmt0++; + } + } else + { + right_prec = left_prec; + } + sign = '\0'; + switch (c) + { + case 'd': + case 'x': + case 'X': + val = va_arg(ap, long); + switch (c) + { + case 'd': + if (val < 0) + { + sign = '-'; + val = -val; + } + length = _cvt(val, buf, 10, "0123456789"); + break; + case 'x': + length = _cvt(val, buf, 16, "0123456789abcdef"); + break; + case 'X': + length = _cvt(val, buf, 16, "0123456789ABCDEF"); + break; + } + cp = buf; + break; + case 's': + cp = va_arg(ap, char *); + length = strlen(cp); + break; + case 'c': + c = va_arg(ap, long /*char*/); + (*putc)(c); + continue; + default: + (*putc)('?'); + } + pad = left_prec - length; + if (sign != '\0') + { + pad--; + } + if (zero_fill) + { + c = '0'; + if (sign != '\0') + { + (*putc)(sign); + sign = '\0'; + } + } else + { + c = ' '; + } + if (!pad_on_right) + { + while (pad-- > 0) + { + (*putc)(c); + } + } + if (sign != '\0') + { + (*putc)(sign); + } + while (length-- > 0) + { + (*putc)(c = *cp++); + if (c == '\n') + { + (*putc)('\r'); + } + } + if (pad_on_right) + { + while (pad-- > 0) + { + (*putc)(c); + } + } + } else + { + (*putc)(c); + if (c == '\n') + { + (*putc)('\r'); + } + } + } +} + +int _cvt(unsigned long val, char *buf, long radix, char *digits) +{ + char temp[80]; + char *cp = temp; + int length = 0; + if (val == 0) + { /* Special case */ + *cp++ = '0'; + } else + while (val) + { + *cp++ = digits[val % radix]; + val /= radix; + } + while (cp != temp) + { + *buf++ = *--cp; + length++; + } + *buf = '\0'; + return (length); +} + +_dump_buf_with_offset(unsigned char *p, int s, unsigned char *base) +{ + int i, c; + if ((unsigned int)s > (unsigned int)p) + { + s = (unsigned int)s - (unsigned int)p; + } + while (s > 0) + { + if (base) + { + _printk("%06X: ", (int)p - (int)base); + } else + { + _printk("%06X: ", p); + } + for (i = 0; i < 16; i++) + { + if (i < s) + { + _printk("%02X", p[i] & 0xFF); + } else + { + _printk(" "); + } + if ((i % 2) == 1) _printk(" "); + if ((i % 8) == 7) _printk(" "); + } + _printk(" |"); + for (i = 0; i < 16; i++) + { + if (i < s) + { + c = p[i] & 0xFF; + if ((c < 0x20) || (c >= 0x7F)) c = '.'; + } else + { + c = ' '; + } + _printk("%c", c); + } + _printk("|\n"); + s -= 16; + p += 16; + } +} + +_dump_buf(unsigned char *p, int s) +{ + _printk("\n"); + _dump_buf_with_offset(p, s, 0); } diff -u --recursive --new-file v2.2.3/linux/arch/ppc/boot/of1275.c linux/arch/ppc/boot/of1275.c --- v2.2.3/linux/arch/ppc/boot/of1275.c Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/boot/of1275.c Fri Mar 19 10:50:03 1999 @@ -0,0 +1,427 @@ +/* Open Firmware Client Interface */ + + +#include "of1275.h" + + +static int (*of_server)(void *) = (int(*)(void*))-1; + +void +of_init(void *handler) +{ + of_server = (int(*)(void*))handler; +} + + +/* 6.3.2.1 Client interface */ + + +int +of_test(const char *name, int *missing) +{ + int result; + static of_test_service s; + s.service = "test"; + s.n_args = 1; + s.n_returns = 1; + s.name = name; + result = of_server(&s); + *missing = s.missing; + return result; +} + + +/* 6.3.2.2 Device tree */ + + +int +of_peer(int phandle, int *sibling_phandle) +{ + int result; + static of_peer_service s; + s.service = "peer"; + s.n_args = 1; + s.n_returns = 1; + s.phandle = phandle; + result = of_server(&s); + *sibling_phandle = s.sibling_phandle; + return result; +} + +int +of_child(int phandle, int *child_phandle) +{ + int result; + static of_child_service s; + s.service = "child"; + s.n_args = 1; + s.n_returns = 1; + s.phandle = phandle; + result = of_server(&s); + *child_phandle = s.child_phandle; + return result; +} + +int +of_parent(int phandle, int *parent_phandle) +{ + int result; + static of_parent_service s; + s.service = "parent"; + s.n_args = 1; + s.n_returns = 1; + s.phandle = phandle; + result = of_server(&s); + *parent_phandle = s.parent_phandle; + return result; +} + +int +of_instance_to_package(int ihandle, int *phandle) +{ + int result; + static of_instance_to_package_service s; + s.service = "instance-to-package"; + s.n_args = 1; + s.n_returns = 1; + s.ihandle = ihandle; + result = of_server(&s); + *phandle = s.phandle; + return result; +} + +int +of_getproplen(int phandle, const char *name, int *proplen) +{ + int result; + static of_getproplen_service s; + s.service = "getproplen"; + s.n_args = 2; + s.n_returns = 1; + s.phandle = phandle; + s.name = name; + result = of_server(&s); + *proplen = s.proplen; + return result; +} + +int +of_getprop(int phandle, const char *name, void *buf, int buflen, int *size) +{ + int result; + static of_getprop_service s; + s.service = "getprop"; + s.n_args = 4; + s.n_returns = 1; + s.phandle = phandle; + s.name = name; + s.buf = buf; + s.buflen = buflen; + result = of_server(&s); + *size = s.size; + return result; +} + +int +of_nextprop(int phandle, const char *previous, void *buf, int *flag) +{ + int result; + static of_nextprop_service s; + s.service = "nextprop"; + s.n_args = 3; + s.n_returns = 1; + s.phandle = phandle; + s.previous = previous; + s.buf = buf; + result = of_server(&s); + *flag = s.flag; + return result; +} + +int +of_setprop(int phandle, const char *name, void *buf, int len, int *size) +{ + int result; + static of_setprop_service s; + s.service = "setprop"; + s.n_args = 4; + s.n_returns = 1; + s.phandle = phandle; + s.name = name; + s.buf = buf; + s.len = len; + result = of_server(&s); + *size = s.size; + return result; +} + +int +of_canon(const char *device_specifier, void *buf, int buflen, int *length) +{ + int result; + static of_canon_service s; + s.service = "canon"; + s.n_args = 3; + s.n_returns = 1; + s.device_specifier = device_specifier; + s.buf = buf; + s.buflen = buflen; + result = of_server(&s); + *length = s.length; + return result; +} + +int +of_finddevice(const char *device_specifier, int *phandle) +{ + int result; + static of_finddevice_service s; + s.service = "finddevice"; + s.n_args = 1; + s.n_returns = 1; + s.device_specifier = device_specifier; + result = of_server(&s); + *phandle = s.phandle; + return result; +} + +int +of_instance_to_path(int ihandle, void *buf, int buflen, int *length) +{ + int result; + static of_instance_to_path_service s; + s.service = "instance-to-path"; + s.n_args = 3; + s.n_returns = 1; + s.ihandle = ihandle; + s.buf = buf; + s.buflen = buflen; + result = of_server(&s); + *length = s.length; + return result; +} + +int +of_package_to_path(int phandle, void *buf, int buflen, int *length) +{ + int result; + static of_package_to_path_service s; + s.service = "package-to-path"; + s.n_args = 3; + s.n_returns = 1; + s.phandle = phandle; + s.buf = buf; + s.buflen = buflen; + result = of_server(&s); + *length = s.length; + return result; +} + +/* int of_call_method(const char *method, int ihandle, ...); */ + + +/* 6.3.2.3 Device I/O */ + + +int +of_open(const char *device_specifier, int *ihandle) +{ + int result; + static of_open_service s; + s.service = "open"; + s.n_args = 1; + s.n_returns = 1; + s.device_specifier = device_specifier; + result = of_server(&s); + *ihandle = s.ihandle; + return result; +} + +int +of_close(int ihandle) +{ + int result; + static of_close_service s; + s.service = "close"; + s.n_args = 1; + s.n_returns = 0; + s.ihandle = ihandle; + result = of_server(&s); + return result; +} + +int +of_read(int ihandle, void *addr, int len, int *actual) +{ + int result; + static of_read_service s; + s.service = "read"; + s.n_args = 3; + s.n_returns = 1; + s.ihandle = ihandle; + s.addr = addr; + s.len = len; + result = of_server(&s); + *actual = s.actual; + return result; +} + +int +of_write(int ihandle, void *addr, int len, int *actual) +{ + int result; + static of_write_service s; + s.service = "write"; + s.n_args = 3; + s.n_returns = 1; + s.ihandle = ihandle; + s.addr = addr; + s.len = len; + result = of_server(&s); + *actual = s.actual; + return result; +} + +int +of_seek(int ihandle, int pos_hi, int pos_lo, int *status) +{ + int result; + static of_seek_service s; + s.service = "seek"; + s.n_args = 3; + s.n_returns = 1; + s.ihandle = ihandle; + s.pos_hi = pos_hi; + s.pos_lo = pos_lo; + result = of_server(&s); + *status = s.status; + return result; +} + + +/* 6.3.2.4 Memory */ + + +int +of_claim(void *virt, int size, int align, void **baseaddr) +{ + int result; + static of_claim_service s; + s.service = "claim"; + s.n_args = 3; + s.n_returns = 1; + s.virt = virt; + s.size = size; + s.align = align; + result = of_server(&s); + *baseaddr = s.baseaddr; + return result; +} + +int +of_release(void *virt, int size) +{ + int result; + static of_release_service s; + s.service = "release"; + s.n_args = 2; + s.n_returns = 0; + s.virt = virt; + s.size = size; + result = of_server(&s); + return result; +} + + +/* 6.3.2.5 Control transfer */ + + +int +of_boot(const char *bootspec) +{ + int result; + static of_boot_service s; + s.service = "boot"; + s.n_args = 1; + s.n_returns = 0; + s.bootspec = bootspec; + result = of_server(&s); + return result; +} + +int +of_enter(void) +{ + int result; + static of_enter_service s; + s.service = "enter"; + s.n_args = 0; + s.n_returns = 0; + result = of_server(&s); + return result; +} + +int +of_exit(void) +{ + int result; + static of_exit_service s; + s.service = "exit"; + s.n_args = 0; + s.n_returns = 0; + result = of_server(&s); + return result; +} + +/* int of_chain(void *virt, int size, void *entry, void *args, int len); */ + + +/* 6.3.2.6 User interface */ + + +/* int of_interpret(const char *arg, ...); */ + +int +of_set_callback(void *newfunc, void **oldfunc) +{ + int result; + static of_set_callback_service s; + s.service = "set-callback"; + s.n_args = 1; + s.n_returns = 1; + s.newfunc = newfunc; + result = of_server(&s); + *oldfunc = s.oldfunc; + return result; +} + +int +of_set_symbol_lookup(void *sym_to_value, void *value_to_sym) +{ + int result; + static of_set_symbol_lookup_service s; + s.service = "set-symbol-lookup"; + s.n_args = 2; + s.n_returns = 0; + s.sym_to_value = sym_to_value; + s.value_to_sym = s.value_to_sym; + result = of_server(&s); + return result; +} + + +/* 6.3.2.7 Time */ + + +int +of_milliseconds(int *ms) +{ + int result; + static of_milliseconds_service s; + s.service = "milliseconds"; + s.n_args = 0; + s.n_returns = 1; + result = of_server(&s); + *ms = s.ms; + return result; +} diff -u --recursive --new-file v2.2.3/linux/arch/ppc/boot/of1275.h linux/arch/ppc/boot/of1275.h --- v2.2.3/linux/arch/ppc/boot/of1275.h Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/boot/of1275.h Fri Mar 19 10:50:03 1999 @@ -0,0 +1,421 @@ +/* 6.3.2.1 Client interface */ + + +typedef struct _of_test_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + const char *name; + /*out*/ + int missing; +} of_test_service; + +int of_test(const char *name, int *missing); + + +/* 6.3.2.2 Device tree */ + + +typedef struct _of_peer_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + int phandle; + /*out*/ + int sibling_phandle; +} of_peer_service; + +int of_peer(int phandle, int *sibling_phandle); + + +typedef struct _of_child_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + int phandle; + /*out*/ + int child_phandle; +} of_child_service; + +int of_child(int phandle, int *child_phandle); + + +typedef struct _of_parent_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + int phandle; + /*out*/ + int parent_phandle; +} of_parent_service; + +int of_child(int phandle, int *parent_phandle); + + +typedef struct _of_instance_to_package_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + int ihandle; + /*out*/ + int phandle; +} of_instance_to_package_service; + +int of_instance_to_package(int ihandle, int *phandle); + + +typedef struct _of_getproplen_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + int phandle; + const char *name; + /*out*/ + int proplen; +} of_getproplen_service; + +int of_getproplen(int phandle, const char *name, int *proplen); + + +typedef struct _of_getprop_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + int phandle; + const char *name; + void *buf; + int buflen; + /*out*/ + int size; +} of_getprop_service; + +int of_getprop(int phandle, const char *name, void *buf, int buflen, + int *size); + + +typedef struct _of_nextprop_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + int phandle; + const char *previous; + void *buf; + /*out*/ + int flag; +} of_nextprop_service; + +int of_nextprop(int phandle, const char *previous, void *buf, int *flag); + + +typedef struct _of_setprop_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + int phandle; + const char *name; + void *buf; + int len; + /*out*/ + int size; +} of_setprop_service; + +int of_setprop(int phandle, const char *name, void *buf, int len, int *size); + + +typedef struct _of_canon_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + const char *device_specifier; + void *buf; + int buflen; + /*out*/ + int length; +} of_canon_service; + +int of_canon(const char *device_specifier, void *buf, int buflen, int *length); + + +typedef struct _of_finddevice_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + const char *device_specifier; + /*out*/ + int phandle; +} of_finddevice_service; + +int of_finddevice(const char *device_specifier, int *phandle); + + +typedef struct _of_instance_to_path_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + int ihandle; + void *buf; + int buflen; + /*out*/ + int length; +} of_instance_to_path_service; + +int of_instance_to_path(int ihandle, void *buf, int buflen, int *length); + + +typedef struct _of_package_to_path_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + int phandle; + void *buf; + int buflen; + /*out*/ + int length; +} of_package_to_path_service; + +int of_package_to_path(int phandle, void *buf, int buflen, int *length); + + +typedef struct _of_call_method_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + const char *method; + int ihandle; + /*...*/ + int args[0]; +} of_call_method_service; + +int of_call_method(const char *method, int ihandle, ...); + + +/* 6.3.2.3 Device I/O */ + + +typedef struct _of_open_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + const char *device_specifier; + /*out*/ + int ihandle; +} of_open_service; + +int of_open(const char *device_specifier, + int *ihandle); + + +typedef struct _of_close_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + int ihandle; + /*out*/ +} of_close_service; + +int of_close(int ihandle); + + +typedef struct _of_read_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + int ihandle; + void *addr; + int len; + /*out*/ + int actual; +} of_read_service; + +int of_read(int ihandle, void *addr, int len, int *actual); + + +typedef struct _of_write_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + int ihandle; + void *addr; + int len; + /*out*/ + int actual; +} of_write_service; + +int of_write(int ihandle, void *addr, int len, int *actual); + + +typedef struct _of_seek_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + int ihandle; + int pos_hi; + int pos_lo; + /*out*/ + int status; +} of_seek_service; + +int of_seek(int ihandle, int pos_hi, int pos_lo, int *status); + + +/* 6.3.2.4 Memory */ + + +typedef struct _of_claim_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + void *virt; + int size; + int align; + /*out*/ + void *baseaddr; +} of_claim_service; + +int of_claim(void *virt, int size, int align, void **baseaddr); + + +typedef struct _of_release_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + void *virt; + int size; + int align; + /*out*/ +} of_release_service; + +int of_release(void *virt, int size); + + +/* 6.3.2.5 Control transfer */ + + +typedef struct _of_boot_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + const char *bootspec; + /*out*/ +} of_boot_service; + +int of_boot(const char *bootspec); + + +typedef struct _of_enter_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + /*out*/ +} of_enter_service; + +int of_enter(void); + + +typedef struct _of_exit_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + /*out*/ +} of_exit_service; + +int of_exit(void); + + +typedef struct _of_chain_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + void *virt; + int size; + void *entry; + void *args; + int len; + /*out*/ +} of_chain_service; + +int of_chain(void *virt, int size, void *entry, void *args, int len); + + +/* 6.3.2.6 User interface */ + + +typedef struct _of_interpret_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + const char *cmd; + int args[0]; + /*...*/ + /*out*/ + /*...*/ +} of_interpret_service; + +int of_interpret(const char *arg, ...); + + +typedef struct _of_set_callback_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + void *newfunc; + /*out*/ + void *oldfunc; +} of_set_callback_service; + +int of_set_callback(void *newfunc, void **oldfunc); + + +typedef struct _of_set_symbol_lookup_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + void *sym_to_value; + void *value_to_sym; + /*out*/ +} of_set_symbol_lookup_service; + +int of_set_symbol_lookup(void *sym_to_value, void *value_to_sym); + + +/* 6.3.2.7 Time */ + + +typedef struct _of_milliseconds_service { + const char *service; + int n_args; + int n_returns; + /*in*/ + /*out*/ + int ms; +} of_milliseconds_service; + +int of_milliseconds(int *ms); diff -u --recursive --new-file v2.2.3/linux/arch/ppc/boot/piggyback.c linux/arch/ppc/boot/piggyback.c --- v2.2.3/linux/arch/ppc/boot/piggyback.c Mon Jan 12 15:18:13 1998 +++ linux/arch/ppc/boot/piggyback.c Wed Dec 31 16:00:00 1969 @@ -1,64 +0,0 @@ -#include - -extern long ce_exec_config[]; - -main(int argc, char *argv[]) -{ - int i, cnt, pos, len; - unsigned int cksum, val; - unsigned char *lp; - unsigned char buf[8192]; - if (argc != 1) - { - fprintf(stderr, "usage: %s out-file\n", argv[0]); - exit(1); - } - fprintf(stdout, "#\n"); - fprintf(stdout, "# Miscellaneous data structures:\n"); - fprintf(stdout, "# WARNING - this file is automatically generated!\n"); - fprintf(stdout, "#\n"); - fprintf(stdout, "\n"); - fprintf(stdout, "\t.data\n"); - fprintf(stdout, "\t.globl input_data\n"); - fprintf(stdout, "input_data:\n"); - pos = 0; - cksum = 0; - while ((len = read(0, buf, sizeof(buf))) > 0) - { - cnt = 0; - lp = (unsigned char *)buf; - len = (len + 3) & ~3; /* Round up to longwords */ - for (i = 0; i < len; i += 4) - { - if (cnt == 0) - { - fprintf(stdout, "\t.long\t"); - } - fprintf(stdout, "0x%02X%02X%02X%02X", lp[0], lp[1], lp[2], lp[3]); - val = *(unsigned long *)lp; - cksum ^= val; - lp += 4; - if (++cnt == 4) - { - cnt = 0; - fprintf(stdout, " # %x \n", pos+i-12); - fflush(stdout); - } else - { - fprintf(stdout, ","); - } - } - if (cnt) - { - fprintf(stdout, "0\n"); - } - pos += len; - } - fprintf(stdout, "\t.globl input_len\n"); - fprintf(stdout, "input_len:\t.long\t0x%x\n", pos); - fflush(stdout); - fclose(stdout); - fprintf(stderr, "cksum = %x\n", cksum); - exit(0); -} - diff -u --recursive --new-file v2.2.3/linux/arch/ppc/chrpboot/Makefile linux/arch/ppc/chrpboot/Makefile --- v2.2.3/linux/arch/ppc/chrpboot/Makefile Thu Nov 19 09:56:27 1998 +++ linux/arch/ppc/chrpboot/Makefile Fri Mar 19 10:50:03 1999 @@ -28,6 +28,12 @@ CONFIG_CHRP = y endif +ifeq ($(CONFIG_SMP),y) +TFTPIMAGE=/tftpboot/zImage.chrp.smp +else +TFTPIMAGE=/tftpboot/zImage.chrp +endif + all: $(TOPDIR)/zImage # @@ -36,10 +42,10 @@ # ifeq ($(CONFIG_CHRP),y) znetboot: zImage - cp zImage /tftpboot/zImage.chrp + cp zImage $(TFTPIMAGE) znetboot.initrd: zImage.initrd - cp zImage.initrd /tftpboot/zImage.chrp + cp zImage.initrd $(TFTPIMAGE) floppy: zImage mcopy zImage a:zImage diff -u --recursive --new-file v2.2.3/linux/arch/ppc/coffboot/Makefile linux/arch/ppc/coffboot/Makefile --- v2.2.3/linux/arch/ppc/coffboot/Makefile Tue Dec 22 14:16:54 1998 +++ linux/arch/ppc/coffboot/Makefile Fri Mar 19 10:50:03 1999 @@ -23,6 +23,12 @@ CONFIG_PMAC = y endif +ifeq ($(CONFIG_SMP),y) +TFTPIMAGE=/tftpboot/zImage.pmac.smp +else +TFTPIMAGE=/tftpboot/zImage.pmac +endif + ifeq ($(CONFIG_PMAC),y) hack-coff: hack-coff.c $(HOSTCC) $(HOSTCFLAGS) -o hack-coff hack-coff.c @@ -33,10 +39,10 @@ # umount /mnt znetboot: vmlinux.coff - cp vmlinux.coff /tftpboot/zImage.pmac + cp vmlinux.coff $(TFTPIMAGE) znetboot.initrd: vmlinux.coff.initrd - cp vmlinux.coff.initrd /tftpboot/zImage.pmac + cp vmlinux.coff.initrd $(TFTPIMAGE) coffboot: $(OBJS) ld.script $(LD) -o coffboot $(LD_ARGS) $(OBJS) $(LIBS) diff -u --recursive --new-file v2.2.3/linux/arch/ppc/common_defconfig linux/arch/ppc/common_defconfig --- v2.2.3/linux/arch/ppc/common_defconfig Wed Mar 10 15:29:45 1999 +++ linux/arch/ppc/common_defconfig Fri Mar 19 10:50:03 1999 @@ -21,7 +21,7 @@ # CONFIG_EXPERIMENTAL=y CONFIG_MODULES=y -CONFIG_MODVERSIONS=y +# CONFIG_MODVERSIONS is not set CONFIG_KMOD=y CONFIG_PCI=y # CONFIG_PCI_QUIRKS is not set @@ -32,10 +32,10 @@ # CONFIG_BSD_PROCESS_ACCT is not set CONFIG_BINFMT_ELF=y CONFIG_KERNEL_ELF=y -# CONFIG_BINFMT_MISC is not set +CONFIG_BINFMT_MISC=m # CONFIG_BINFMT_JAVA is not set # CONFIG_PARPORT is not set -# CONFIG_VGA_CONSOLE is not set +CONFIG_VGA_CONSOLE=y CONFIG_FB=y CONFIG_FB_COMPAT_XPMAC=y CONFIG_PMAC_PBOOK=y @@ -64,14 +64,14 @@ CONFIG_BLK_DEV_IDEDISK=y CONFIG_BLK_DEV_IDECD=y # CONFIG_BLK_DEV_IDETAPE is not set -# CONFIG_BLK_DEV_IDEFLOPPY is not set +CONFIG_BLK_DEV_IDEFLOPPY=y # CONFIG_BLK_DEV_IDESCSI is not set # CONFIG_BLK_DEV_CMD640 is not set # CONFIG_BLK_DEV_RZ1000 is not set # CONFIG_BLK_DEV_IDEPCI is not set -CONFIG_BLK_DEV_SL82C105=y +# CONFIG_BLK_DEV_SL82C105 is not set # CONFIG_IDE_CHIPSETS is not set -CONFIG_BLK_DEV_LOOP=m +# CONFIG_BLK_DEV_LOOP is not set # CONFIG_BLK_DEV_NBD is not set # CONFIG_BLK_DEV_MD is not set CONFIG_BLK_DEV_RAM=y @@ -84,26 +84,29 @@ # # Networking options # -# CONFIG_PACKET is not set -# CONFIG_NETLINK is not set +CONFIG_PACKET=y +CONFIG_NETLINK=y +# CONFIG_RTNETLINK is not set +# CONFIG_NETLINK_DEV is not set # CONFIG_FIREWALL is not set # CONFIG_FILTER is not set CONFIG_UNIX=y CONFIG_INET=y -# CONFIG_IP_MULTICAST is not set +CONFIG_IP_MULTICAST=y # CONFIG_IP_ADVANCED_ROUTER is not set # CONFIG_IP_PNP is not set # CONFIG_IP_ROUTER is not set # CONFIG_NET_IPIP is not set # CONFIG_NET_IPGRE is not set -# CONFIG_IP_ALIAS is not set -CONFIG_SYN_COOKIES=y -# CONFIG_INET_RARP is not set -# CONFIG_IP_NOSR is not set +# CONFIG_IP_MROUTE is not set +CONFIG_IP_ALIAS=y +# CONFIG_SYN_COOKIES is not set +CONFIG_INET_RARP=y +CONFIG_IP_NOSR=y CONFIG_SKB_LARGE=y # CONFIG_IPV6 is not set # CONFIG_IPX is not set -# CONFIG_ATALK is not set +CONFIG_ATALK=m # CONFIG_X25 is not set # CONFIG_LAPB is not set # CONFIG_BRIDGE is not set @@ -124,12 +127,12 @@ # CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y -CONFIG_CHR_DEV_ST=m +CONFIG_CHR_DEV_ST=y CONFIG_BLK_DEV_SR=y CONFIG_BLK_DEV_SR_VENDOR=y # CONFIG_CHR_DEV_SG is not set # CONFIG_SCSI_MULTI_LUN is not set -# CONFIG_SCSI_CONSTANTS is not set +CONFIG_SCSI_CONSTANTS=y # CONFIG_SCSI_LOGGING is not set # @@ -140,7 +143,10 @@ # CONFIG_SCSI_AHA152X is not set # CONFIG_SCSI_AHA1542 is not set # CONFIG_SCSI_AHA1740 is not set -# CONFIG_SCSI_AIC7XXX is not set +CONFIG_SCSI_AIC7XXX=y +# CONFIG_OVERRIDE_CMDS is not set +CONFIG_AIC7XXX_PROC_STATS=y +CONFIG_AIC7XXX_RESET_DELAY=15 # CONFIG_SCSI_ADVANSYS is not set # CONFIG_SCSI_IN2000 is not set # CONFIG_SCSI_AM53C974 is not set @@ -158,10 +164,10 @@ # CONFIG_SCSI_NCR53C7xx is not set CONFIG_SCSI_NCR53C8XX=y CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8 -CONFIG_SCSI_NCR53C8XX_MAX_TAGS=4 -CONFIG_SCSI_NCR53C8XX_SYNC=5 +CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32 +CONFIG_SCSI_NCR53C8XX_SYNC=20 # CONFIG_SCSI_NCR53C8XX_PROFILE is not set -CONFIG_SCSI_NCR53C8XX_IOMAPPED=y +# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set # CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set # CONFIG_SCSI_PAS16 is not set # CONFIG_SCSI_PCI2000 is not set @@ -186,6 +192,7 @@ # CONFIG_ARCNET is not set # CONFIG_DUMMY is not set # CONFIG_EQUALIZER is not set +# CONFIG_ETHERTAP is not set CONFIG_NET_ETHERNET=y CONFIG_MACE=y CONFIG_BMAC=y @@ -198,7 +205,7 @@ # CONFIG_ACENIC is not set # CONFIG_NET_ISA is not set CONFIG_NET_EISA=y -CONFIG_PCNET32=m +# CONFIG_PCNET32 is not set # CONFIG_AC3200 is not set # CONFIG_APRICOT is not set # CONFIG_CS89x0 is not set @@ -218,16 +225,17 @@ # CONFIG_FDDI is not set # CONFIG_HIPPI is not set # CONFIG_DLCI is not set +# CONFIG_LTPC is not set +# CONFIG_COPS is not set +# CONFIG_IPDDP is not set CONFIG_PPP=y -CONFIG_SLIP=m -# CONFIG_SLIP_COMPRESSED is not set -# CONFIG_SLIP_SMART is not set -# CONFIG_SLIP_MODE_SLIP6 is not set +# CONFIG_SLIP is not set # CONFIG_NET_RADIO is not set # CONFIG_TR is not set # CONFIG_SHAPER is not set # CONFIG_HOSTESS_SV11 is not set # CONFIG_COSA is not set +# CONFIG_RCPCI is not set # # Amateur Radio support @@ -240,7 +248,7 @@ # CONFIG_ISDN is not set # -# CD-ROM drivers (not for SCSI or IDE/ATAPI drives) +# Old CD-ROM drivers (not SCSI, not IDE) # # CONFIG_CD_NO_IDESCSI is not set @@ -266,9 +274,9 @@ CONFIG_FBCON_CFB32=y # CONFIG_FBCON_FONTWIDTH8_ONLY is not set CONFIG_FBCON_FONTS=y -CONFIG_FONT_8x8=y +# CONFIG_FONT_8x8 is not set CONFIG_FONT_8x16=y -# CONFIG_FONT_SUN8x16 is not set +CONFIG_FONT_SUN8x16=y CONFIG_FONT_SUN12x22=y # CONFIG_FONT_6x11 is not set # CONFIG_FONT_PEARL_8x8 is not set @@ -279,21 +287,12 @@ # CONFIG_VT=y CONFIG_VT_CONSOLE=y -CONFIG_SERIAL=m +# CONFIG_SERIAL is not set # CONFIG_SERIAL_EXTENDED is not set # CONFIG_SERIAL_NONSTANDARD is not set -# CONFIG_UNIX98_PTYS is not set -CONFIG_MOUSE=y - -# -# Mice -# -# CONFIG_ATIXL_BUSMOUSE is not set -# CONFIG_BUSMOUSE is not set -# CONFIG_MS_BUSMOUSE is not set -CONFIG_PSMOUSE=y -# CONFIG_82C710_MOUSE is not set -# CONFIG_PC110_PAD is not set +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 +# CONFIG_MOUSE is not set # CONFIG_QIC02_TAPE is not set # CONFIG_WATCHDOG is not set # CONFIG_NVRAM is not set @@ -318,20 +317,21 @@ # Filesystems # # CONFIG_QUOTA is not set -# CONFIG_AUTOFS_FS is not set +CONFIG_AUTOFS_FS=y # CONFIG_ADFS_FS is not set # CONFIG_AFFS_FS is not set CONFIG_HFS_FS=y CONFIG_FAT_FS=m CONFIG_MSDOS_FS=m # CONFIG_UMSDOS_FS is not set -# CONFIG_VFAT_FS is not set +CONFIG_VFAT_FS=m CONFIG_ISO9660_FS=y # CONFIG_JOLIET is not set # CONFIG_MINIX_FS is not set # CONFIG_NTFS_FS is not set # CONFIG_HPFS_FS is not set CONFIG_PROC_FS=y +CONFIG_DEVPTS_FS=y # CONFIG_QNX4FS_FS is not set # CONFIG_ROMFS_FS is not set CONFIG_EXT2_FS=y @@ -343,7 +343,7 @@ # # CONFIG_CODA_FS is not set CONFIG_NFS_FS=y -CONFIG_NFSD=m +CONFIG_NFSD=y # CONFIG_NFSD_SUN is not set CONFIG_SUNRPC=y CONFIG_LOCKD=y @@ -363,7 +363,7 @@ # # Native Language Support # -# CONFIG_NLS_CODEPAGE_437 is not set +CONFIG_NLS_CODEPAGE_437=y # CONFIG_NLS_CODEPAGE_737 is not set # CONFIG_NLS_CODEPAGE_775 is not set # CONFIG_NLS_CODEPAGE_850 is not set @@ -401,30 +401,4 @@ # CONFIG_SOUND_SONICVIBES is not set # CONFIG_SOUND_MSNDCLAS is not set # CONFIG_SOUND_MSNDPIN is not set -CONFIG_SOUND_OSS=y -# CONFIG_SOUND_PAS is not set -# CONFIG_SOUND_SB is not set -# CONFIG_SOUND_ADLIB is not set -# CONFIG_SOUND_GUS is not set -# CONFIG_SOUND_MPU401 is not set -# CONFIG_SOUND_PSS is not set -# CONFIG_SOUND_MSS is not set -# CONFIG_SOUND_SSCAPE is not set -# CONFIG_SOUND_TRIX is not set -# CONFIG_SOUND_MAD16 is not set -# CONFIG_SOUND_WAVEFRONT is not set -# CONFIG_SOUND_CS4232 is not set -# CONFIG_SOUND_OPL3SA2 is not set -# CONFIG_SOUND_MAUI is not set -# CONFIG_SOUND_SGALAXY is not set -# CONFIG_SOUND_AD1816 is not set -# CONFIG_SOUND_OPL3SA1 is not set -# CONFIG_SOUND_SOFTOSS is not set -# CONFIG_SOUND_YM3812 is not set -# CONFIG_SOUND_VMIDI is not set -# CONFIG_SOUND_UART6850 is not set - -# -# Additional low level sound drivers -# -# CONFIG_LOWLEVEL_SOUND is not set +# CONFIG_SOUND_OSS is not set diff -u --recursive --new-file v2.2.3/linux/arch/ppc/config.in linux/arch/ppc/config.in --- v2.2.3/linux/arch/ppc/config.in Tue Feb 23 15:21:32 1999 +++ linux/arch/ppc/config.in Wed Mar 10 21:30:31 1999 @@ -1,4 +1,4 @@ -# $Id: config.in,v 1.80 1998/11/11 03:54:56 paulus Exp $ +# $Id: config.in,v 1.84 1999/02/23 08:08:38 davem Exp $ # For a description of the syntax of this configuration file, # see the Configure script. # @@ -172,7 +172,7 @@ comment 'Sound' tristate 'Sound card support' CONFIG_SOUND if [ "$CONFIG_SOUND" != "n" ]; then - tristate 'Amiga or PowerMac DMA sound support' CONFIG_DMASOUND + dep_tristate 'Amiga or PowerMac DMA sound support' CONFIG_DMASOUND $CONFIG_SOUND source drivers/sound/Config.in fi diff -u --recursive --new-file v2.2.3/linux/arch/ppc/defconfig linux/arch/ppc/defconfig --- v2.2.3/linux/arch/ppc/defconfig Wed Mar 10 15:29:45 1999 +++ linux/arch/ppc/defconfig Wed Mar 10 21:30:31 1999 @@ -262,6 +262,7 @@ # CONFIG_SHAPER is not set # CONFIG_HOSTESS_SV11 is not set # CONFIG_COSA is not set +# CONFIG_RCPCI is not set # # Amateur Radio support diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/chrp_pci.c linux/arch/ppc/kernel/chrp_pci.c --- v2.2.3/linux/arch/ppc/kernel/chrp_pci.c Thu Aug 6 14:06:29 1998 +++ linux/arch/ppc/kernel/chrp_pci.c Fri Mar 19 10:50:03 1999 @@ -170,6 +170,79 @@ return PCIBIOS_SUCCESSFUL; } +#define python_config_address(bus) (unsigned *)((0xfef00000+0xf8000)-(bus*0x100000)) +#define python_config_data(bus) ((0xfef00000+0xf8010)-(bus*0x100000)) +#define PYTHON_CFA(b, d, o) (0x80 | ((b) << 8) | ((d) << 16) \ + | (((o) & ~3) << 24)) + +int python_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char *val) +{ + if (bus > 2) { + *val = 0xff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + out_be32( python_config_address( bus ), PYTHON_CFA(bus,dev_fn,offset) ); + *val = in_8((unsigned char *)python_config_data(bus) + (offset&3)); + return PCIBIOS_SUCCESSFUL; +} + +int python_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val) +{ + if (bus > 2) { + *val = 0xffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + out_be32( python_config_address( bus ), PYTHON_CFA(bus,dev_fn,offset) ); + *val = in_le16((unsigned short *)(python_config_data(bus) + (offset&3))); + return PCIBIOS_SUCCESSFUL; +} + + +int python_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int *val) +{ + if (bus > 2) { + *val = 0xffffffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + out_be32( python_config_address( bus ), PYTHON_CFA(bus,dev_fn,offset) ); + *val = in_le32((unsigned *)python_config_data(bus)); + return PCIBIOS_SUCCESSFUL; +} + +int python_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char val) +{ + if (bus > 2) + return PCIBIOS_DEVICE_NOT_FOUND; + out_be32( python_config_address( bus ), PYTHON_CFA(bus,dev_fn,offset) ); + out_8((volatile unsigned char *)python_config_data(bus) + (offset&3), val); + return PCIBIOS_SUCCESSFUL; +} + +int python_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val) +{ + if (bus > 2) + return PCIBIOS_DEVICE_NOT_FOUND; + out_be32( python_config_address( bus ), PYTHON_CFA(bus,dev_fn,offset) ); + out_le16((volatile unsigned short *)python_config_data(bus) + (offset&3), + val); + return PCIBIOS_SUCCESSFUL; +} + +int python_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int val) +{ + if (bus > 2) + return PCIBIOS_DEVICE_NOT_FOUND; + out_be32( python_config_address( bus ), PYTHON_CFA(bus,dev_fn,offset) ); + out_le32((unsigned *)python_config_data(bus) + (offset&3), val); + return PCIBIOS_SUCCESSFUL; +} + /* * Temporary fixes for PCI devices. These should be replaced by OF query * code -- Geert @@ -214,77 +287,4 @@ OpenPIC_InitSenses = hydra_openpic_initsenses; OpenPIC_NumInitSenses = sizeof(hydra_openpic_initsenses); return 1; -} - - -extern int chrp_ide_irq; - -__initfunc(int w83c553f_init(void)) -{ - u_char bus, dev; -#if 0 - unsigned char t8; - unsigned short t16; -#endif - unsigned int t32; - struct pci_dev *pdev; - if ((pdev = pci_find_device(PCI_VENDOR_ID_WINBOND, - PCI_DEVICE_ID_WINBOND_83C553, NULL))) { - bus = pdev->bus->number; - dev = pdev->devfn + 1; - pcibios_read_config_dword(bus, dev, PCI_VENDOR_ID, &t32); - if (t32 == (PCI_DEVICE_ID_WINBOND_82C105<<16) + PCI_VENDOR_ID_WINBOND) { -#if 0 - printk("Enabling SL82C105 IDE on W83C553F\n"); - /* - * FIXME: this doesn't help :-( - */ - - /* I/O mapping */ - pcibios_read_config_word(bus, dev, PCI_COMMAND, &t16); - t16 |= PCI_COMMAND_IO; - pcibios_write_config_word(bus, dev, PCI_COMMAND, t16); - - /* Standard IDE registers */ - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_0, - 0xffffffff); - pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_0, &t32); - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_0, - 0x000001f0 | 1); - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_1, - 0xffffffff); - pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_1, &t32); - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_1, - 0x000003f4 | 1); - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_2, - 0xffffffff); - pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_2, &t32); - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_2, - 0x00000170 | 1); - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_3, - 0xffffffff); - pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_3, &t32); - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_3, - 0x00000374 | 1); - - /* IDE Bus Master Control */ - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_4, - 0xffffffff); - pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_4, &t32); - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_4, - 0x1000 | 1); - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_5, - 0xffffffff); - pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_5, &t32); - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_5, - 0x1010 | 1); - - /* IDE Interrupt */ - pcibios_read_config_byte(bus, dev, PCI_INTERRUPT_LINE, &t8); - chrp_ide_irq = t8; -#endif - return 1; - } - } - return 0; } diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/chrp_setup.c linux/arch/ppc/kernel/chrp_setup.c --- v2.2.3/linux/arch/ppc/kernel/chrp_setup.c Thu Nov 19 09:56:27 1998 +++ linux/arch/ppc/kernel/chrp_setup.c Fri Mar 19 10:50:03 1999 @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -42,7 +43,6 @@ #include extern void hydra_init(void); -extern void w83c553f_init(void); /* for the mac fs */ kdev_t boot_dev; @@ -53,7 +53,6 @@ extern unsigned long loops_per_sec; unsigned long empty_zero_page[1024]; -extern unsigned char aux_device_present; #ifdef CONFIG_BLK_DEV_RAM extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */ @@ -88,46 +87,53 @@ model = get_property(root, "model", NULL); len = sprintf(buffer,"machine\t\t: CHRP %s\n", model); - /* VLSI VAS96011/12 `Golden Gate 2' */ - /* Memory banks */ - sdramen = (in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+GG2_PCI_DRAM_CTRL)) - >>31) & 1; - for (i = 0; i < (sdramen ? 4 : 6); i++) { - t = in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+GG2_PCI_DRAM_BANK0+ - i*4)); - if (!(t & 1)) - continue; - switch ((t>>8) & 0x1f) { - case 0x1f: - model = "4 MB"; - break; - case 0x1e: - model = "8 MB"; - break; - case 0x1c: - model = "16 MB"; - break; - case 0x18: - model = "32 MB"; - break; - case 0x10: - model = "64 MB"; - break; - case 0x00: - model = "128 MB"; - break; - default: - model = "Reserved"; - break; - } - len += sprintf(buffer+len, "memory bank %d\t: %s %s\n", i, model, - gg2_memtypes[sdramen ? 1 : ((t>>1) & 3)]); + /* longtrail (goldengate) stuff */ + if ( !strncmp( model, "IBM,LongTrail", 9 ) ) + { + /* VLSI VAS96011/12 `Golden Gate 2' */ + /* Memory banks */ + sdramen = (in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+ + GG2_PCI_DRAM_CTRL)) + >>31) & 1; + for (i = 0; i < (sdramen ? 4 : 6); i++) { + t = in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+ + GG2_PCI_DRAM_BANK0+ + i*4)); + if (!(t & 1)) + continue; + switch ((t>>8) & 0x1f) { + case 0x1f: + model = "4 MB"; + break; + case 0x1e: + model = "8 MB"; + break; + case 0x1c: + model = "16 MB"; + break; + case 0x18: + model = "32 MB"; + break; + case 0x10: + model = "64 MB"; + break; + case 0x00: + model = "128 MB"; + break; + default: + model = "Reserved"; + break; + } + len += sprintf(buffer+len, "memory bank %d\t: %s %s\n", i, model, + gg2_memtypes[sdramen ? 1 : ((t>>1) & 3)]); + } + /* L2 cache */ + t = in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+GG2_PCI_CC_CTRL)); + len += sprintf(buffer+len, "board l2\t: %s %s (%s)\n", + gg2_cachesizes[(t>>7) & 3], + gg2_cachetypes[(t>>2) & 3], + gg2_cachemodes[t & 3]); } - /* L2 cache */ - t = in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+GG2_PCI_CC_CTRL)); - len += sprintf(buffer+len, "board l2\t: %s %s (%s)\n", - gg2_cachesizes[(t>>7) & 3], gg2_cachetypes[(t>>2) & 3], - gg2_cachemodes[t & 3]); return len; } @@ -190,8 +196,6 @@ /* init to some ~sane value until calibrate_delay() runs */ loops_per_sec = 50000000; - aux_device_present = 0xaa; - #ifdef CONFIG_BLK_DEV_INITRD /* this is fine for chrp */ initrd_below_start_ok = 1; @@ -219,8 +223,15 @@ * -- Geert */ hydra_init(); /* Mac I/O */ - w83c553f_init(); /* PCI-ISA bridge and IDE */ + /* Some IBM machines don't have the hydra -- Cort */ + if ( !OpenPIC ) + { + OpenPIC = (struct OpenPIC *)*(unsigned long *)get_property( + find_path_device("/"), "platform-open-pic", NULL); + OpenPIC = ioremap((unsigned long)OpenPIC, sizeof(struct OpenPIC)); + } + /* * Fix the Super I/O configuration */ diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/chrp_time.c linux/arch/ppc/kernel/chrp_time.c --- v2.2.3/linux/arch/ppc/kernel/chrp_time.c Fri May 8 23:14:44 1998 +++ linux/arch/ppc/kernel/chrp_time.c Fri Mar 19 10:50:03 1999 @@ -154,7 +154,8 @@ __initfunc(void chrp_calibrate_decr(void)) { struct device_node *cpu; - int freq, *fp, divisor; + int *fp, divisor; + unsigned long freq; if (via_calibrate_decr()) return; @@ -170,10 +171,9 @@ if (fp != 0) freq = *fp; } - freq *= 60; /* try to make freq/1e6 an integer */ divisor = 60; - printk("time_init: decrementer frequency = %d/%d\n", freq, divisor); + printk("time_init: decrementer frequency = %lu/%d\n", freq, divisor); decrementer_count = freq / HZ / divisor; count_period_num = divisor; count_period_den = freq / 1000000; diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/feature.c linux/arch/ppc/kernel/feature.c --- v2.2.3/linux/arch/ppc/kernel/feature.c Thu Nov 19 09:56:27 1998 +++ linux/arch/ppc/kernel/feature.c Wed Mar 10 21:30:31 1999 @@ -41,7 +41,6 @@ OH_BAY_FLOPPY_ENABLE, /* FEATURE_Mediabay_floppy_enable */ 0, /* FEATURE_BMac_reset */ 0, /* FEATURE_BMac_IO_enable */ - 0, /* FEATURE_Modem_PowerOn -> guess...*/ 0 /* FEATURE_Modem_Reset -> guess...*/ }; @@ -64,8 +63,7 @@ OH_BAY_FLOPPY_ENABLE, /* FEATURE_Mediabay_floppy_enable */ 0x80000000, /* FEATURE_BMac_reset */ 0x60000000, /* FEATURE_BMac_IO_enable */ - 0x02000000, /* FEATURE_Modem_PowerOn -> guess...*/ - 0x07000000 /* FEATURE_Modem_Reset -> guess...*/ + 0x02000000 /* FEATURE_Modem_Reset -> guess...*/ }; /* definition of a feature controller object */ diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/head.S linux/arch/ppc/kernel/head.S --- v2.2.3/linux/arch/ppc/kernel/head.S Thu Dec 31 10:28:59 1998 +++ linux/arch/ppc/kernel/head.S Fri Mar 19 10:50:03 1999 @@ -1,7 +1,7 @@ /* * arch/ppc/kernel/head.S * - * $Id: head.S,v 1.114 1998/12/28 10:28:45 paulus Exp $ + * $Id: head.S,v 1.121 1999/03/16 10:40:29 cort Exp $ * * PowerPC version * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) @@ -44,8 +44,13 @@ /* optimization for 603 to load the tlb directly from the linux table */ #define NO_RELOAD_HTAB 1 +#ifndef CONFIG_8xx CACHE_LINE_SIZE = 32 LG_CACHE_LINE_SIZE = 5 +#else +CACHE_LINE_SIZE = 16 +LG_CACHE_LINE_SIZE = 4 +#endif #define TOPHYS(x) (x - KERNELBASE) @@ -81,7 +86,6 @@ sync; \ isync -/* This instruction is not implemented on the PPC 603 or 601 */ #ifndef CONFIG_8xx /* This instruction is not implemented on the PPC 603 or 601 */ #define tlbia \ @@ -212,6 +216,7 @@ mr r29,r5 mr r28,r6 mr r27,r7 + li r24,0 /* cpu # */ #ifndef CONFIG_8xx bl prom_init .globl __secondary_start @@ -236,15 +241,33 @@ mtspr IBAT1L,r10 b 5f 4: -#ifndef CONFIG_APUS - ori r11,r11,0x1fe /* set up BAT registers for 604 */ - li r8,2 /* R/W access */ -#else +#ifdef CONFIG_APUS + ori r11,r11,BL_8M<<2|0x2 /* set up an 8MB mapping */ ori r11,r11,0xfe /* set up an 8MB mapping */ lis r8,CYBERBASEp@h lwz r8,0(r8) addis r8,r8,KERNELBASE@h addi r8,r8,2 +#else + ori r11,r11,BL_256M<<2|0x2 /* set up BAT registers for 604 */ + li r8,2 /* R/W access */ + /* + * allow secondary cpus to get at all of ram in early bootup + * since their init_task may be up there -- Cort + */ + oris r18,r8,0x10000000@h + oris r21,r11,(KERNELBASE+0x10000000)@h + mtspr DBAT1L,r18 /* N.B. 6xx (not 601) have valid */ + mtspr DBAT1U,r21 /* bit in upper BAT register */ + mtspr IBAT1L,r18 + mtspr IBAT1U,r21 + + oris r18,r8,0x20000000@h + oris r21,r11,(KERNELBASE+0x20000000)@h + mtspr DBAT2L,r18 /* N.B. 6xx (not 601) have valid */ + mtspr DBAT2U,r21 /* bit in upper BAT register */ + mtspr IBAT2L,r28 + mtspr IBAT2U,r21 #endif mtspr DBAT0L,r8 /* N.B. 6xx (not 601) have valid */ mtspr DBAT0U,r11 /* bit in upper BAT register */ @@ -327,20 +350,28 @@ lis r8, MI_Kp@h /* Set the protection mode */ mtspr MI_AP, r8 mtspr MD_AP, r8 -#ifdef CONFIG_MBX + +/* We will get these from a configuration file as soon as I verify + * the extraneous bits don't cause problems in the TLB. + */ +#if defined(CONFIG_MBX) || defined(CONFIG_RPXLITE) +#define BOOT_IMMR 0xfa000000 +#endif +#ifdef CONFIG_BSEIP +#define BOOT_IMMR 0xff000000 +#endif /* Map another 8 MByte at 0xfa000000 to get the processor * internal registers (among other things). */ - lis r8, 0xfa000000@h /* Create vaddr for TLB */ + lis r8, BOOT_IMMR@h /* Create vaddr for TLB */ ori r8, r8, MD_EVALID /* Mark it valid */ mtspr MD_EPN, r8 li r8, MD_PS8MEG /* Set 8M byte page */ ori r8, r8, MD_SVALID /* Make it valid */ mtspr MD_TWC, r8 - lis r8, 0xfa000000@h /* Create paddr for TLB */ + lis r8, BOOT_IMMR@h /* Create paddr for TLB */ ori r8, r8, MI_BOOTINIT|0x2 /* Inhibit cache -- Cort */ mtspr MD_RPN, r8 -#endif /* Since the cache is enabled according to the information we * just loaded into the TLB, invalidate and enable the caches here. @@ -354,9 +385,8 @@ #if 0 mtspr DC_CST, r8 #else - /* I still have a bug somewhere because the Ethernet driver - * does not want to work with copyback enabled. For now, - * at least enable write through. + /* For a debug option, I left this here to easily enable + * the write through cache mode */ lis r8, DC_SFWT@h mtspr DC_CST, r8 @@ -442,7 +472,11 @@ .long int_return /* System reset */ +#ifdef CONFIG_SMP /* MVME/MTX start the secondary here */ + STD_EXCEPTION(0x100, Reset, __secondary_start_psurge) +#else STD_EXCEPTION(0x100, Reset, UnknownException) +#endif /* Machine check */ STD_EXCEPTION(0x200, MachineCheck, MachineCheckException) @@ -1148,6 +1182,8 @@ mflr r23 andi. r24,r23,0x3f00 /* get vector offset */ stw r24,TRAP(r21) + li r22,RESULT + stwcx. r22,r22,r21 /* to clear the reservation */ li r22,0 stw r22,RESULT(r21) mtspr SPRG2,r22 /* r1 is now kernel sp */ @@ -1155,7 +1191,7 @@ cmplw 0,r1,r2 cmplw 1,r1,r24 crand 1,1,4 - bgt stack_ovf /* if r2 < r1 < r2+TASK_STRUCT_SIZE */ + bgt- stack_ovf /* if r2 < r1 < r2+TASK_STRUCT_SIZE */ lwz r24,0(r23) /* virtual address of handler */ lwz r23,4(r23) /* where to go when done */ mtspr SRR0,r24 @@ -1204,13 +1240,10 @@ Hash_bits = 12 /* e.g. 256kB hash table */ Hash_msk = (((1 << Hash_bits) - 1) * 64) - .globl hash_table_lock -hash_table_lock: -.long 0 - .globl hash_page hash_page: #ifdef __SMP__ + eieio lis r2,hash_table_lock@h ori r2,r2,hash_table_lock@l tophys(r2,r2,r6) @@ -1226,7 +1259,7 @@ 12: cmpw r6,r0 bdnzf 2,10b tw 31,31,31 -11: +11: eieio #endif /* Get PTE (linux-style) and check access */ lwz r5,PG_TABLES(r5) @@ -1234,13 +1267,25 @@ rlwimi r5,r3,12,20,29 /* insert top 10 bits of address */ lwz r5,0(r5) /* get pmd entry */ rlwinm. r5,r5,0,0,19 /* extract address of pte page */ +#ifdef __SMP__ beq- hash_page_out /* return if no mapping */ +#else + /* XXX it seems like the 601 will give a machine fault on the + rfi if its alignment is wrong (bottom 4 bits of address are + 8 or 0xc) and we have had a not-taken conditional branch + to the address following the rfi. */ + beqlr- +#endif tophys(r2,r5,r2) rlwimi r2,r3,22,20,29 /* insert next 10 bits of address */ lwz r6,0(r2) /* get linux-style pte */ ori r4,r4,1 /* set _PAGE_PRESENT bit in access */ andc. r0,r4,r6 /* check access & ~permission */ +#ifdef __SMP__ bne- hash_page_out /* return if access not permitted */ +#else + bnelr- +#endif ori r6,r6,0x100 /* set _PAGE_ACCESSED in pte */ rlwinm r5,r4,5,24,24 /* _PAGE_RW access -> _PAGE_DIRTY */ @@ -1257,7 +1302,9 @@ /* Construct the high word of the PPC-style PTE */ mfsrin r5,r3 /* get segment reg for segment */ rlwinm r5,r5,7,1,24 /* put VSID in 0x7fffff80 bits */ +#ifndef __SMP__ /* do this later for SMP */ oris r5,r5,0x8000 /* set V (valid) bit */ +#endif rlwimi r5,r3,10,26,31 /* put in API (abbrev page index) */ /* Get the address of the primary PTE group in the hash table */ @@ -1274,9 +1321,6 @@ li r2,8 /* PTEs/group */ bne 10f /* no PTE: go look for an empty slot */ tlbie r3 /* invalidate TLB entry */ -#ifdef __SMP__ - tlbsync -#endif /* Search the primary PTEG for a PTE whose 1st word matches r5 */ mtctr r2 @@ -1345,18 +1389,43 @@ addi r4,r4,1 stw r4,0(r2) +#ifndef __SMP__ /* Store PTE in PTEG */ found_empty: stw r5,0(r3) found_slot: stw r6,4(r3) - SYNC + sync + +#else /* __SMP__ */ /* - * These nop's seem to be necessary to avoid getting a machine - * check on the rfi on 601 processors. + * Between the tlbie above and updating the hash table entry below, + * another CPU could read the hash table entry and put it in its TLB. + * There are 3 cases: + * 1. using an empty slot + * 2. updating an earlier entry to change permissions (i.e. enable write) + * 3. taking over the PTE for an unrelated address + * + * In each case it doesn't really matter if the other CPUs have the old + * PTE in their TLB. So we don't need to bother with another tlbie here, + * which is convenient as we've overwritten the register that had the + * address. :-) The tlbie above is mainly to make sure that this CPU comes + * and gets the new PTE from the hash table. + * + * We do however have to make sure that the PTE is never in an invalid + * state with the V bit set. */ - nop - nop +found_empty: +found_slot: + stw r5,0(r3) /* clear V (valid) bit in PTE */ + sync + tlbsync + sync + stw r6,4(r3) /* put in correct RPN, WIMG, PP bits */ + sync + oris r5,r5,0x8000 + stw r5,0(r3) /* finally set V bit in PTE */ +#endif /* __SMP__ */ /* * Update the hash table miss count. We only want misses here @@ -1380,6 +1449,7 @@ tophys(r2,r2,r6) li r0,0 stw r0,hash_table_lock@l(r2) + eieio #endif /* Return from the exception */ @@ -1398,17 +1468,22 @@ REST_GPR(20, r21) REST_2GPRS(22, r21) lwz r21,GPR21(r21) - SYNC rfi -hash_page_out: #ifdef __SMP__ +hash_page_out: lis r2,hash_table_lock@ha tophys(r2,r2,r6) li r0,0 stw r0,hash_table_lock@l(r2) -#endif + eieio blr + + .globl hash_table_lock +hash_table_lock: + .long 0 +#endif + next_slot: .long 0 @@ -1600,10 +1675,38 @@ . = 0x4000 #endif +#ifdef CONFIG_SMP + .globl __secondary_start_psurge +__secondary_start_psurge: + li r24,1 /* cpu # */ + b __secondary_start + + .globl __secondary_hold +__secondary_hold: + /* tell the master we're here */ + lis r5,0x4@h + ori r5,r5,0x4@l + stw r3,0(r5) + dcbf 0,r5 +100: + lis r5,0 + dcbi 0,r5 + lwz r4,0(r5) + /* wait until we're told to start */ + cmp 0,r4,r3 + bne 100b + /* our cpu # was at addr 0 - go */ + lis r5,__secondary_start@h + ori r5,r5,__secondary_start@l + tophys(r5,r5,r4) + mtlr r5 + mr r24,r3 /* cpu # */ + blr +#endif /* CONFIG_SMP */ + /* * This is where the main kernel code starts. */ - start_here: #ifndef CONFIG_8xx /* @@ -1650,9 +1753,9 @@ /* get current */ lis r2,current_set@h ori r2,r2,current_set@l - addi r2,r2,4 + slwi r24,r24,2 /* cpu # to current_set[cpu#] */ + add r2,r2,r24 lwz r2,0(r2) - b 10f 99: #endif /* __SMP__ */ @@ -1677,12 +1780,10 @@ #ifdef __SMP__ 10: #endif /* __SMP__ */ - /* stack */ addi r1,r2,TASK_UNION_SIZE li r0,0 stwu r0,-STACK_FRAME_OVERHEAD(r1) - /* * Decide what sort of machine this is and initialize the MMU. */ @@ -1693,7 +1794,6 @@ mr r7,r27 bl identify_machine bl MMU_init - /* * Go back to running unmapped so we can load up new values * for SDR1 (hash table pointer) and the segment registers @@ -1725,8 +1825,10 @@ 2: SYNC /* Force all PTE updates to finish */ tlbia /* Clear all TLB entries */ + sync /* wait for tlbia/tlbie to finish */ #ifdef __SMP__ - tlbsync + tlbsync /* ... on all CPUs */ + sync #endif #ifndef CONFIG_8xx mtspr SDR1,r6 @@ -1976,7 +2078,7 @@ /* Set up segment registers for new task */ rlwinm r5,r5,4,8,27 /* VSID = context << 4 */ addis r5,r5,0x6000 /* Set Ks, Ku bits */ - li r0,8 /* TASK_SIZE / SEGMENT_SIZE */ + li r0,12 /* TASK_SIZE / SEGMENT_SIZE */ mtctr r0 li r3,0 3: mtsrin r5,r3 @@ -2118,7 +2220,7 @@ _GLOBAL(set_context) rlwinm r3,r3,4,8,27 /* VSID = context << 4 */ addis r3,r3,0x6000 /* Set Ks, Ku bits */ - li r0,8 /* TASK_SIZE / SEGMENT_SIZE */ + li r0,12 /* TASK_SIZE / SEGMENT_SIZE */ mtctr r0 li r4,0 3: mtsrin r3,r4 @@ -2177,6 +2279,27 @@ blr /* + * Like above, but only do the D-cache. This is used by the 8xx + * to push the cache so the CPM doesn't get stale data. + * + * flush_dcache_range(unsigned long start, unsigned long stop) + */ +_GLOBAL(flush_dcache_range) + li r5,CACHE_LINE_SIZE-1 + andc r3,r3,r5 + subf r4,r3,r4 + add r4,r4,r5 + srwi. r4,r4,LG_CACHE_LINE_SIZE + beqlr + mtctr r4 + +1: dcbst 0,r3 + addi r3,r3,CACHE_LINE_SIZE + bdnz 1b + sync /* wait for dcbst's to get to ram */ + blr + +/* * Flush a particular page from the DATA cache * Note: this is necessary because the instruction cache does *not* * snoop from the data cache. @@ -2207,25 +2330,35 @@ blr /* + * Clear a page using the dcbz instruction, which doesn't cause any + * memory traffic (except to write out any cache lines which get + * displaced). This only works on cacheable memory. + */ +_GLOBAL(clear_page) + li r0,4096/CACHE_LINE_SIZE + mtctr r0 +1: dcbz 0,r3 + addi r3,r3,CACHE_LINE_SIZE + bdnz 1b + blr + +/* * Flush entries from the hash table with VSIDs in the range * given. */ #ifndef CONFIG_8xx _GLOBAL(flush_hash_segments) + lis r5,Hash@ha + lwz r5,Hash@l(r5) /* base of hash table */ #ifdef NO_RELOAD_HTAB -/* - * Bitmask of PVR numbers of 603-like chips, - * for which we don't use the hash table at all. - */ -#define PVR_603_LIKE 0x13000000 /* bits 3, 6, 7 set */ - - mfspr r0,PVR - rlwinm r0,r0,16,27,31 - lis r9,PVR_603_LIKE@h - rlwnm. r0,r9,r0,0,0 - beq+ 99f + cmpwi 0,r5,0 + bne+ 99f tlbia - isync + sync +#ifdef __SMP__ + tlbsync + sync +#endif blr 99: #endif /* NO_RELOAD_HTAB */ @@ -2247,14 +2380,13 @@ bne- 10b stwcx. r8,0,r9 bne- 10b + eieio #endif rlwinm r3,r3,7,1,24 /* put VSID lower limit in position */ oris r3,r3,0x8000 /* set V bit */ rlwinm r4,r4,7,1,24 /* put VSID upper limit in position */ oris r4,r4,0x8000 ori r4,r4,0x7f - lis r5,Hash@ha - lwz r5,Hash@l(r5) /* base of hash table */ lis r6,Hash_size@ha lwz r6,Hash_size@l(r6) /* size in bytes */ srwi r6,r6,3 /* # PTEs */ @@ -2270,11 +2402,11 @@ 2: bdnz 1b /* continue with loop */ sync tlbia - isync + sync #ifdef __SMP__ tlbsync + sync lis r3,hash_table_lock@ha - li r0,0 stw r0,hash_table_lock@l(r3) mtmsr r10 SYNC @@ -2287,14 +2419,17 @@ * flush_hash_page(unsigned context, unsigned long va) */ _GLOBAL(flush_hash_page) + lis r6,Hash@ha + lwz r6,Hash@l(r6) /* hash table base */ #ifdef NO_RELOAD_HTAB - mfspr r0,PVR - rlwinm r0,r0,16,27,31 - lis r9,PVR_603_LIKE@h - rlwnm. r0,r9,r0,0,0 - beq+ 99f + cmpwi 0,r6,0 /* hash table in use? */ + bne+ 99f tlbie r4 /* in hw tlb too */ - isync + sync +#ifdef __SMP__ + tlbsync + sync +#endif blr 99: #endif /* NO_RELOAD_HTAB */ @@ -2311,11 +2446,12 @@ ori r9,r9,hash_table_lock@l lwz r8,PROCESSOR(r2) oris r8,r8,9 -10: lwarx r6,0,r9 - cmpi 0,r6,0 +10: lwarx r7,0,r9 + cmpi 0,r7,0 bne- 10b stwcx. r8,0,r9 bne- 10b + eieio #endif rlwinm r3,r3,11,1,20 /* put context into vsid */ rlwimi r3,r4,11,21,24 /* put top 4 bits of va into vsid */ @@ -2328,8 +2464,6 @@ lwz r5,Hash_mask@l(r5) /* hash mask */ slwi r5,r5,6 /* << 6 */ and r7,r7,r5 - lis r6,Hash@ha - lwz r6,Hash@l(r6) /* hash table base */ add r6,r6,r7 /* address of primary PTEG */ li r8,8 mtctr r8 @@ -2350,9 +2484,10 @@ stw r0,0(r7) /* invalidate entry */ 4: sync tlbie r4 /* in hw tlb too */ - isync + sync #ifdef __SMP__ tlbsync + sync lis r3,hash_table_lock@h li r0,0 stw r0,hash_table_lock@l(r3) @@ -2418,30 +2553,27 @@ rfi /* return to caller */ #endif /* CONFIG_8xx */ -#ifdef CONFIG_MBX -/* Jump into the system reset for the MBX rom. +#ifdef CONFIG_8xx +/* Jump into the system reset for the rom. * We first disable the MMU, and then jump to the ROM reset address. * - * This does not work, don't bother trying. There is no place in - * the ROM we can jump to cause a reset. We will have to program - * a watchdog of some type that we don't service to cause a processor - * reset. - */ - .globl MBX_gorom -MBX_gorom: - li r3,MSR_KERNEL & ~(MSR_IR|MSR_DR) - lis r4,2f@h - addis r4,r4,-KERNELBASE@h - ori r4,r4,2f@l - mtspr SRR0,r4 - mtspr SRR1,r3 - rfi + * r3 is the board info structure, r4 is the location for starting. + * I use this for building a small kernel that can load other kernels, + * rather than trying to write or rely on a rom monitor that can tftp load. + */ + .globl m8xx_gorom +m8xx_gorom: + li r5,MSR_KERNEL & ~(MSR_IR|MSR_DR) + lis r6,2f@h + addis r6,r6,-KERNELBASE@h + ori r6,r6,2f@l + mtspr SRR0,r6 + mtspr SRR1,r5 + rfi 2: - lis r4, 0xfe000000@h - addi r4, r4, 0xfe000000@l - mtlr r4 - blr -#endif /* CONFIG_MBX */ + mtlr r4 + blr +#endif /* CONFIG_8xx */ /* * We put a few things here that have to be page-aligned. diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/idle.c linux/arch/ppc/kernel/idle.c --- v2.2.3/linux/arch/ppc/kernel/idle.c Thu Dec 31 10:28:59 1998 +++ linux/arch/ppc/kernel/idle.c Wed Mar 10 21:30:32 1999 @@ -1,5 +1,5 @@ /* - * $Id: idle.c,v 1.57 1998/12/28 10:28:46 paulus Exp $ + * $Id: idle.c,v 1.60 1999/02/12 07:06:26 cort Exp $ * * Idle daemon for PowerPC. Idle daemon will handle any action * that needs to be taken when the system becomes idle. @@ -39,6 +39,12 @@ unsigned long zero_paged_on = 0; unsigned long powersave_nap = 0; +unsigned long *zero_cache; /* head linked list of pre-zero'd pages */ +unsigned long zero_sz; /* # currently pre-zero'd pages */ +unsigned long zeropage_hits; /* # zero'd pages request that we've done */ +unsigned long zeropage_calls; /* # zero'd pages request that've been made */ +unsigned long zerototal; /* # pages zero'd over time */ + int idled(void *unused) { /* endless loop with no priority at all */ @@ -108,8 +114,6 @@ /* if we don't have a htab */ if ( Hash_size == 0 ) return; - lock_dcache(1); - #if 0 /* find a random place in the htab to start each time */ start = &Hash[jiffies%(Hash_size/sizeof(PTE))]; @@ -147,7 +151,6 @@ } out: if ( current->need_resched ) printk("need_resched: %lx\n", current->need_resched); - unlock_dcache(); #endif /* CONFIG_8xx */ } @@ -159,7 +162,7 @@ { unsigned long page = 0; - atomic_inc((atomic_t *)&quicklists.zeropage_calls); + atomic_inc((atomic_t *)&zero_cache_calls); if ( zero_quicklist ) { /* atomically remove this page from the list */ @@ -177,10 +180,10 @@ #endif /* __SMP__ */ /* we can update zerocount after the fact since it is not * used for anything but control of a loop which doesn't - * matter since it won't affect anything if it zero's one + * matter since it won't affect anything if it zeros one * less page -- Cort */ - atomic_inc((atomic_t *)&quicklists.zeropage_hits); + atomic_inc((atomic_t *)&zero_cache_hits); atomic_dec((atomic_t *)&zero_cache_sz); /* zero out the pointer to next in the page */ @@ -222,7 +225,6 @@ /* * Make the page no cache so we don't blow our cache with 0's - * We should just turn off the cache instead. -- Cort */ pte = find_pte(init_task.mm, pageptr); if ( !pte ) @@ -254,8 +256,8 @@ * So we update the list atomically without locking it. * -- Cort */ + /* turn cache on for this page */ - pte_cache(*pte); flush_tlb_page(find_vma(init_task.mm,pageptr),pageptr); /* atomically add this page to the list */ @@ -280,7 +282,7 @@ * reads it. -- Cort */ atomic_inc((atomic_t *)&zero_cache_sz); - atomic_inc((atomic_t *)&quicklists.zerototal); + atomic_inc((atomic_t *)&zero_cache_total); } } @@ -307,5 +309,4 @@ default: return; } - } diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/irq.c linux/arch/ppc/kernel/irq.c --- v2.2.3/linux/arch/ppc/kernel/irq.c Thu Dec 31 10:28:59 1998 +++ linux/arch/ppc/kernel/irq.c Wed Mar 10 21:30:32 1999 @@ -1,5 +1,5 @@ /* - * $Id: irq.c,v 1.91 1998/12/28 10:28:47 paulus Exp $ + * $Id: irq.c,v 1.102 1999/02/03 01:36:59 paulus Exp $ * * arch/ppc/kernel/irq.c * @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -56,58 +57,71 @@ #include #include #include +#include #ifdef CONFIG_8xx #include #include #endif +static void no_action(int cpl, void *dev_id, struct pt_regs *regs) { } +extern volatile unsigned long ipi_count; +static void dispatch_handler(struct pt_regs *regs, int irq); +void enable_irq(unsigned int irq_nr); +void disable_irq(unsigned int irq_nr); + +static void i8259_mask_and_ack_irq(unsigned int irq_nr); +static void i8259_mask_irq(unsigned int irq_nr); +static void i8259_unmask_irq(unsigned int irq_nr); +#ifdef CONFIG_8xx +static void mbx_mask_and_ack(unsigned int irq_nr); +static void mbx_mask_irq(unsigned int irq_nr); +static void mbx_unmask_irq(unsigned int irq_nr); +static void mbx_i8259_action(int cpl, void *dev_id, struct pt_regs *regs); +#else /* CONFIG_8xx */ +static volatile unsigned char *chrp_int_ack_special; extern void process_int(unsigned long vec, struct pt_regs *fp); extern void apus_init_IRQ(void); extern void amiga_disable_irq(unsigned int irq); extern void amiga_enable_irq(unsigned int irq); -static void no_action(int cpl, void *dev_id, struct pt_regs *regs) { } -static volatile unsigned char *chrp_int_ack_special; -extern volatile unsigned long ipi_count; static void pmac_fix_gatwick_interrupts(struct device_node *gw, int irq_base); +static void gatwick_action(int cpl, void *dev_id, struct pt_regs *regs); +static void pmac_mask_irq(unsigned int irq_nr); +static void pmac_unmask_irq(unsigned int irq_nr); +static void pmac_mask_and_ack_irq(unsigned int irq_nr); +static void chrp_mask_and_ack_irq(unsigned int irq_nr); +static void chrp_unmask_irq(unsigned int irq_nr); +static void chrp_mask_irq(unsigned int irq_nr); +#ifdef __SMP__ +static void openpic_ipi_action(int cpl, void *dev_id, struct pt_regs *regs); +extern void smp_message_recv(void); +#endif /* __SMP__ */ +#endif /* CONFIG_8xx */ #ifdef CONFIG_APUS /* Rename a few functions. Requires the CONFIG_APUS protection. */ #define request_irq nop_ppc_request_irq #define free_irq nop_ppc_free_irq #define get_irq_list nop_get_irq_list +#define VEC_SPUR (24) #endif -#ifndef CONFIG_8xx -void (*mask_and_ack_irq)(int irq_nr); -void (*mask_irq)(unsigned int irq_nr); -void (*unmask_irq)(unsigned int irq_nr); -#else /* CONFIG_8xx */ -/* init_IRQ() happens too late for the MBX because we initialize the - * CPM early and it calls request_irq() before we have these function - * pointers initialized. - */ -#define mask_and_ack_irq(irq) mbx_mask_irq(irq) -#define mask_irq(irq) mbx_mask_irq(irq) -#define unmask_irq(irq) mbx_unmask_irq(irq) -#endif /* CONFIG_8xx */ -#define VEC_SPUR (24) -#undef SHOW_IRQ -#undef SHOW_GATWICK_IRQS #define NR_MASK_WORDS ((NR_IRQS + 31) / 32) -#define cached_21 (((char *)(cached_irq_mask))[3]) -#define cached_A1 (((char *)(cached_irq_mask))[2]) -#define PREP_IRQ_MASK (((unsigned int)cached_A1)<<8) | (unsigned int)cached_21 +unsigned char cached_8259[2] = { 0xff, 0xff }; +#define cached_A1 (cached_8259[0]) +#define cached_21 (cached_8259[1]) unsigned int local_bh_count[NR_CPUS]; unsigned int local_irq_count[NR_CPUS]; int max_irqs; int max_real_irqs; -static struct irqaction *irq_action[NR_IRQS]; static int spurious_interrupts = 0; static unsigned int cached_irq_mask[NR_MASK_WORDS]; unsigned int lost_interrupts[NR_MASK_WORDS]; atomic_t n_lost_interrupts; +#ifndef CONFIG_8xx +#define GATWICK_IRQ_POOL_SIZE 10 +static struct interrupt_info gatwick_int_pool[GATWICK_IRQ_POOL_SIZE]; /* pmac */ struct pmac_irq_hw { unsigned int flag; @@ -116,41 +130,22 @@ unsigned int level; }; -/* XXX these addresses should be obtained from the device tree */ -volatile struct pmac_irq_hw *pmac_irq_hw[4] = { +/* these addresses are obtained from the device tree now -- Cort */ +volatile struct pmac_irq_hw *pmac_irq_hw[4] __pmac = { (struct pmac_irq_hw *) 0xf3000020, (struct pmac_irq_hw *) 0xf3000010, (struct pmac_irq_hw *) 0xf4000020, (struct pmac_irq_hw *) 0xf4000010, }; - -/* This is the interrupt used on the main controller for the secondary - controller. Happens on PowerBooks G3 Series (a second mac-io) - -- BenH - */ -static int second_irq = -999; - -/* Returns the number of 0's to the left of the most significant 1 bit */ -static inline int cntlzw(int bits) -{ - int lz; - - asm ("cntlzw %0,%1" : "=r" (lz) : "r" (bits)); - return lz; -} - -static inline void sync(void) -{ - asm volatile ("sync"); -} +#endif /* CONFIG_8xx */ /* nasty hack for shared irq's since we need to do kmalloc calls but - * can't very very early in the boot when we need to do a request irq. + * can't very early in the boot when we need to do a request irq. * this needs to be removed. * -- Cort */ static char cache_bitmask = 0; -static struct irqaction malloc_cache[4]; +static struct irqaction malloc_cache[8]; extern int mem_init_done; void *irq_kmalloc(size_t size, int pri) @@ -179,168 +174,155 @@ kfree(ptr); } -#ifndef CONFIG_8xx -void i8259_mask_and_ack_irq(int irq_nr) -{ - /* spin_lock(&irq_controller_lock);*/ - cached_irq_mask[0] |= 1 << irq_nr; - if (irq_nr > 7) { - inb(0xA1); /* DUMMY */ - outb(cached_A1,0xA1); - outb(0x62,0x20); /* Specific EOI to cascade */ - /*outb(0x20,0xA0);*/ - outb(0x60|(irq_nr-8), 0xA0); /* specific eoi */ - } else { - inb(0x21); /* DUMMY */ - outb(cached_21,0x21); - /*outb(0x20,0x20);*/ - outb(0x60|irq_nr,0x20); /* specific eoi */ - - } - /* spin_unlock(&irq_controller_lock);*/ -} - -void __pmac pmac_mask_and_ack_irq(int irq_nr) -{ - unsigned long bit = 1UL << (irq_nr & 0x1f); - int i = irq_nr >> 5; - - if ((unsigned)irq_nr >= max_irqs) - return; - /*spin_lock(&irq_controller_lock);*/ +struct hw_interrupt_type { + const char * typename; + void (*startup)(unsigned int irq); + void (*shutdown)(unsigned int irq); + void (*handle)(unsigned int irq, struct pt_regs * regs); + void (*enable)(unsigned int irq); + void (*disable)(unsigned int irq); + void (*mask_and_ack)(unsigned int irq); + int irq_offset; +}; - clear_bit(irq_nr, cached_irq_mask); - if (test_and_clear_bit(irq_nr, lost_interrupts)) - atomic_dec(&n_lost_interrupts); - out_le32(&pmac_irq_hw[i]->ack, bit); - out_le32(&pmac_irq_hw[i]->enable, cached_irq_mask[i]); - out_le32(&pmac_irq_hw[i]->ack, bit); - /* make sure ack gets to controller before we enable interrupts */ - sync(); +#define mask_irq(irq) ({if (irq_desc[irq].ctl && irq_desc[irq].ctl->disable) irq_desc[irq].ctl->disable(irq);}) +#define unmask_irq(irq) ({if (irq_desc[irq].ctl && irq_desc[irq].ctl->enable) irq_desc[irq].ctl->enable(irq);}) +#define mask_and_ack_irq(irq) ({if (irq_desc[irq].ctl && irq_desc[irq].ctl->mask_and_ack) irq_desc[irq].ctl->mask_and_ack(irq);}) - /*spin_unlock(&irq_controller_lock);*/ - /*if ( irq_controller_lock.lock ) - panic("irq controller lock still held in mask and ack\n");*/ -} +struct irqdesc { + struct irqaction *action; + struct hw_interrupt_type *ctl; +}; +static struct irqdesc irq_desc[NR_IRQS] = {{0, 0}, }; -void __openfirmware chrp_mask_and_ack_irq(int irq_nr) -{ - /* spinlocks are done by i8259_mask_and_ack() - Cort */ - if (is_8259_irq(irq_nr)) - i8259_mask_and_ack_irq(irq_nr); -} +static struct hw_interrupt_type i8259_pic = { + " i8259 ", + NULL, + NULL, + NULL, + i8259_unmask_irq, + i8259_mask_irq, + i8259_mask_and_ack_irq, + 0 +}; +#ifndef CONFIG_8xx +static struct hw_interrupt_type pmac_pic = { + " PMAC-PIC ", + NULL, + NULL, + NULL, + pmac_unmask_irq, + pmac_mask_irq, + pmac_mask_and_ack_irq, + 0 +}; +static struct hw_interrupt_type gatwick_pic = { + " GATWICK ", + NULL, + NULL, + NULL, + pmac_unmask_irq, + pmac_mask_irq, + pmac_mask_and_ack_irq, + 0 +}; -static void i8259_set_irq_mask(int irq_nr) -{ - if (irq_nr > 7) { - outb(cached_A1,0xA1); - } else { - outb(cached_21,0x21); - } -} +static struct hw_interrupt_type open_pic = { + " OpenPIC ", + NULL, + NULL, + NULL, + chrp_unmask_irq, + chrp_mask_irq, + chrp_mask_and_ack_irq, + 0 +}; +#else +static struct hw_interrupt_type ppc8xx_pic = { + " 8xx SIU ", + NULL, + NULL, + NULL, + mbx_unmask_irq, + mbx_mask_irq, + mbx_mask_and_ack, + 0 +}; +#endif /* CONFIG_8xx */ -static void __pmac pmac_set_irq_mask(int irq_nr) +int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, const char * devname, void *dev_id) { - unsigned long bit = 1UL << (irq_nr & 0x1f); - int i = irq_nr >> 5; - - if ((unsigned)irq_nr >= max_irqs) - return; - - /* enable unmasked interrupts */ - out_le32(&pmac_irq_hw[i]->enable, cached_irq_mask[i]); + struct irqaction *old, **p, *action; + unsigned long flags; - /* - * Unfortunately, setting the bit in the enable register - * when the device interrupt is already on *doesn't* set - * the bit in the flag register or request another interrupt. - */ - if ((bit & cached_irq_mask[i]) - && (ld_le32(&pmac_irq_hw[i]->level) & bit) - && !(ld_le32(&pmac_irq_hw[i]->flag) & bit)) { - if (!test_and_set_bit(irq_nr, lost_interrupts)) - atomic_inc(&n_lost_interrupts); + if (irq >= NR_IRQS) + return -EINVAL; + if (!handler) + { + /* Free */ + for (p = &irq_desc[irq].action; (action = *p) != NULL; p = &action->next) + { + /* Found it - now free it */ + save_flags(flags); + cli(); + *p = action->next; + restore_flags(flags); + irq_kfree(action); + return 0; + } + return -ENOENT; } -} - -/* - * These have to be protected by the spinlock - * before being called. - */ -static void i8259_mask_irq(unsigned int irq_nr) -{ - cached_irq_mask[0] |= 1 << irq_nr; - i8259_set_irq_mask(irq_nr); -} - -static void i8259_unmask_irq(unsigned int irq_nr) -{ - cached_irq_mask[0] &= ~(1 << irq_nr); - i8259_set_irq_mask(irq_nr); -} - -static void __pmac pmac_mask_irq(unsigned int irq_nr) -{ - clear_bit(irq_nr, cached_irq_mask); - pmac_set_irq_mask(irq_nr); - sync(); -} - -static void __pmac pmac_unmask_irq(unsigned int irq_nr) -{ - set_bit(irq_nr, cached_irq_mask); - pmac_set_irq_mask(irq_nr); -} - -static void __openfirmware chrp_mask_irq(unsigned int irq_nr) -{ - if (is_8259_irq(irq_nr)) - i8259_mask_irq(irq_nr); - else - openpic_disable_irq(irq_to_openpic(irq_nr)); -} + + action = (struct irqaction *) + irq_kmalloc(sizeof(struct irqaction), GFP_KERNEL); + if (!action) + return -ENOMEM; + + save_flags(flags); + cli(); + + action->handler = handler; + action->flags = irqflags; + action->mask = 0; + action->name = devname; + action->dev_id = dev_id; + action->next = NULL; + enable_irq(irq); + + p = &irq_desc[irq].action; + + if ((old = *p) != NULL) { + /* Can't share interrupts unless both agree to */ + if (!(old->flags & action->flags & SA_SHIRQ)) + return -EBUSY; + /* add new interrupt at end of irq queue */ + do { + p = &old->next; + old = *p; + } while (old); + } + *p = action; -static void __openfirmware chrp_unmask_irq(unsigned int irq_nr) -{ - if (is_8259_irq(irq_nr)) - i8259_unmask_irq(irq_nr); - else - openpic_enable_irq(irq_to_openpic(irq_nr)); -} -#else /* CONFIG_8xx */ -static void mbx_mask_irq(unsigned int irq_nr) -{ - cached_irq_mask[0] &= ~(1 << (31-irq_nr)); - ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask = - cached_irq_mask[0]; + restore_flags(flags); + return 0; } -static void mbx_unmask_irq(unsigned int irq_nr) +void free_irq(unsigned int irq, void *dev_id) { - cached_irq_mask[0] |= (1 << (31-irq_nr)); - ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask = - cached_irq_mask[0]; + request_irq(irq, NULL, 0, NULL, dev_id); } -#endif /* CONFIG_8xx */ void disable_irq(unsigned int irq_nr) { - /*unsigned long flags;*/ - - /* spin_lock_irqsave(&irq_controller_lock, flags);*/ mask_irq(irq_nr); - /* spin_unlock_irqrestore(&irq_controller_lock, flags);*/ synchronize_irq(); } void enable_irq(unsigned int irq_nr) { - /*unsigned long flags;*/ - - /* spin_lock_irqsave(&irq_controller_lock, flags);*/ unmask_irq(irq_nr); - /* spin_unlock_irqrestore(&irq_controller_lock, flags);*/ } int get_irq_list(char *buf) @@ -354,8 +336,8 @@ *(char *)(buf+len++) = '\n'; for (i = 0 ; i < NR_IRQS ; i++) { - action = irq_action[i]; - if ((!action || !action->handler) && (i != second_irq)) + action = irq_desc[i].action; + if ( !action || !action->handler ) continue; len += sprintf(buf+len, "%3d: ", i); #ifdef __SMP__ @@ -365,36 +347,13 @@ #else len += sprintf(buf+len, "%10u ", kstat_irqs(i)); #endif /* __SMP__ */ - switch( _machine ) - { - case _MACH_prep: - len += sprintf(buf+len, " 82c59 "); - break; - case _MACH_Pmac: - if (i < 64) - len += sprintf(buf+len, " PMAC-PIC "); - else - len += sprintf(buf+len, " GATWICK "); - break; - case _MACH_chrp: - if ( is_8259_irq(i) ) - len += sprintf(buf+len, " 82c59 "); - else - len += sprintf(buf+len, " OpenPIC "); - break; - case _MACH_mbx: - len += sprintf(buf+len, " MPC8xx "); - break; + if ( irq_desc[i].ctl ) + len += sprintf(buf+len, " %s ", irq_desc[i].ctl->typename ); + len += sprintf(buf+len, " %s",action->name); + for (action=action->next; action; action = action->next) { + len += sprintf(buf+len, ", %s", action->name); } - - if (i != second_irq) { - len += sprintf(buf+len, " %s",action->name); - for (action=action->next; action; action = action->next) { - len += sprintf(buf+len, ", %s", action->name); - } - len += sprintf(buf+len, "\n"); - } else - len += sprintf(buf+len, " Gatwick secondary IRQ controller\n"); + len += sprintf(buf+len, "\n"); } #ifdef __SMP__ /* should this be per processor send/receive? */ @@ -405,269 +364,53 @@ #endif len += sprintf(buf+len, "BAD: %10u",spurious_interrupts); for ( i = 0 ; i <= smp_num_cpus-1; i++ ) - len += sprintf(buf+len," "); - len += sprintf(buf+len, " spurious or short\n"); + len += sprintf(buf+len," "); + len += sprintf(buf+len, " spurious or short\n"); return len; } - -/* - * Global interrupt locks for SMP. Allow interrupts to come in on any - * CPU, yet make cli/sti act globally to protect critical regions.. - */ -#ifdef __SMP__ -unsigned char global_irq_holder = NO_PROC_ID; -unsigned volatile int global_irq_lock; -atomic_t global_irq_count; - -atomic_t global_bh_count; -atomic_t global_bh_lock; - -static void show(char * str) +static void dispatch_handler(struct pt_regs *regs, int irq) { - int i; - unsigned long *stack; + int status; + struct irqaction *action; int cpu = smp_processor_id(); - - printk("\n%s, CPU %d:\n", str, cpu); - printk("irq: %d [%d %d]\n", - atomic_read(&global_irq_count), local_irq_count[0], local_irq_count[1]); - printk("bh: %d [%d %d]\n", - atomic_read(&global_bh_count), local_bh_count[0], local_bh_count[1]); - stack = (unsigned long *) &str; - for (i = 40; i ; i--) { - unsigned long x = *++stack; - if (x > (unsigned long) &init_task_union && x < (unsigned long) &vsprintf) { - printk("<[%08lx]> ", x); - } + + mask_and_ack_irq(irq); + status = 0; + action = irq_desc[irq].action; + kstat.irqs[cpu][irq]++; + if (action && action->handler) { + if (!(action->flags & SA_INTERRUPT)) + __sti(); + do { + status |= action->flags; + action->handler(irq, action->dev_id, regs); + action = action->next; + } while ( action ); + __cli(); + unmask_irq(irq); + } else { + spurious_interrupts++; + disable_irq( irq ); } } #define MAXCOUNT 100000000 -static inline void wait_on_bh(void) -{ - int count = MAXCOUNT; - do { - if (!--count) { - show("wait_on_bh"); - count = ~0; - } - /* nothing .. wait for the other bh's to go away */ - } while (atomic_read(&global_bh_count) != 0); -} - - -static inline void wait_on_irq(int cpu) -{ - int count = MAXCOUNT; - - for (;;) { - - /* - * Wait until all interrupts are gone. Wait - * for bottom half handlers unless we're - * already executing in one.. - */ - if (!atomic_read(&global_irq_count)) { - if (local_bh_count[cpu] || !atomic_read(&global_bh_count)) - break; - } - - /* Duh, we have to loop. Release the lock to avoid deadlocks */ - clear_bit(0,&global_irq_lock); - - for (;;) { - if (!--count) { - show("wait_on_irq"); - count = ~0; - } - __sti(); - /* don't worry about the lock race Linus found - * on intel here. -- Cort - */ - __cli(); - if (atomic_read(&global_irq_count)) - continue; - if (global_irq_lock) - continue; - if (!local_bh_count[cpu] && atomic_read(&global_bh_count)) - continue; - if (!test_and_set_bit(0,&global_irq_lock)) - break; - } - } -} - -/* - * This is called when we want to synchronize with - * bottom half handlers. We need to wait until - * no other CPU is executing any bottom half handler. - * - * Don't wait if we're already running in an interrupt - * context or are inside a bh handler. - */ -void synchronize_bh(void) -{ - if (atomic_read(&global_bh_count) && !in_interrupt()) - wait_on_bh(); -} - - -/* - * This is called when we want to synchronize with - * interrupts. We may for example tell a device to - * stop sending interrupts: but to make sure there - * are no interrupts that are executing on another - * CPU we need to call this function. - */ -void synchronize_irq(void) -{ - if (atomic_read(&global_irq_count)) { - /* Stupid approach */ - cli(); - sti(); - } -} - -static inline void get_irqlock(int cpu) -{ - unsigned int loops = MAXCOUNT; - - if (test_and_set_bit(0,&global_irq_lock)) { - /* do we already hold the lock? */ - if ((unsigned char) cpu == global_irq_holder) - return; - /* Uhhuh.. Somebody else got it. Wait.. */ - do { - do { - if (loops-- == 0) { - printk("get_irqlock(%d) waiting, global_irq_holder=%d\n", cpu, global_irq_holder); -#ifdef CONFIG_XMON - xmon(0); -#endif - } - } while (test_bit(0,&global_irq_lock)); - } while (test_and_set_bit(0,&global_irq_lock)); - } - /* - * We also need to make sure that nobody else is running - * in an interrupt context. - */ - wait_on_irq(cpu); - - /* - * Ok, finally.. - */ - global_irq_holder = cpu; -} - -/* - * A global "cli()" while in an interrupt context - * turns into just a local cli(). Interrupts - * should use spinlocks for the (very unlikely) - * case that they ever want to protect against - * each other. - * - * If we already have local interrupts disabled, - * this will not turn a local disable into a - * global one (problems with spinlocks: this makes - * save_flags+cli+sti usable inside a spinlock). - */ -void __global_cli(void) -{ - unsigned int flags; - - __save_flags(flags); - if (flags & (1 << 15)) { - int cpu = smp_processor_id(); - __cli(); - if (!local_irq_count[cpu]) - get_irqlock(cpu); - } -} - -void __global_sti(void) -{ - int cpu = smp_processor_id(); - - if (!local_irq_count[cpu]) - release_irqlock(cpu); - __sti(); -} - -/* - * SMP flags value to restore to: - * 0 - global cli - * 1 - global sti - * 2 - local cli - * 3 - local sti - */ -unsigned long __global_save_flags(void) -{ - int retval; - int local_enabled; - unsigned long flags; - - __save_flags(flags); - local_enabled = (flags >> 15) & 1; - /* default to local */ - retval = 2 + local_enabled; - - /* check for global flags if we're not in an interrupt */ - if (!local_irq_count[smp_processor_id()]) { - if (local_enabled) - retval = 1; - if (global_irq_holder == (unsigned char) smp_processor_id()) - retval = 0; - } - return retval; -} - -void __global_restore_flags(unsigned long flags) -{ - switch (flags) { - case 0: - __global_cli(); - break; - case 1: - __global_sti(); - break; - case 2: - __cli(); - break; - case 3: - __sti(); - break; - default: - printk("global_restore_flags: %08lx (%08lx)\n", - flags, (&flags)[-1]); - } -} - -#endif /* __SMP__ */ - -asmlinkage void do_IRQ(struct pt_regs *regs, int isfake) +asmlinkage void do_IRQ(struct pt_regs *regs, int isfake) { int irq; - unsigned long bits; - struct irqaction *action; + unsigned long bits = 0; int cpu = smp_processor_id(); - int status; int openpic_eoi_done = 0; - /* save the HID0 in case dcache was off - see idle.c - * this hack should leave for a better solution -- Cort */ - unsigned dcache_locked; - - dcache_locked = unlock_dcache(); hardirq_enter(cpu); #ifndef CONFIG_8xx #ifdef __SMP__ - if ( cpu != 0 ) + /* IPI's are a hack on the powersurge -- Cort */ + if ( (_machine == _MACH_Pmac) && (cpu != 0) ) { if (!isfake) { - extern void smp_message_recv(void); #ifdef CONFIG_XMON static int xmon_2nd; if (xmon_2nd) @@ -713,36 +456,6 @@ irq -= cntlzw(bits); break; } - - /* Here, we handle interrupts coming from Gatwick, - * normal interrupt code will take care of acking and - * masking the irq on Gatwick itself but we ack&mask - * the Gatwick main interrupt on Heathrow now. It's - * unmasked later, after interrupt handling. -- BenH - */ - if (irq == second_irq) { - mask_and_ack_irq(second_irq); - for (irq = max_irqs - 1; irq > max_real_irqs; irq -= 32) { - int i = irq >> 5; - bits = ld_le32(&pmac_irq_hw[i]->flag) - | lost_interrupts[i]; - if (bits == 0) - continue; - irq -= cntlzw(bits); - break; - } - /* If not found, on exit, irq is 63 (128-1-32-32). - * We set it to -1 and revalidate second controller - */ - if (irq < max_real_irqs) { - irq = -1; - unmask_irq(second_irq); - } -#ifdef SHOW_GATWICK_IRQS - printk("Gatwick irq %d (i:%d, bits:0x%08lx\n", irq, i, bits); -#endif - } - break; case _MACH_chrp: irq = openpic_irq(0); @@ -761,42 +474,50 @@ openpic_eoi(0); openpic_eoi_done = 1; } - else if (irq >= OPENPIC_VEC_TIMER) + if (irq == OPENPIC_VEC_SPURIOUS) { - /* - * OpenPIC interrupts >64 will be used for other purposes - * like interprocessor interrupts and hardware errors - */ - if (irq == OPENPIC_VEC_SPURIOUS) { /* * Spurious interrupts should never be * acknowledged */ - spurious_interrupts++; - openpic_eoi_done = 1; - } else { - /* - * Here we should process IPI timer - * for now the interrupt is dismissed. - */ - } - goto out; + spurious_interrupts++; + openpic_eoi_done = 1; } + bits = 1UL << irq; break; case _MACH_prep: outb(0x0C, 0x20); irq = inb(0x20) & 7; if (irq == 2) { -retry_cascade: outb(0x0C, 0xA0); - irq = inb(0xA0); - /* if no intr left */ - if ( !(irq & 128 ) ) - goto out; - irq = (irq&7) + 8; + irq = (inb(0xA0) & 7) + 8; + bits |= 1UL << irq; +#if 0 + /* It's possible to loose intrs here + * if we get 2 intrs in the upper 8 + * bits. We eoi irq 2 and handle one of + * the upper intrs but then ignore it + * since we've already eoi-d 2. So, + * we must keep track of lost intrs. + * -- Cort + */ + while (1) + { + int i; + outb(0x0C, 0xA0); + i = inb(0xA0); + if ( !(i & 128) ) + break; + irq &= 7; + irq += 8; + bits |= 1UL << irq; + } +#endif } - bits = 1UL << irq; + else + bits = 1UL << irq; + break; #ifdef CONFIG_APUS case _MACH_apus: @@ -822,7 +543,6 @@ APUS_WRITE(APUS_IPL_EMU, IPLEMU_IPLMASK); APUS_WRITE(APUS_IPL_EMU, (IPLEMU_SETRESET | (~(old_level) & IPLEMU_IPLMASK))); - apus_out: hardirq_exit(cpu); APUS_WRITE(APUS_IPL_EMU, IPLEMU_DISABLEINT); @@ -831,14 +551,11 @@ #endif } - if (irq < 0) { - /* we get here with Gatwick but the 'bogus' isn't correct in that case -- Cort */ - if ( irq != second_irq ) - { - printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n", - irq, regs->nip); - spurious_interrupts++; - } + if (irq < 0) + { + printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n", + irq, regs->nip); + spurious_interrupts++; goto out; } @@ -848,136 +565,349 @@ */ bits = ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sivec; irq = bits >> 26; + irq += ppc8xx_pic.irq_offset; + bits = 1UL << irq; #endif /* CONFIG_8xx */ - mask_and_ack_irq(irq); - status = 0; - action = irq_action[irq]; - kstat.irqs[cpu][irq]++; - if (action && action->handler) { - if (!(action->flags & SA_INTERRUPT)) - __sti(); - do { - status |= action->flags; - action->handler(irq, action->dev_id, regs); - action = action->next; - } while ( action ); - __cli(); - unmask_irq(irq); - } else { -#ifndef CONFIG_8xx - if ( irq == 7 ) /* i8259 gives us irq 7 on 'short' intrs */ -#endif - spurious_interrupts++; - disable_irq( irq ); - } +#if 0 + /* + * this allows for > 1 interrupt at a time so we can + * clear out any 'double' interrupts on prep and + * finish up the lost interrupts. + * It doesn't currently work for irqs > 31 so I'm leaving + * it commented out for now. + * -- Cort + */ + for ( i = 0 ; i < sizeof(bits)*8 ; i++ ) + if ( bits & (1UL<= max_real_irqs) - unmask_irq(second_irq); - - /* make sure we don't miss any cascade intrs due to eoi-ing irq 2 */ -#ifndef CONFIG_8xx - if ( is_prep && (irq > 7) ) - goto retry_cascade; - /* do_bottom_half is called if necessary from int_return in head.S */ +#ifndef CONFIG_8xx out: if (_machine == _MACH_chrp && !openpic_eoi_done) openpic_eoi(0); #endif /* CONFIG_8xx */ hardirq_exit(cpu); - #ifdef CONFIG_APUS out2: #endif - /* restore the HID0 in case dcache was off - see idle.c - * this hack should leave for a better solution -- Cort */ - lock_dcache(dcache_locked); } -int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), - unsigned long irqflags, const char * devname, void *dev_id) +unsigned long probe_irq_on (void) { - struct irqaction *old, **p, *action; - unsigned long flags; - -#ifdef SHOW_IRQ - printk("request_irq(): irq %d handler %08x name %s dev_id %04x\n", - irq,(int)handler,devname,(int)dev_id); -#endif /* SHOW_IRQ */ + return 0; +} - if (irq >= NR_IRQS) - return -EINVAL; - - /* Cannot allocate second controller IRQ */ - if (irq == second_irq) - return -EBUSY; +int probe_irq_off (unsigned long irqs) +{ + return 0; +} - if (!handler) - { - /* Free */ - for (p = irq + irq_action; (action = *p) != NULL; p = &action->next) - { - /* Found it - now free it */ - save_flags(flags); - cli(); - *p = action->next; - restore_flags(flags); - irq_kfree(action); - return 0; - } - return -ENOENT; +static void i8259_mask_and_ack_irq(unsigned int irq_nr) +{ + if ( irq_nr >= i8259_pic.irq_offset ) + irq_nr -= i8259_pic.irq_offset; + if (irq_nr > 7) { + cached_A1 |= 1 << (irq_nr-8); + inb(0xA1); /* DUMMY */ + outb(cached_A1,0xA1); + outb(0x62,0x20); /* Specific EOI to cascade */ + /*outb(0x20,0xA0);*/ + outb(0x60|(irq_nr-8), 0xA0); /* specific eoi */ + } else { + cached_21 |= 1 << irq_nr; + inb(0x21); /* DUMMY */ + outb(cached_21,0x21); + /*outb(0x20,0x20);*/ + outb(0x60|irq_nr,0x20); /* specific eoi */ } +} + +static void i8259_set_irq_mask(int irq_nr) +{ + outb(cached_A1,0xA1); + outb(cached_21,0x21); +} + +static void i8259_mask_irq(unsigned int irq_nr) +{ + if ( irq_nr >= i8259_pic.irq_offset ) + irq_nr -= i8259_pic.irq_offset; + if ( irq_nr < 8 ) + cached_21 |= 1 << irq_nr; + else + cached_A1 |= 1 << (irq_nr-8); + i8259_set_irq_mask(irq_nr); +} + +static void i8259_unmask_irq(unsigned int irq_nr) +{ + + if ( irq_nr >= i8259_pic.irq_offset ) + irq_nr -= i8259_pic.irq_offset; + if ( irq_nr < 8 ) + cached_21 &= ~(1 << irq_nr); + else + cached_A1 &= ~(1 << (irq_nr-8)); + i8259_set_irq_mask(irq_nr); +} + +#ifndef CONFIG_8xx +static void gatwick_action(int cpl, void *dev_id, struct pt_regs *regs) +{ + int irq, bits; - action = (struct irqaction *) - irq_kmalloc(sizeof(struct irqaction), GFP_KERNEL); - if (!action) - return -ENOMEM; - save_flags(flags); - cli(); - - action->handler = handler; - action->flags = irqflags; - action->mask = 0; - action->name = devname; - action->dev_id = dev_id; - action->next = NULL; - enable_irq(irq); - p = irq_action + irq; + for (irq = max_irqs - 1; irq > max_real_irqs; irq -= 32) { + int i = irq >> 5; + bits = ld_le32(&pmac_irq_hw[i]->flag) + | lost_interrupts[i]; + if (bits == 0) + continue; + irq -= cntlzw(bits); + break; + } + /* The previous version of this code allowed for this case, we + * don't. Put this here to check for it. + * -- Cort + */ + if ( irq_desc[irq].ctl != &gatwick_pic ) + printk("gatwick irq not from gatwick pic\n"); + else + dispatch_handler( regs, irq ); +} + +void pmac_mask_and_ack_irq(unsigned int irq_nr) +{ + unsigned long bit = 1UL << (irq_nr & 0x1f); + int i = irq_nr >> 5; + + if ((unsigned)irq_nr >= max_irqs) + return; + + clear_bit(irq_nr, cached_irq_mask); + if (test_and_clear_bit(irq_nr, lost_interrupts)) + atomic_dec(&n_lost_interrupts); + out_le32(&pmac_irq_hw[i]->ack, bit); + out_le32(&pmac_irq_hw[i]->enable, cached_irq_mask[i]); + out_le32(&pmac_irq_hw[i]->ack, bit); + do { + /* make sure ack gets to controller before we enable interrupts */ + mb(); + } while(in_le32(&pmac_irq_hw[i]->flag) & bit); + +} + +void __openfirmware chrp_mask_and_ack_irq(unsigned int irq_nr) +{ + if (is_8259_irq(irq_nr)) + i8259_mask_and_ack_irq(irq_nr); +} + +static void pmac_set_irq_mask(int irq_nr) +{ + unsigned long bit = 1UL << (irq_nr & 0x1f); + int i = irq_nr >> 5; + + if ((unsigned)irq_nr >= max_irqs) + return; + + /* enable unmasked interrupts */ + out_le32(&pmac_irq_hw[i]->enable, cached_irq_mask[i]); - if ((old = *p) != NULL) { - /* Can't share interrupts unless both agree to */ - if (!(old->flags & action->flags & SA_SHIRQ)) - return -EBUSY; - /* add new interrupt at end of irq queue */ - do { - p = &old->next; - old = *p; - } while (old); + do { + /* make sure mask gets to controller before we + return to user */ + mb(); + } while((in_le32(&pmac_irq_hw[i]->enable) & bit) + != (cached_irq_mask[i] & bit)); + + /* + * Unfortunately, setting the bit in the enable register + * when the device interrupt is already on *doesn't* set + * the bit in the flag register or request another interrupt. + */ + if ((bit & cached_irq_mask[i]) + && (ld_le32(&pmac_irq_hw[i]->level) & bit) + && !(ld_le32(&pmac_irq_hw[i]->flag) & bit)) { + if (!test_and_set_bit(irq_nr, lost_interrupts)) + atomic_inc(&n_lost_interrupts); } - *p = action; +} + +static void pmac_mask_irq(unsigned int irq_nr) +{ + clear_bit(irq_nr, cached_irq_mask); + pmac_set_irq_mask(irq_nr); + mb(); +} + +static void pmac_unmask_irq(unsigned int irq_nr) +{ + set_bit(irq_nr, cached_irq_mask); + pmac_set_irq_mask(irq_nr); +} + +static void __openfirmware chrp_mask_irq(unsigned int irq_nr) +{ + if (is_8259_irq(irq_nr)) + i8259_mask_irq(irq_nr); + else + openpic_disable_irq(irq_to_openpic(irq_nr)); +} + +static void __openfirmware chrp_unmask_irq(unsigned int irq_nr) +{ + if (is_8259_irq(irq_nr)) + i8259_unmask_irq(irq_nr); + else + openpic_enable_irq(irq_to_openpic(irq_nr)); +} + +/* This routine will fix some missing interrupt values in the device tree + * on the gatwick mac-io controller used by some PowerBooks + */ +static void __init pmac_fix_gatwick_interrupts(struct device_node *gw, int irq_base) +{ + struct device_node *node; + int count; - restore_flags(flags); - return 0; + memset(gatwick_int_pool, 0, sizeof(gatwick_int_pool)); + node = gw->child; + count = 0; + while(node) + { + /* Fix SCC */ + if (strcasecmp(node->name, "escc") == 0) + if (node->child) { + if (node->child->n_intrs < 3) { + node->child->intrs = &gatwick_int_pool[count]; + count += 3; + } + node->child->n_intrs = 3; + node->child->intrs[0].line = 15+irq_base; + node->child->intrs[1].line = 4+irq_base; + node->child->intrs[2].line = 5+irq_base; + printk(KERN_INFO "irq: fixed SCC on second controller (%d,%d,%d)\n", + node->child->intrs[0].line, + node->child->intrs[1].line, + node->child->intrs[2].line); + } + /* Fix media-bay & left SWIM */ + if (strcasecmp(node->name, "media-bay") == 0) { + struct device_node* ya_node; + + if (node->n_intrs == 0) + node->intrs = &gatwick_int_pool[count++]; + node->n_intrs = 1; + node->intrs[0].line = 29+irq_base; + printk(KERN_INFO "irq: fixed media-bay on second controller (%d)\n", + node->intrs[0].line); + + ya_node = node->child; + while(ya_node) + { + if (strcasecmp(ya_node->name, "floppy") == 0) { + if (ya_node->n_intrs < 2) { + ya_node->intrs = &gatwick_int_pool[count]; + count += 2; + } + ya_node->n_intrs = 2; + ya_node->intrs[0].line = 19+irq_base; + ya_node->intrs[1].line = 1+irq_base; + printk(KERN_INFO "irq: fixed floppy on second controller (%d,%d)\n", + ya_node->intrs[0].line, ya_node->intrs[1].line); + } + if (strcasecmp(ya_node->name, "ata4") == 0) { + if (ya_node->n_intrs < 2) { + ya_node->intrs = &gatwick_int_pool[count]; + count += 2; + } + ya_node->n_intrs = 2; + ya_node->intrs[0].line = 14+irq_base; + ya_node->intrs[1].line = 3+irq_base; + printk(KERN_INFO "irq: fixed ide on second controller (%d,%d)\n", + ya_node->intrs[0].line, ya_node->intrs[1].line); + } + ya_node = ya_node->sibling; + } + } + node = node->sibling; + } + if (count > 10) { + printk("WARNING !! Gatwick interrupt pool overflow\n"); + printk(" GATWICK_IRQ_POOL_SIZE = %d\n", GATWICK_IRQ_POOL_SIZE); + printk(" requested = %d\n", count); + } } -void free_irq(unsigned int irq, void *dev_id) +#ifdef __SMP__ +static void openpic_ipi_action(int cpl, void *dev_id, struct pt_regs *regs) { - request_irq(irq, NULL, 0, NULL, dev_id); + smp_message_recv(); } +#endif /* __SMP__ */ -unsigned long probe_irq_on (void) + +#else /* CONFIG_8xx */ + +static void mbx_i8259_action(int cpl, void *dev_id, struct pt_regs *regs) { - return 0; + int bits, irq; + + /* A bug in the QSpan chip causes it to give us 0xff always + * when doing a character read. So read 32 bits and shift. + * This doesn't seem to return useful values anyway, but + * read it to make sure things are acked. + * -- Cort + */ + irq = (inl(0x508) >> 24)&0xff; + if ( irq != 0xff ) printk("iack %d\n", irq); + + outb(0x0C, 0x20); + irq = inb(0x20) & 7; + if (irq == 2) + { + outb(0x0C, 0xA0); + irq = inb(0xA0); + irq = (irq&7) + 8; + } + bits = 1UL << irq; + irq += i8259_pic.irq_offset; + dispatch_handler( regs, irq ); } -int probe_irq_off (unsigned long irqs) +static void mbx_mask_and_ack(unsigned int irq_nr) +{ + /* this shouldn't be masked, we mask the 8259 if we need to -- Cort */ + if ( irq_nr != ISA_BRIDGE_INT ) + mbx_mask_irq(irq_nr); + if ( irq_nr >= ppc8xx_pic.irq_offset ) + irq_nr -= ppc8xx_pic.irq_offset; + /* clear the pending bits */ + ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sipend = 1 << (31-irq_nr); +} + +static void mbx_mask_irq(unsigned int irq_nr) { - return 0; + if ( irq_nr == ISA_BRIDGE_INT ) return; + if ( irq_nr >= ppc8xx_pic.irq_offset ) + irq_nr -= ppc8xx_pic.irq_offset; + cached_irq_mask[0] &= ~(1 << (31-irq_nr)); + ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask = cached_irq_mask[0]; } -#ifndef CONFIG_8xx -__initfunc(static void i8259_init(void)) +static void mbx_unmask_irq(unsigned int irq_nr) +{ + if ( irq_nr >= ppc8xx_pic.irq_offset ) + irq_nr -= ppc8xx_pic.irq_offset; + cached_irq_mask[0] |= (1 << (31-irq_nr)); + ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask = cached_irq_mask[0]; +} +#endif /* CONFIG_8xx */ + +static void __init i8259_init(void) { /* init master interrupt controller */ outb(0x11, 0x20); /* Start init sequence */ @@ -985,7 +915,6 @@ outb(0x04, 0x21); /* edge tiggered, Cascade (slave) on IRQ2 */ outb(0x01, 0x21); /* Select 8086 mode */ outb(0xFF, 0x21); /* Mask all */ - /* init slave interrupt controller */ outb(0x11, 0xA0); /* Start init sequence */ outb(0x08, 0xA1); /* Vector base */ @@ -994,32 +923,31 @@ outb(0xFF, 0xA1); /* Mask all */ outb(cached_A1, 0xA1); outb(cached_21, 0x21); - if (request_irq(2, no_action, SA_INTERRUPT, "cascade", NULL) != 0) - panic("Could not allocate cascade IRQ!"); - enable_irq(2); /* Enable cascade interrupt */ + request_irq( i8259_pic.irq_offset + 2, no_action, SA_INTERRUPT, + "8259 secondary cascade", NULL ); + enable_irq(i8259_pic.irq_offset + 2); /* Enable cascade interrupt */ } -#endif /* CONFIG_8xx */ -/* On MBX8xx, the interrupt control (SIEL) was set by EPPC-bug. External - * interrupts can be either edge or level triggered, but there is no - * reason for us to change the EPPC-bug values (it would not work if we did). - */ -__initfunc(void init_IRQ(void)) +void __init init_IRQ(void) { extern void xmon_irq(int, void *, struct pt_regs *); int i; + static int once = 0; +#ifndef CONFIG_8xx struct device_node *irqctrler; unsigned long addr; struct device_node *np; - + int second_irq = -999; +#endif + if ( once ) + return; + else + once++; + #ifndef CONFIG_8xx switch (_machine) { case _MACH_Pmac: - mask_and_ack_irq = pmac_mask_and_ack_irq; - mask_irq = pmac_mask_irq; - unmask_irq = pmac_unmask_irq; - /* G3 powermacs have 64 interrupts, G3 Series PowerBook have 128, others have 32 */ max_irqs = max_real_irqs = 32; @@ -1032,7 +960,9 @@ else max_irqs = 64; } - + for ( i = 0; i < max_real_irqs ; i++ ) + irq_desc[i].ctl = &pmac_pic; + /* get addresses of first controller */ if (irqctrler) { if (irqctrler->n_addrs > 0) { @@ -1057,7 +987,6 @@ /* disable all interrupts in all controllers */ for (i = 0; i * 32 < max_irqs; ++i) out_le32(&pmac_irq_hw[i]->enable, 0); - /* get interrupt line of secondary interrupt controller */ if (irqctrler) { @@ -1066,7 +995,10 @@ (int)second_irq); if (device_is_compatible(irqctrler, "gatwick")) pmac_fix_gatwick_interrupts(irqctrler, max_real_irqs); - enable_irq(second_irq); + for ( i = max_real_irqs ; i < max_irqs ; i++ ) + irq_desc[i].ctl = &gatwick_pic; + request_irq( second_irq, gatwick_action, SA_INTERRUPT, + "gatwick cascade", 0 ); } printk("System has %d possible interrupts\n", max_irqs); if (max_irqs != max_real_irqs) @@ -1074,124 +1006,287 @@ max_real_irqs); #ifdef CONFIG_XMON - request_irq(20, xmon_irq, 0, "NMI", 0); + request_irq(20, xmon_irq, 0, "NMI - XMON", 0); #endif /* CONFIG_XMON */ break; case _MACH_chrp: - mask_and_ack_irq = chrp_mask_and_ack_irq; - mask_irq = chrp_mask_irq; - unmask_irq = chrp_unmask_irq; - if ( !(np = find_devices("pci") ) ) printk("Cannot find pci to get ack address\n"); else { chrp_int_ack_special = (volatile unsigned char *) - (*(unsigned long *)get_property(np, + (*(unsigned long *)get_property(np, "8259-interrupt-acknowledge", NULL)); } + for ( i = 16 ; i < 36 ; i++ ) + irq_desc[i].ctl = &open_pic; + /* openpic knows that it's at irq 16 offset + * so we don't need to set it in the pic structure + * -- Cort + */ openpic_init(1); + for ( i = 0 ; i < 16 ; i++ ) + irq_desc[i].ctl = &i8259_pic; i8259_init(); - cached_irq_mask[0] = cached_irq_mask[1] = ~0UL; #ifdef CONFIG_XMON request_irq(openpic_to_irq(HYDRA_INT_ADB_NMI), xmon_irq, 0, "NMI", 0); #endif /* CONFIG_XMON */ +#ifdef __SMP__ + request_irq(openpic_to_irq(OPENPIC_VEC_SPURIOUS), + openpic_ipi_action, 0, "IPI0", 0); +#endif /* __SMP__ */ break; case _MACH_prep: - mask_and_ack_irq = i8259_mask_and_ack_irq; - mask_irq = i8259_mask_irq; - unmask_irq = i8259_unmask_irq; - cached_irq_mask[0] = ~0UL; - + for ( i = 0 ; i < 16 ; i++ ) + irq_desc[i].ctl = &i8259_pic; i8259_init(); - /* - * According to the Carolina spec from ibm irqs 0,1,2, and 8 - * must be edge triggered. Also, the pci intrs must be level - * triggered and _only_ isa intrs can be level sensitive - * which are 3-7,9-12,14-15. 13 is special - it can be level. - * - * power on default is 0's in both regs - all edge. - * - * These edge/level control regs allow edge/level status - * to be decided on a irq basis instead of on a PIC basis. - * It's still pretty ugly. - * - Cort - */ - { - unsigned char irq_mode1 = 0, irq_mode2 = 0; - irq_mode1 = 0; /* to get rid of compiler warnings */ - /* - * On Carolina, irq 15 and 13 must be level (scsi/ide/net). - */ - if ( _prep_type == _PREP_IBM ) - irq_mode2 |= 0xa0; - } break; #ifdef CONFIG_APUS case _MACH_apus: - mask_irq = amiga_disable_irq; - unmask_irq = amiga_enable_irq; apus_init_IRQ(); break; #endif } -#endif /* CONFIG_8xx */ +#else /* CONFIG_8xx */ + ppc8xx_pic.irq_offset = 16; + for ( i = 16 ; i < 32 ; i++ ) + irq_desc[i].ctl = &ppc8xx_pic; + unmask_irq(CPM_INTERRUPT); + + for ( i = 0 ; i < 16 ; i++ ) + irq_desc[i].ctl = &i8259_pic; + i8259_init(); + request_irq(ISA_BRIDGE_INT, mbx_i8259_action, 0, "8259 cascade", NULL); + enable_irq(ISA_BRIDGE_INT); +#endif /* CONFIG_8xx */ } -/* This routine will fix some missing interrupt values in the device tree - * on the gatwick mac-io controller used by some PowerBooks - */ -static void __init pmac_fix_gatwick_interrupts(struct device_node *gw, int irq_base) +#ifdef __SMP__ +unsigned char global_irq_holder = NO_PROC_ID; +unsigned volatile int global_irq_lock; +atomic_t global_irq_count; + +atomic_t global_bh_count; +atomic_t global_bh_lock; + +static void show(char * str) { - struct device_node *node; - static struct interrupt_info int_pool[4]; - - memset(int_pool, 0, sizeof(int_pool)); - node = gw->child; - while(node) - { - /* Fix SCC */ - if (strcasecmp(node->name, "escc") == 0) - if (node->child && node->child->n_intrs == 0) - { - node->child->n_intrs = 1; - node->child->intrs = &int_pool[0]; - int_pool[0].line = 15+irq_base; - printk(KERN_INFO "irq: fixed SCC on second controller (%d)\n", - int_pool[0].line); - } - /* Fix media-bay & left SWIM */ - if (strcasecmp(node->name, "media-bay") == 0) - { - struct device_node* ya_node; + int i; + unsigned long *stack; + int cpu = smp_processor_id(); - if (node->n_intrs == 0) - { - node->n_intrs = 1; - node->intrs = &int_pool[1]; - int_pool[1].line = 29+irq_base; - printk(KERN_INFO "irq: fixed media-bay on second controller (%d)\n", - int_pool[1].line); - } - ya_node = node->child; - while(ya_node) - { - if ((strcasecmp(ya_node->name, "floppy") == 0) && - ya_node->n_intrs == 0) - { - ya_node->n_intrs = 2; - ya_node->intrs = &int_pool[2]; - int_pool[2].line = 19+irq_base; - int_pool[3].line = 1+irq_base; - printk(KERN_INFO "irq: fixed floppy on second controller (%d,%d)\n", - int_pool[2].line, int_pool[3].line); - } - ya_node = ya_node->sibling; + printk("\n%s, CPU %d:\n", str, cpu); + printk("irq: %d [%d %d]\n", + atomic_read(&global_irq_count), local_irq_count[0], local_irq_count[1]); + printk("bh: %d [%d %d]\n", + atomic_read(&global_bh_count), local_bh_count[0], local_bh_count[1]); + stack = (unsigned long *) &str; + for (i = 40; i ; i--) { + unsigned long x = *++stack; + if (x > (unsigned long) &init_task_union && x < (unsigned long) &vsprintf) { + printk("<[%08lx]> ", x); + } + } +} + +static inline void wait_on_bh(void) +{ + int count = MAXCOUNT; + do { + if (!--count) { + show("wait_on_bh"); + count = ~0; + } + /* nothing .. wait for the other bh's to go away */ + } while (atomic_read(&global_bh_count) != 0); +} + + +static inline void wait_on_irq(int cpu) +{ + int count = MAXCOUNT; + + for (;;) { + + /* + * Wait until all interrupts are gone. Wait + * for bottom half handlers unless we're + * already executing in one.. + */ + if (!atomic_read(&global_irq_count)) { + if (local_bh_count[cpu] || !atomic_read(&global_bh_count)) + break; + } + + /* Duh, we have to loop. Release the lock to avoid deadlocks */ + clear_bit(0,&global_irq_lock); + + for (;;) { + if (!--count) { + show("wait_on_irq"); + count = ~0; } + __sti(); + /* don't worry about the lock race Linus found + * on intel here. -- Cort + */ + __cli(); + if (atomic_read(&global_irq_count)) + continue; + if (global_irq_lock) + continue; + if (!local_bh_count[cpu] && atomic_read(&global_bh_count)) + continue; + if (!test_and_set_bit(0,&global_irq_lock)) + break; } - node = node->sibling; } +} + +/* + * This is called when we want to synchronize with + * bottom half handlers. We need to wait until + * no other CPU is executing any bottom half handler. + * + * Don't wait if we're already running in an interrupt + * context or are inside a bh handler. + */ +void synchronize_bh(void) +{ + if (atomic_read(&global_bh_count) && !in_interrupt()) + wait_on_bh(); +} + +/* + * This is called when we want to synchronize with + * interrupts. We may for example tell a device to + * stop sending interrupts: but to make sure there + * are no interrupts that are executing on another + * CPU we need to call this function. + */ +void synchronize_irq(void) +{ + if (atomic_read(&global_irq_count)) { + /* Stupid approach */ + cli(); + sti(); + } +} + +static inline void get_irqlock(int cpu) +{ + unsigned int loops = MAXCOUNT; + + if (test_and_set_bit(0,&global_irq_lock)) { + /* do we already hold the lock? */ + if ((unsigned char) cpu == global_irq_holder) + return; + /* Uhhuh.. Somebody else got it. Wait.. */ + do { + do { + if (loops-- == 0) { + printk("get_irqlock(%d) waiting, global_irq_holder=%d\n", cpu, global_irq_holder); +#ifdef CONFIG_XMON + xmon(0); +#endif + } + } while (test_bit(0,&global_irq_lock)); + } while (test_and_set_bit(0,&global_irq_lock)); + } + /* + * We also need to make sure that nobody else is running + * in an interrupt context. + */ + wait_on_irq(cpu); + + /* + * Ok, finally.. + */ + global_irq_holder = cpu; +} + +/* + * A global "cli()" while in an interrupt context + * turns into just a local cli(). Interrupts + * should use spinlocks for the (very unlikely) + * case that they ever want to protect against + * each other. + * + * If we already have local interrupts disabled, + * this will not turn a local disable into a + * global one (problems with spinlocks: this makes + * save_flags+cli+sti usable inside a spinlock). + */ +void __global_cli(void) +{ + unsigned int flags; + __save_flags(flags); + if (flags & (1 << 15)) { + int cpu = smp_processor_id(); + __cli(); + if (!local_irq_count[cpu]) + get_irqlock(cpu); + } } + +void __global_sti(void) +{ + int cpu = smp_processor_id(); + + if (!local_irq_count[cpu]) + release_irqlock(cpu); + __sti(); +} + +/* + * SMP flags value to restore to: + * 0 - global cli + * 1 - global sti + * 2 - local cli + * 3 - local sti + */ +unsigned long __global_save_flags(void) +{ + int retval; + int local_enabled; + unsigned long flags; + + __save_flags(flags); + local_enabled = (flags >> 15) & 1; + /* default to local */ + retval = 2 + local_enabled; + + /* check for global flags if we're not in an interrupt */ + if (!local_irq_count[smp_processor_id()]) { + if (local_enabled) + retval = 1; + if (global_irq_holder == (unsigned char) smp_processor_id()) + retval = 0; + } + return retval; +} + +void __global_restore_flags(unsigned long flags) +{ + switch (flags) { + case 0: + __global_cli(); + break; + case 1: + __global_sti(); + break; + case 2: + __cli(); + break; + case 3: + __sti(); + break; + default: + printk("global_restore_flags: %08lx (%08lx)\n", + flags, (&flags)[-1]); + } +} +#endif /* __SMP__ */ diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/misc.S linux/arch/ppc/kernel/misc.S --- v2.2.3/linux/arch/ppc/kernel/misc.S Thu Dec 31 10:28:59 1998 +++ linux/arch/ppc/kernel/misc.S Wed Mar 10 21:30:32 1999 @@ -109,7 +109,13 @@ * Flush MMU TLB */ _GLOBAL(_tlbia) + sync tlbia + sync +#ifdef __SMP__ + tlbsync + sync +#endif blr /* @@ -117,11 +123,17 @@ */ _GLOBAL(_tlbie) tlbie r3 + sync +#ifdef __SMP__ + tlbsync + sync +#endif blr + /* * Atomic [test&set] exchange * - * void *xchg_u32(void *ptr, unsigned long val) + * unsigned long xchg_u32(void *ptr, unsigned long val) * Changes the memory location '*ptr' to be val and returns * the previous value stored there. */ @@ -133,6 +145,27 @@ blr /* + * Try to acquire a spinlock. + * Only does the stwcx. if the load returned 0 - the Programming + * Environments Manual suggests not doing unnecessary stcwx.'s + * since they may inhibit forward progress by other CPUs in getting + * a lock. + */ +_GLOBAL(__spin_trylock) + mr r4,r3 + eieio /* prevent reordering of stores */ + li r5,-1 + lwarx r3,0,r4 /* fetch old value, establish reservation */ + cmpwi 0,r3,0 /* is it 0? */ + bnelr- /* return failure if not */ + stwcx. r5,0,r4 /* try to update with new value */ + bne- 1f /* if we failed */ + eieio /* prevent reordering of stores */ + blr +1: li r3,1 /* return non-zero for failure */ + blr + +/* * Atomic add/sub/inc/dec operations * * void atomic_add(int c, int *v) @@ -580,6 +613,16 @@ stfd 0,-4(r5) blr + .globl __clear_msr_me +__clear_msr_me: + mfmsr r0 /* Get current interrupt state */ + lis r3,0 + ori r3,r3,MSR_ME + andc r0,r0,r3 /* Clears bit in (r4) */ + sync /* Some chip revs have problems here */ + mtmsr r0 /* Update machine state */ + blr + _GLOBAL(cvt_df) cvt_df: lfd 0,-4(r5) /* load up fpscr value */ @@ -836,4 +879,5 @@ .long sys_sendfile .long sys_ni_syscall /* streams1 */ .long sys_ni_syscall /* streams2 */ + .long sys_vfork .space (NR_syscalls-183)*4 diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/openpic.c linux/arch/ppc/kernel/openpic.c --- v2.2.3/linux/arch/ppc/kernel/openpic.c Mon Oct 5 13:13:36 1998 +++ linux/arch/ppc/kernel/openpic.c Fri Mar 19 10:50:03 1999 @@ -107,7 +107,7 @@ } #endif -static inline u_int openpic_read(volatile u_int *addr) +u_int openpic_read(volatile u_int *addr) { u_int val; @@ -176,8 +176,8 @@ __initfunc(void openpic_init(int main_pic)) { u_int t, i; - u_int vendorid, devid, stepping, timerfreq; - const char *version, *vendor, *device; + u_int timerfreq; + const char *version; if (!OpenPIC) panic("No OpenPIC found"); @@ -200,32 +200,6 @@ OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1; printk("OpenPIC Version %s (%d CPUs and %d IRQ sources) at %p\n", version, NumProcessors, NumSources, OpenPIC); - - t = openpic_read(&OpenPIC->Global.Vendor_Identification); - vendorid = t & OPENPIC_VENDOR_ID_VENDOR_ID_MASK; - devid = (t & OPENPIC_VENDOR_ID_DEVICE_ID_MASK) >> - OPENPIC_VENDOR_ID_DEVICE_ID_SHIFT; - stepping = (t & OPENPIC_VENDOR_ID_STEPPING_MASK) >> - OPENPIC_VENDOR_ID_STEPPING_SHIFT; - switch (vendorid) { - case OPENPIC_VENDOR_ID_APPLE: - vendor = "Apple"; - break; - default: - vendor = "Unknown"; - break; - } - switch (devid) { - case OPENPIC_DEVICE_ID_APPLE_HYDRA: - device = "Hydra"; - break; - default: - device = "Unknown"; - break; - } - printk("OpenPIC Vendor %d (%s), Device %d (%s), Stepping %d\n", vendorid, - vendor, devid, device, stepping); - timerfreq = openpic_read(&OpenPIC->Global.Timer_Frequency); printk("OpenPIC timer frequency is "); if (timerfreq) diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/pci.c linux/arch/ppc/kernel/pci.c --- v2.2.3/linux/arch/ppc/kernel/pci.c Fri Jan 8 22:36:03 1999 +++ linux/arch/ppc/kernel/pci.c Fri Mar 19 10:50:03 1999 @@ -1,5 +1,5 @@ /* - * $Id: pci.c,v 1.43 1998/12/29 18:55:11 cort Exp $ + * $Id: pci.c,v 1.53 1999/03/12 23:38:02 cort Exp $ * Common pmac/prep/chrp pci routines. -- Cort */ @@ -24,10 +24,11 @@ unsigned long isa_io_base; unsigned long isa_mem_base; unsigned long pci_dram_offset; - unsigned int * pci_config_address; unsigned char * pci_config_data; +static void fix_intr(struct device_node *node, struct pci_dev *dev); + /* * It would be nice if we could create a include/asm/pci.h and have just * function ptrs for all these in there, but that isn't the case. @@ -73,12 +74,14 @@ ptr_pcibios_write_config_word = name##_pcibios_write_config_word; \ ptr_pcibios_write_config_dword = name##_pcibios_write_config_dword +decl_config_access_method(mech1); decl_config_access_method(pmac); decl_config_access_method(grackle); decl_config_access_method(gg2); decl_config_access_method(raven); decl_config_access_method(prep); decl_config_access_method(mbx); +decl_config_access_method(python); int pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned char *val) @@ -116,17 +119,24 @@ return 1; } -__initfunc(void pcibios_init(void)) +void __init pcibios_init(void) { } -__initfunc(void - setup_pci_ptrs(void)) +void __init setup_pci_ptrs(void) { -#ifndef CONFIG_MBX +#ifndef CONFIG_MBX PPC_DEVICE *hostbridge; switch (_machine) { case _MACH_prep: + printk("PReP architecture\n"); + if ( _prep_type == _PREP_Radstone ) + { + printk("Setting PCI access method to mech1\n"); + set_config_access_method(mech1); + break; + } + hostbridge=residual_find_device(PROCESSORDEVICE, NULL, BridgeController, PCIBridge, -1, 0); @@ -163,13 +173,30 @@ if ( !strncmp("MOT", get_property(find_path_device("/"), "model", NULL),3) ) { + pci_dram_offset = 0; + isa_mem_base = 0xf7000000; isa_io_base = 0xfe000000; set_config_access_method(grackle); } else { - isa_io_base = GG2_ISA_IO_BASE; - set_config_access_method(gg2); + if ( !strncmp("F5", + get_property(find_path_device("/"), + "ibm,model-class", NULL),2) ) + + { + pci_dram_offset = 0x80000000; + isa_mem_base = 0xa0000000; + isa_io_base = 0x88000000; + set_config_access_method(python); + } + else + { + pci_dram_offset = 0; + isa_mem_base = 0xf7000000; + isa_io_base = 0xf8000000; + set_config_access_method(gg2); + } } break; default: @@ -181,7 +208,7 @@ #undef set_config_access_method } -__initfunc(void pcibios_fixup(void)) +void __init pcibios_fixup(void) { extern unsigned long route_pci_interrupts(void); struct pci_dev *dev; @@ -193,6 +220,12 @@ switch (_machine ) { case _MACH_prep: + if ( _prep_type == _PREP_Radstone ) + { + printk("Radstone boards require no PCI fixups\n"); + break; + } + route_pci_interrupts(); for(dev=pci_devices; dev; dev=dev->next) { @@ -207,7 +240,7 @@ { if ( dev->base_address[i] > 0x10000000 ) { - printk("Relocating PCI address %x -> %x\n", + printk("Relocating PCI address %lx -> %lx\n", dev->base_address[i], (dev->base_address[i] & 0x00FFFFFF) | 0x01000000); @@ -245,23 +278,12 @@ * AAPL,interrupts property. */ struct bridge_data *bp = bridges[dev->bus->number]; - struct device_node *node; - unsigned int *reg; unsigned char pin; - + if (pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin) || - !pin) + !pin) continue; /* No interrupt generated -> no fixup */ - for (node = bp->node->child; node != 0; - node = node->sibling) { - reg = (unsigned int *) get_property(node, "reg", 0); - if (reg == 0 || ((reg[0] >> 8) & 0xff) != dev->devfn) - continue; - /* this is the node, see if it has interrupts */ - if (node->n_intrs > 0) - dev->irq = node->intrs[0].line; - break; - } + fix_intr(bp->node->child, dev); } break; } @@ -272,11 +294,36 @@ #endif /* CONFIG_MBX */ } -__initfunc(void pcibios_fixup_bus(struct pci_bus *bus)) +void __init pcibios_fixup_bus(struct pci_bus *bus) { } -__initfunc(char *pcibios_setup(char *str)) +char __init *pcibios_setup(char *str) { return str; } + +#ifndef CONFIG_MBX +/* Recursively searches any node that is of type PCI-PCI bridge. Without + * this, the old code would miss children of P2P bridges and hence not + * fix IRQ's for cards located behind P2P bridges. + * - Ranjit Deshpande, 01/20/99 + */ +static void __init fix_intr(struct device_node *node, struct pci_dev *dev) +{ + unsigned int *reg, *class_code; + + for (; node != 0;node = node->sibling) { + class_code = (unsigned int *) get_property(node, "class-code", 0); + if((*class_code >> 8) == PCI_CLASS_BRIDGE_PCI) + fix_intr(node->child, dev); + reg = (unsigned int *) get_property(node, "reg", 0); + if (reg == 0 || ((reg[0] >> 8) & 0xff) != dev->devfn) + continue; + /* this is the node, see if it has interrupts */ + if (node->n_intrs > 0) + dev->irq = node->intrs[0].line; + break; + } +} +#endif diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/pmac_pci.c linux/arch/ppc/kernel/pmac_pci.c --- v2.2.3/linux/arch/ppc/kernel/pmac_pci.c Thu Nov 19 09:56:27 1998 +++ linux/arch/ppc/kernel/pmac_pci.c Wed Mar 10 21:30:32 1999 @@ -84,7 +84,12 @@ (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + (offset & ~3)); } else { - out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1); + /* Bus number once again taken into consideration. + * Change applied from 2.1.24. This makes devices located + * behind PCI-PCI bridges visible. + * -Ranjit Deshpande, 01/20/99 + */ + out_le32(bp->cfg_addr, (bus << 16) + (dev_fn << 8) + (offset & ~3) + 1); } udelay(2); *val = in_8(bp->cfg_data + (offset & 3)); @@ -109,7 +114,8 @@ (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + (offset & ~3)); } else { - out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1); + /* See pci_read_config_byte */ + out_le32(bp->cfg_addr, (bus << 16) + (dev_fn << 8) + (offset & ~3) + 1); } udelay(2); *val = in_le16((volatile unsigned short *)(bp->cfg_data + (offset & 3))); @@ -134,7 +140,8 @@ (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + offset); } else { - out_le32(bp->cfg_addr, (dev_fn << 8) + offset + 1); + /* See pci_read_config_byte */ + out_le32(bp->cfg_addr, (bus << 16) + (dev_fn << 8) + offset + 1); } udelay(2); *val = in_le32((volatile unsigned int *)bp->cfg_data); @@ -156,7 +163,8 @@ (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + (offset & ~3)); } else { - out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1); + /* See pci_read_config_byte */ + out_le32(bp->cfg_addr, (bus << 16) + (dev_fn << 8) + (offset & ~3) + 1); } udelay(2); out_8(bp->cfg_data + (offset & 3), val); @@ -180,7 +188,8 @@ (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + (offset & ~3)); } else { - out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1); + /* See pci_read_config_byte */ + out_le32(bp->cfg_addr, (bus << 16) + (dev_fn << 8) + (offset & ~3) + 1); } udelay(2); out_le16((volatile unsigned short *)(bp->cfg_data + (offset & 3)), val); @@ -204,7 +213,8 @@ (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + offset); } else { - out_le32(bp->cfg_addr, (dev_fn << 8) + offset + 1); + /* See pci_read_config_byte */ + out_le32(bp->cfg_addr, (bus << 16) + (dev_fn << 8) + (offset & ~3) + 1); } udelay(2); out_le32((volatile unsigned int *)bp->cfg_data, val); diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/pmac_setup.c linux/arch/ppc/kernel/pmac_setup.c --- v2.2.3/linux/arch/ppc/kernel/pmac_setup.c Thu Nov 19 09:56:27 1998 +++ linux/arch/ppc/kernel/pmac_setup.c Wed Mar 10 21:30:32 1999 @@ -56,6 +56,9 @@ unsigned char drive_info; +int ppc_override_l2cr = 0; +int ppc_override_l2cr_value; + extern char saved_command_line[]; #define DEFAULT_ROOT_DEVICE 0x0801 /* sda1 - slightly silly choice */ @@ -132,6 +135,16 @@ } } + /* Checks "l2cr-value" property in the registry */ + np = find_devices("cpus"); + if (np != 0) { + unsigned int *l2cr = (unsigned int *) + get_property(np, "l2cr-value", NULL); + if (l2cr != 0) { + len += sprintf(buffer+len, "l2cr override\t: 0x%x\n", *l2cr); + } + } + return len; } @@ -209,6 +222,26 @@ ohare_init(); *memory_start_p = pmac_find_bridges(*memory_start_p, *memory_end_p); + + /* Checks "l2cr-value" property in the registry */ + if ( (_get_PVR() >> 16) == 8) { + struct device_node *np = find_devices("cpus"); + if (np != 0) { + unsigned int *l2cr = (unsigned int *) + get_property(np, "l2cr-value", NULL); + if (l2cr != 0) { + ppc_override_l2cr = 1; + ppc_override_l2cr_value = *l2cr; + _set_L2CR(0); + _set_L2CR(ppc_override_l2cr_value); + } + } + } + + if (ppc_override_l2cr) + printk(KERN_INFO "L2CR overriden (0x%x), backside cache is %s\n", + ppc_override_l2cr_value, (ppc_override_l2cr_value & 0x80000000) + ? "enabled" : "disabled"); feature_init(); diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/ppc_defs.h linux/arch/ppc/kernel/ppc_defs.h --- v2.2.3/linux/arch/ppc/kernel/ppc_defs.h Mon Oct 5 13:13:36 1998 +++ linux/arch/ppc/kernel/ppc_defs.h Wed Dec 31 16:00:00 1969 @@ -1,69 +0,0 @@ -/* - * WARNING! This file is automatically generated - DO NOT EDIT! - */ -#define KERNELBASE -1073741824 -#define STATE 0 -#define NEXT_TASK 48 -#define COUNTER 24 -#define PROCESSOR 36 -#define SIGPENDING 8 -#define TSS 568 -#define MM 872 -#define TASK_STRUCT_SIZE 912 -#define KSP 0 -#define PG_TABLES 4 -#define PGD 8 -#define LAST_SYSCALL 20 -#define PT_REGS 12 -#define PF_TRACESYS 32 -#define TASK_FLAGS 4 -#define NEED_RESCHED 20 -#define TSS_FPR0 24 -#define TSS_FPSCR 284 -#define TSS_SMP_FORK_RET 288 -#define TASK_UNION_SIZE 8192 -#define STACK_FRAME_OVERHEAD 16 -#define INT_FRAME_SIZE 192 -#define GPR0 16 -#define GPR1 20 -#define GPR2 24 -#define GPR3 28 -#define GPR4 32 -#define GPR5 36 -#define GPR6 40 -#define GPR7 44 -#define GPR8 48 -#define GPR9 52 -#define GPR10 56 -#define GPR11 60 -#define GPR12 64 -#define GPR13 68 -#define GPR14 72 -#define GPR15 76 -#define GPR16 80 -#define GPR17 84 -#define GPR18 88 -#define GPR19 92 -#define GPR20 96 -#define GPR21 100 -#define GPR22 104 -#define GPR23 108 -#define GPR24 112 -#define GPR25 116 -#define GPR26 120 -#define GPR27 124 -#define GPR28 128 -#define GPR29 132 -#define GPR30 136 -#define GPR31 140 -#define _NIP 144 -#define _MSR 148 -#define _CTR 156 -#define _LINK 160 -#define _CCR 168 -#define _XER 164 -#define _DAR 180 -#define _DSISR 184 -#define ORIG_GPR3 152 -#define RESULT 188 -#define TRAP 176 diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/ppc_ksyms.c linux/arch/ppc/kernel/ppc_ksyms.c --- v2.2.3/linux/arch/ppc/kernel/ppc_ksyms.c Fri Jan 8 22:36:03 1999 +++ linux/arch/ppc/kernel/ppc_ksyms.c Wed Mar 10 21:30:32 1999 @@ -8,11 +8,12 @@ #include #include +#include #include #include #include -#include #include +#include #include #include #include @@ -46,6 +47,7 @@ asmlinkage long long __ashrdi3(long long, int); asmlinkage int abs(int); +EXPORT_SYMBOL(clear_page); EXPORT_SYMBOL(do_signal); EXPORT_SYMBOL(syscall_trace); EXPORT_SYMBOL(transfer_to_handler); diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/prep_pci.c linux/arch/ppc/kernel/prep_pci.c --- v2.2.3/linux/arch/ppc/kernel/prep_pci.c Tue Dec 22 14:16:54 1998 +++ linux/arch/ppc/kernel/prep_pci.c Fri Mar 19 10:50:03 1999 @@ -1,5 +1,5 @@ /* - * $Id: prep_pci.c,v 1.24 1998/12/10 02:39:51 cort Exp $ + * $Id: prep_pci.c,v 1.25 1999/03/03 15:09:45 cort Exp $ * PReP pci functions. * Originally by Gary Thomas * rewritten and updated by Cort Dougan (cort@cs.nmt.edu) @@ -312,6 +312,106 @@ #define CAROLINA_IRQ_EDGE_MASK_LO 0x00 /* IRQ's 0-7 */ #define CAROLINA_IRQ_EDGE_MASK_HI 0xA4 /* IRQ's 8-15 [10,13,15] */ +/* + * 8259 edge/level control definitions + */ +#define ISA8259_M_ELCR 0x4d0 +#define ISA8259_S_ELCR 0x4d1 + +#define ELCRS_INT15_LVL 0x80 +#define ELCRS_INT14_LVL 0x40 +#define ELCRS_INT12_LVL 0x10 +#define ELCRS_INT11_LVL 0x08 +#define ELCRS_INT10_LVL 0x04 +#define ELCRS_INT9_LVL 0x02 +#define ELCRS_INT8_LVL 0x01 +#define ELCRM_INT7_LVL 0x80 +#define ELCRM_INT5_LVL 0x20 + +/* + * Mechanism 1 configuration space access as defined in the PCI spec. + */ +#define CFG_ADDR (volatile u_int *)0x80000cf8 +#define CFG_DATA 0x80000cfc + +#define CFG_DEV_ADDR(b, d, o) (0x80000000 | \ + ((b) << 16) | \ + ((d) << 8) | \ + ((o) & ~3)) + +unsigned char max_bus=255; + +int mech1_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char *val) +{ + *val = 0xff; + if (bus > max_bus) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le32(CFG_ADDR, CFG_DEV_ADDR(bus, dev_fn, offset)); + *val = in_8((unsigned char *)CFG_DATA + (offset & 3)); + return PCIBIOS_SUCCESSFUL; +} + +int mech1_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val) +{ + *val = 0xffff; + if (bus > max_bus) + return PCIBIOS_DEVICE_NOT_FOUND; + if ((offset & 1) != 0) + return PCIBIOS_BAD_REGISTER_NUMBER; + out_le32(CFG_ADDR, CFG_DEV_ADDR(bus, dev_fn, offset)); + *val = in_le16((volatile unsigned short *)(CFG_DATA + (offset&3))); + return PCIBIOS_SUCCESSFUL; +} + +int mech1_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int *val) +{ + *val = 0xffffffff; + if (bus > max_bus) + return PCIBIOS_DEVICE_NOT_FOUND; + if ((offset & 3) != 0) + return PCIBIOS_BAD_REGISTER_NUMBER; + out_le32(CFG_ADDR, CFG_DEV_ADDR(bus, dev_fn, offset)); + *val = in_le32((volatile unsigned int *)CFG_DATA); + return PCIBIOS_SUCCESSFUL; +} + +int mech1_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char val) +{ + if (bus > max_bus) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le32(CFG_ADDR, CFG_DEV_ADDR(bus, dev_fn, offset)); + out_8((unsigned char *)CFG_DATA + (offset & 3), val); + return PCIBIOS_SUCCESSFUL; +} + +int mech1_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val) +{ + if (bus > max_bus) + return PCIBIOS_DEVICE_NOT_FOUND; + if ((offset & 1) != 0) + return PCIBIOS_BAD_REGISTER_NUMBER; + out_le32(CFG_ADDR, CFG_DEV_ADDR(bus, dev_fn, offset)); + out_le16((volatile unsigned short *)(CFG_DATA + (offset&3)), val); + return PCIBIOS_SUCCESSFUL; +} + +int mech1_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int val) +{ + if (bus > max_bus) + return PCIBIOS_DEVICE_NOT_FOUND; + if ((offset & 1) != 0) + return PCIBIOS_BAD_REGISTER_NUMBER; + out_le32(CFG_ADDR, CFG_DEV_ADDR(bus, dev_fn, offset)); + out_le32((volatile unsigned int *)CFG_DATA, val); + return PCIBIOS_SUCCESSFUL; +} + __prep int prep_pcibios_read_config_dword (unsigned char bus, @@ -526,6 +626,71 @@ outb(pl_id|CAROLINA_IRQ_EDGE_MASK_HI, 0x04d1); pl_id=inb(0x04d1); /*printk("Hi mask now %#0x\n", pl_id);*/ + } else if ( _prep_type == _PREP_Radstone ) + { + unsigned char ucElcrM, ucElcrS; + + /* + * Set up edge/level + */ + switch(ucSystemType) + { + case RS_SYS_TYPE_PPC1: + { + if(ucBoardRevMaj<5) + { + ucElcrS=ELCRS_INT15_LVL; + } + else + { + ucElcrS=ELCRS_INT9_LVL | + ELCRS_INT11_LVL | + ELCRS_INT14_LVL | + ELCRS_INT15_LVL; + } + ucElcrM=ELCRM_INT5_LVL | ELCRM_INT7_LVL; + break; + } + + case RS_SYS_TYPE_PPC1a: + { + ucElcrS=ELCRS_INT9_LVL | + ELCRS_INT11_LVL | + ELCRS_INT14_LVL | + ELCRS_INT15_LVL; + ucElcrM=ELCRM_INT5_LVL; + break; + } + + case RS_SYS_TYPE_PPC2: + case RS_SYS_TYPE_PPC2a: + case RS_SYS_TYPE_PPC2ep: + case RS_SYS_TYPE_PPC4: + case RS_SYS_TYPE_PPC4a: + default: + { + ucElcrS=ELCRS_INT9_LVL | + ELCRS_INT10_LVL | + ELCRS_INT11_LVL | + ELCRS_INT14_LVL | + ELCRS_INT15_LVL; + ucElcrM=ELCRM_INT5_LVL | + ELCRM_INT7_LVL; + break; + } + } + + /* + * Write edge/level selection + */ + outb(ucElcrS, ISA8259_S_ELCR); + outb(ucElcrM, ISA8259_M_ELCR); + + /* + * Radstone boards have PCI interrupts all set up + * so leave well alone + */ + return 0; } else { printk("No known machine pci routing!\n"); diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/prep_setup.c linux/arch/ppc/kernel/prep_setup.c --- v2.2.3/linux/arch/ppc/kernel/prep_setup.c Tue Dec 22 14:16:54 1998 +++ linux/arch/ppc/kernel/prep_setup.c Fri Mar 19 10:50:03 1999 @@ -54,7 +54,6 @@ extern unsigned long Hash_size, Hash_mask; extern int probingmem; extern unsigned long loops_per_sec; -extern unsigned char aux_device_present; #ifdef CONFIG_BLK_DEV_RAM extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */ @@ -136,6 +135,8 @@ break; } break; + default: + break; } @@ -163,11 +164,12 @@ { extern char cmd_line[]; unsigned char reg; + unsigned char ucMothMemType; + unsigned char ucEquipPres1; /* init to some ~sane value until calibrate_delay() runs */ loops_per_sec = 50000000; - aux_device_present = 0xaa; /* Set up floppy in PS/2 mode */ outb(0x09, SIO_CONFIG_RA); reg = inb(SIO_CONFIG_RD); @@ -179,16 +181,38 @@ switch ( _prep_type ) { case _PREP_IBM: + /* Enable L2. Assume we don't need to flush -- Cort*/ + *(unsigned char *)(0x8000081c) |= 3; ROOT_DEV = to_kdev_t(0x0301); /* hda1 */ break; case _PREP_Motorola: + /* Enable L2. Assume we don't need to flush -- Cort*/ + *(unsigned char *)(0x8000081c) |= 3; ROOT_DEV = to_kdev_t(0x0801); /* sda1 */ break; + case _PREP_Radstone: + ROOT_DEV = to_kdev_t(0x0801); /* sda1 */ + + /* + * Determine system type + */ + ucMothMemType=inb(0x866); + ucEquipPres1=inb(0x80c); + + ucSystemType=((ucMothMemType&0x03)<<1) | + ((ucEquipPres1&0x80)>>7); + ucSystemType^=7; + + /* + * Determine board revision for use by + * rev. specific code + */ + ucBoardRev=inb(0x854); + ucBoardRevMaj=ucBoardRev>>5; + ucBoardRevMin=ucBoardRev&0x1f; + break; } - /* Enable L2. Assume we don't need to flush -- Cort*/ - *(unsigned char *)(0x8000081c) = *(unsigned char *)(0x8000081c)|3; - printk("Boot arguments: %s\n", cmd_line); #ifdef CONFIG_SOUND_CS4232 diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/prep_time.c linux/arch/ppc/kernel/prep_time.c --- v2.2.3/linux/arch/ppc/kernel/prep_time.c Thu Aug 6 14:06:29 1998 +++ linux/arch/ppc/kernel/prep_time.c Fri Mar 19 10:50:03 1999 @@ -54,6 +54,41 @@ MOTO_RTC_CONTROLA, MOTO_RTC_CONTROLB /* 10,11 */ }; +/* + * The following struture is used to access the MK48T18 + */ +typedef volatile struct _MK48T18 { + unsigned char ucNvRAM[0x3ff8]; /* NvRAM locations */ + unsigned char ucControl; + unsigned char ucSecond; /* 0-59 */ + unsigned char ucMinute; /* 0-59 */ + unsigned char ucHour; /* 0-23 */ + unsigned char ucDay; /* 1-7 */ + unsigned char ucDate; /* 1-31 */ + unsigned char ucMonth; /* 1-12 */ + unsigned char ucYear; /* 0-99 */ +} MK48T18, *PMK48T18; + +/* + * The control register contains a 5 bit calibration value plus sign + * and read/write enable bits + */ +#define MK48T18_CTRL_CAL_MASK 0x1f +#define MK48T18_CTRL_CAL_SIGN 0x20 +#define MK48T18_CTRL_READ 0x40 +#define MK48T18_CTRL_WRITE 0x80 +/* + * The STOP bit is the most significant bit of the seconds location + */ +#define MK48T18_SEC_MASK 0x7f +#define MK48T18_SEC_STOP 0x80 +/* + * The day location also contains the frequency test bit which should + * be zero for normal operation + */ +#define MK48T18_DAY_MASK 0x07 +#define MK48T18_DAY_FT 0x40 + __prep int prep_cmos_clock_read(int addr) { @@ -65,6 +100,8 @@ outb(clock_transl[addr], NVRAM_AS0); return (inb(NVRAM_DATA)); } + else if ( _prep_type == _PREP_Radstone ) + return CMOS_READ(addr); printk("Unknown machine in prep_cmos_clock_read()!\n"); return -1; @@ -85,6 +122,12 @@ outb(val,NVRAM_DATA); return; } + else if ( _prep_type == _PREP_Radstone ) + { + CMOS_WRITE(val,addr); + return; + } + printk("Unknown machine in prep_cmos_clock_write()!\n"); } @@ -113,6 +156,7 @@ BIN_TO_BCD(tm.tm_min); BIN_TO_BCD(tm.tm_hour); BIN_TO_BCD(tm.tm_mon); + BIN_TO_BCD(tm.tm_wday); BIN_TO_BCD(tm.tm_mday); BIN_TO_BCD(tm.tm_year); } @@ -120,9 +164,10 @@ prep_cmos_clock_write(tm.tm_min,RTC_MINUTES); prep_cmos_clock_write(tm.tm_hour,RTC_HOURS); prep_cmos_clock_write(tm.tm_mon,RTC_MONTH); + prep_cmos_clock_write(tm.tm_wday+1,RTC_DAY_OF_WEEK); prep_cmos_clock_write(tm.tm_mday,RTC_DAY_OF_MONTH); prep_cmos_clock_write(tm.tm_year,RTC_YEAR); - + /* The following flags have to be released exactly in this order, * otherwise the DS12887 (popular MC146818A clone with integrated * battery and quartz) will not reset the oscillator and will not @@ -132,6 +177,42 @@ */ prep_cmos_clock_write(save_control, RTC_CONTROL); prep_cmos_clock_write(save_freq_select, RTC_FREQ_SELECT); + + /* + * Radstone Technology PPC1a boards use an MK48T18 device + * as the "master" RTC but also have a DS1287 equivalent incorporated + * into the PCI-ISA bridge device. The DS1287 is initialised by the boot + * firmware to reflect the value held in the MK48T18 and thus the + * time may be read from this device both here and in the rtc driver. + * Whenever we set the time, however, if it is to be preserved across + * boots we must also update the "master" RTC. + */ + if((_prep_type==_PREP_Radstone) && (ucSystemType==RS_SYS_TYPE_PPC1a)) + { + PMK48T18 pMk48t18=(PMK48T18)(_ISA_MEM_BASE+0x00800000); + + /* + * Set the write enable bit + */ + pMk48t18->ucControl|=MK48T18_CTRL_WRITE; + eieio(); + /* + * Update the clock + */ + pMk48t18->ucSecond=tm.tm_sec; + pMk48t18->ucMinute=tm.tm_min; + pMk48t18->ucHour=tm.tm_hour; + pMk48t18->ucMonth=tm.tm_mon; + pMk48t18->ucDay=tm.tm_wday+1; + pMk48t18->ucDate=tm.tm_mday; + pMk48t18->ucYear=tm.tm_year; + + eieio(); + /* + * Clear the write enable bit + */ + pMk48t18->ucControl&=~MK48T18_CTRL_WRITE; + } if ( (time_state == TIME_ERROR) || (time_state == TIME_BAD) ) time_state = TIME_OK; diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/process.c linux/arch/ppc/kernel/process.c --- v2.2.3/linux/arch/ppc/kernel/process.c Fri Jan 8 22:36:03 1999 +++ linux/arch/ppc/kernel/process.c Wed Mar 10 21:30:32 1999 @@ -1,5 +1,5 @@ /* - * $Id: process.c,v 1.70 1999/01/07 16:28:59 cort Exp $ + * $Id: process.c,v 1.75 1999/02/12 07:06:29 cort Exp $ * * linux/arch/ppc/kernel/process.c * @@ -49,6 +49,15 @@ extern spinlock_t scheduler_lock; struct task_struct *last_task_used_math = NULL; +static struct vm_area_struct init_mmap = INIT_MMAP; +static struct fs_struct init_fs = INIT_FS; +static struct file * init_fd_array[NR_OPEN] = { NULL, }; +static struct files_struct init_files = INIT_FILES; +static struct signal_struct init_signals = INIT_SIGNALS; +struct mm_struct init_mm = INIT_MM; +union task_union init_task_union = { INIT_TASK }; +/* only used to get secondary processor up */ +struct task_struct *current_set[NR_CPUS] = {&init_task, }; #undef SHOW_TASK_SWITCHES 1 #undef CHECK_STACK 1 @@ -65,18 +74,6 @@ return ((unsigned long)tsk) + sizeof(struct task_struct); } -static struct vm_area_struct init_mmap = INIT_MMAP; -static struct fs_struct init_fs = INIT_FS; -static struct file * init_fd_array[NR_OPEN] = { NULL, }; -static struct files_struct init_files = INIT_FILES; -static struct signal_struct init_signals = INIT_SIGNALS; - -struct mm_struct init_mm = INIT_MM; -union task_union init_task_union = { INIT_TASK }; - -/* only used to get secondary processor up */ -struct task_struct *current_set[NR_CPUS] = {&init_task, }; - int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs) { @@ -397,7 +394,6 @@ int res; - lock_kernel(); res = do_fork(SIGCHLD, regs->gpr[1], regs); /* only parent returns here */ #ifdef __SMP__ @@ -408,8 +404,13 @@ if ((current->pid == 0) && (current == &init_task)) res = 1; #endif /* __SMP__ */ - unlock_kernel(); return res; +} + +asmlinkage int sys_vfork(int p1, int p2, int p3, int p4, int p5, int p6, + struct pt_regs *regs) +{ + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->gpr[1], regs); } asmlinkage int sys_execve(unsigned long a0, unsigned long a1, unsigned long a2, diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/prom.c linux/arch/ppc/kernel/prom.c --- v2.2.3/linux/arch/ppc/kernel/prom.c Thu Nov 19 09:56:27 1998 +++ linux/arch/ppc/kernel/prom.c Fri Mar 19 10:50:03 1999 @@ -1,5 +1,5 @@ /* - * $Id: prom.c,v 1.46 1998/11/11 03:55:09 paulus Exp $ + * $Id: prom.c,v 1.50 1999/03/16 10:40:34 cort Exp $ * * Procedures for interfacing to the Open Firmware PROM on * Power Macintosh computers. @@ -99,10 +99,12 @@ static struct device_node *allnodes = 0; static void clearscreen(void); +static void flushscreen(void); #ifdef CONFIG_BOOTX_TEXT static void drawchar(char c); +static void drawhex(unsigned long v); static void drawstring(const char *c); static void scrollscreen(void); @@ -167,6 +169,11 @@ #define ALIGN(x) (((x) + sizeof(unsigned long)-1) & -sizeof(unsigned long)) +/* Is boot-info compatible ? */ +#define BOOT_INFO_IS_COMPATIBLE(bi) ((bi)->compatible_version <= BOOT_INFO_VERSION) +#define BOOT_INFO_IS_V2_COMPATIBLE(bi) ((bi)->version >= 2) +#define BOOT_INFO_IS_V4_COMPATIBLE(bi) ((bi)->version >= 4) + __init static void prom_exit() @@ -256,6 +263,9 @@ void prom_init(int r3, int r4, prom_entry pp) { + int cpu = 0, i; + phandle node; + char type[16], *path; unsigned long mem; ihandle prom_rtas; unsigned long offset = reloc_offset(); @@ -273,6 +283,9 @@ unsigned long space; unsigned long ptr, x; char *model; +#ifdef CONFIG_BOOTX_TEXT + unsigned long flags; +#endif RELOC(boot_infos) = PTRUNRELOC(bi); @@ -283,32 +296,72 @@ RELOC(g_loc_Y) = 0; RELOC(g_max_loc_X) = (bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) / 8; RELOC(g_max_loc_Y) = (bi->dispDeviceRect[3] - bi->dispDeviceRect[1]) / 16; - prom_print(RELOC("Welcome to Linux, kernel " UTS_RELEASE " booting...\n")); + + /* Test if boot-info is compatible. Done only in config CONFIG_BOOTX_TEXT since + there is nothing much we can do with an incompatible version, except display + a message and eventually hang the processor... + + I'll try to keep enough of boot-info compatible in the future to always allow + display of this message; + */ + if (!BOOT_INFO_IS_COMPATIBLE(bi)) + prom_print(RELOC(" !!! WARNING - Incompatible version of BootX !!!\n\n\n")); + + prom_print(RELOC("Welcome to Linux, kernel " UTS_RELEASE "\n")); + prom_print(RELOC("\nstarted at : 0x")); + drawhex(reloc_offset() + KERNELBASE); + prom_print(RELOC("\nlinked at : 0x")); + drawhex(KERNELBASE); + prom_print(RELOC("\nframe buffer at : 0x")); + drawhex((unsigned long)bi->dispDeviceBase); + prom_print(RELOC(" (phys), 0x")); + drawhex((unsigned long)bi->logicalDisplayBase); + prom_print(RELOC(" (log)")); + prom_print(RELOC("\nMSR : 0x")); + __asm__ __volatile__ ("mfmsr %0" : "=r" ((flags)) : : "memory"); + drawhex(flags); + prom_print(RELOC("\n\n")); #endif - - /* - * XXX If this is an iMac, turn off the USB controller. + /* Out of the #if/#endif since it flushes the clearscreen too */ + flushscreen(); + + /* New BootX enters kernel with MMU off, i/os are not allowed + here. This hack will have been done by the boostrap anyway. */ - model = (char *) early_get_property - (r4 + bi->deviceTreeOffset, 4, RELOC("model")); - if (model && strcmp(model, RELOC("iMac,1")) == 0) { - out_le32((unsigned *)0x80880008, 1); /* XXX */ + if (bi->version < 4) { + /* + * XXX If this is an iMac, turn off the USB controller. + */ + model = (char *) early_get_property + (r4 + bi->deviceTreeOffset, 4, RELOC("model")); + if (model && strcmp(model, RELOC("iMac,1")) == 0) { + out_le32((unsigned *)0x80880008, 1); /* XXX */ + } } - + space = bi->deviceTreeOffset + bi->deviceTreeSize; if (bi->ramDisk) space = bi->ramDisk + bi->ramDiskSize; RELOC(klimit) = PTRUNRELOC((char *) bi + space); - /* - * Touch each page to make sure the PTEs for them - * are in the hash table - the aim is to try to avoid - * getting DSI exceptions while copying the kernel image. + /* New BootX will have flushed all TLBs and enters kernel with + MMU switched OFF, so this should not be useful anymore. */ - for (ptr = (KERNELBASE + offset) & PAGE_MASK; - ptr < (unsigned long)bi + space; ptr += PAGE_SIZE) - x = *(volatile unsigned long *)ptr; - + if (bi->version < 4) { + /* + * Touch each page to make sure the PTEs for them + * are in the hash table - the aim is to try to avoid + * getting DSI exceptions while copying the kernel image. + */ + for (ptr = (KERNELBASE + offset) & PAGE_MASK; + ptr < (unsigned long)bi + space; ptr += PAGE_SIZE) + x = *(volatile unsigned long *)ptr; + } + +#ifdef CONFIG_BOOTX_TEXT + prom_print(RELOC("booting...\n")); + flushscreen(); +#endif return; } @@ -379,7 +432,7 @@ prom_args.nret = 2; prom_args.args[0] = RELOC("instantiate-rtas"); prom_args.args[1] = prom_rtas; - prom_args.args[2] = ((void *)RELOC(rtas_data)-KERNELBASE-offset); + prom_args.args[2] = ((void *)(RELOC(rtas_data)-KERNELBASE)); RELOC(prom)(&prom_args); if (prom_args.args[nargs] != 0) i = 0; @@ -393,6 +446,81 @@ prom_print(RELOC(" done\n")); } RELOC(klimit) = (char *) (mem - offset); +#ifdef CONFIG_SMP + /* + * With CHRP SMP we need to use the OF to start the other + * processors so we can't wait until smp_boot_cpus (the OF is + * trashed by then) so we have to put the processors into + * a holding pattern controlled by the kernel (not OF) before + * we destroy the OF. + * + * This used a chunk of high memory, puts some holding pattern + * code there and sends the other processors off to there until + * smp_boot_cpus tells them to do something. We do that by using + * physical address 0x0. The holding pattern checks that address + * until its cpu # is there, when it is that cpu jumps to + * __secondary_start(). smp_boot_cpus() takes care of setting those + * values. + * + * We also use physical address 0x4 here to tell when a cpu + * is in its holding pattern code. + * + * -- Cort + */ + { + extern void __secondary_hold(void); + unsigned long i; + char type[16]; + + + /* + * XXX: hack to make sure we're chrp, assume that if we're + * chrp we have a device_type property -- Cort + */ + node = call_prom(RELOC("finddevice"), 1, 1, RELOC("/")); + if ( (int)call_prom(RELOC("getprop"), 4, 1, node, + RELOC("device_type"),type, sizeof(type)) <= 0) + return; + + /* copy the holding pattern code to someplace safe (8M) */ + memcpy( (void *)(8<<20), RELOC(__secondary_hold), 0x10000 ); + for (i = 8<<20; i < ((8<<20)+0x10000); i += 32) + { + asm volatile("dcbf 0,%0" : : "r" (i) : "memory"); + asm volatile("icbi 0,%0" : : "r" (i) : "memory"); + } + } + + /* look for cpus */ + for (node = 0; prom_next_node(&node);) + { + type[0] = 0; + call_prom(RELOC("getprop"), 4, 1, node, RELOC("device_type"), + type, sizeof(type)); + if (strcmp(type, RELOC("cpu")) != 0) + continue; + path = (char *) mem; + memset(path, 0, 256); + if ((int) call_prom(RELOC("package-to-path"), 3, 1, + node, path, 255) < 0) + continue; + /* XXX: hack - don't start cpu 0, this cpu -- Cort */ + if ( cpu++ == 0 ) + continue; + prom_print(RELOC("starting cpu ")); + prom_print(path); + *(unsigned long *)(0x4) = 0; + asm volatile("dcbf 0,%0": : "r" (0x4) : "memory"); + call_prom(RELOC("start-cpu"), 3, 0, node, 8<<20, cpu-1); + for ( i = 0 ; (i < 10000) && + (*(ulong *)(0x4) == (ulong)0); i++ ) + ; + if (*(ulong *)(0x4) == (ulong)cpu-1 ) + prom_print(RELOC("...ok\n")); + else + prom_print(RELOC("...failed\n")); + } +#endif } /* @@ -631,6 +759,12 @@ mem_start = ifunc(np, mem_start); } + /* the f50 sets the name to 'display' and 'compatible' to what we + * expect for the name -- Cort + */ + if (!strcmp(np->name, "display")) + np->name = get_property(np, "compatible", 0); + if (!strcmp(np->name, "device-tree")) ifunc = interpret_root_props; else if (np->type == 0) @@ -1191,8 +1325,11 @@ prom_exit(); } -#define CALC_BASE(y) (bi->dispDeviceBase + bi->dispDeviceRect[0] * \ - (bi->dispDeviceDepth >> 3) + bi->dispDeviceRowBytes * (y)) +/* Calc the base address of a given point (x,y) */ +#define CALC_BASE(x,y) ((BOOT_INFO_IS_V2_COMPATIBLE(bi) ? bi->logicalDisplayBase : \ + bi->dispDeviceBase) + (bi->dispDeviceRect[0] + (x)) * \ + (bi->dispDeviceDepth >> 3) + bi->dispDeviceRowBytes * \ + ((y) + bi->dispDeviceRect[1])) __init static void @@ -1200,7 +1337,7 @@ { unsigned long offset = reloc_offset(); boot_infos_t* bi = PTRRELOC(RELOC(boot_infos)); - unsigned long *base = (unsigned long *)CALC_BASE(0); + unsigned long *base = (unsigned long *)CALC_BASE(0,0); unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) * (bi->dispDeviceDepth >> 3)) >> 2; int i,j; @@ -1214,6 +1351,33 @@ } } +__inline__ void dcbst(const void* addr) +{ + __asm__ __volatile__ ("dcbst 0,%0" :: "r" (addr)); +} + +__init +static void +flushscreen(void) +{ + unsigned long offset = reloc_offset(); + boot_infos_t* bi = PTRRELOC(RELOC(boot_infos)); + unsigned long *base = (unsigned long *)CALC_BASE(0,0); + unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) * + (bi->dispDeviceDepth >> 3)) >> 2; + int i,j; + + for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1]); i++) + { + unsigned long *ptr = base; + for(j=width; j>0; j-=8) { + dcbst(ptr); + ptr += 8; + } + base += (bi->dispDeviceRowBytes >> 2); + } +} + #ifdef CONFIG_BOOTX_TEXT __init @@ -1222,8 +1386,8 @@ { unsigned long offset = reloc_offset(); boot_infos_t* bi = PTRRELOC(RELOC(boot_infos)); - unsigned long *src = (unsigned long *)CALC_BASE(16); - unsigned long *dst = (unsigned long *)CALC_BASE(0); + unsigned long *src = (unsigned long *)CALC_BASE(0,16); + unsigned long *dst = (unsigned long *)CALC_BASE(0,0); unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) * (bi->dispDeviceDepth >> 3)) >> 2; int i,j; @@ -1252,20 +1416,17 @@ { unsigned long offset = reloc_offset(); - switch(c) - { - case '\r': RELOC(g_loc_X) = 0; break; - case '\n': RELOC(g_loc_X) = 0; RELOC(g_loc_Y)++; break; + switch(c) { + case '\r': RELOC(g_loc_X) = 0; break; + case '\n': RELOC(g_loc_X) = 0; RELOC(g_loc_Y)++; break; default: draw_byte(c, RELOC(g_loc_X)++, RELOC(g_loc_Y)); - if (RELOC(g_loc_X) >= RELOC(g_max_loc_X)) - { + if (RELOC(g_loc_X) >= RELOC(g_max_loc_X)) { RELOC(g_loc_X) = 0; RELOC(g_loc_Y)++; } } - while (RELOC(g_loc_Y) >= RELOC(g_max_loc_Y)) - { + while (RELOC(g_loc_Y) >= RELOC(g_max_loc_Y)) { scrollscreen(); RELOC(g_loc_Y)--; } @@ -1281,17 +1442,32 @@ __init static void +drawhex(unsigned long v) +{ + static char hex_table[] = "0123456789abcdef"; + unsigned long offset = reloc_offset(); + + drawchar(RELOC(hex_table)[(v >> 28) & 0x0000000FUL]); + drawchar(RELOC(hex_table)[(v >> 24) & 0x0000000FUL]); + drawchar(RELOC(hex_table)[(v >> 20) & 0x0000000FUL]); + drawchar(RELOC(hex_table)[(v >> 16) & 0x0000000FUL]); + drawchar(RELOC(hex_table)[(v >> 12) & 0x0000000FUL]); + drawchar(RELOC(hex_table)[(v >> 8) & 0x0000000FUL]); + drawchar(RELOC(hex_table)[(v >> 4) & 0x0000000FUL]); + drawchar(RELOC(hex_table)[(v >> 0) & 0x0000000FUL]); +} + + +__init +static void draw_byte(unsigned char c, long locX, long locY) { unsigned long offset = reloc_offset(); - boot_infos_t* bi = PTRRELOC(RELOC(boot_infos)); - unsigned char *base = bi->dispDeviceBase - + (bi->dispDeviceRowBytes * ((locY * 16) + bi->dispDeviceRect[1])) - + (bi->dispDeviceDepth >> 3) * ((locX * 8) + bi->dispDeviceRect[0]); - unsigned char *font = &RELOC(vga_font)[((unsigned long)c) * 16]; + boot_infos_t* bi = PTRRELOC(RELOC(boot_infos)); + unsigned char *base = CALC_BASE(locX << 3, locY << 4); + unsigned char *font = &RELOC(vga_font)[((unsigned long)c) * 16]; - switch(bi->dispDeviceDepth) - { + switch(bi->dispDeviceDepth) { case 32: draw_byte_32(font, (unsigned long *)base); break; diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/setup.c linux/arch/ppc/kernel/setup.c --- v2.2.3/linux/arch/ppc/kernel/setup.c Fri Jan 8 22:36:03 1999 +++ linux/arch/ppc/kernel/setup.c Fri Mar 19 10:50:03 1999 @@ -1,5 +1,5 @@ /* - * $Id: setup.c,v 1.122 1998/12/31 20:51:19 cort Exp $ + * $Id: setup.c,v 1.130 1999/03/11 01:45:15 cort Exp $ * Common prep/pmac/chrp boot and setup code. */ @@ -27,6 +27,7 @@ #include #ifdef CONFIG_MBX #include +#include #endif #include @@ -42,6 +43,7 @@ #endif /* END APUS defs */ +extern boot_infos_t *boot_infos; extern char cmd_line[512]; char saved_command_line[256]; unsigned char aux_device_present; @@ -55,12 +57,22 @@ #endif /* ! CONFIG_MACH_SPECIFIC */ /* copy of the residual data */ +#ifndef CONFIG_MBX unsigned char __res[sizeof(RESIDUAL)] __prepdata = {0,}; +#else +unsigned char __res[sizeof(bd_t)] = {0,}; +#endif + RESIDUAL *res = (RESIDUAL *)&__res; int _prep_type; - -extern boot_infos_t *boot_infos; +/* + * This is used to identify the board type from a given PReP board + * vendor. Board revision is also made available. + */ +unsigned char ucSystemType; +unsigned char ucBoardRev; +unsigned char ucBoardRevMaj, ucBoardRevMin; /* * Perhaps we can put the pmac screen_info[] here @@ -114,12 +126,8 @@ void machine_restart(char *cmd) { #ifndef CONFIG_MBX - struct adb_request req; unsigned long flags; - unsigned long i = 10000; -#if 0 - int err; -#endif + struct adb_request req; switch(_machine) { @@ -142,31 +150,32 @@ #if 0 /* RTAS doesn't seem to work on Longtrail. For now, do it the same way as the PReP. */ /*err = call_rtas("system-reboot", 0, 1, NULL); - printk("RTAS system-reboot returned %d\n", err); - for (;;);*/ + printk("RTAS system-reboot returned %d\n", err); + for (;;);*/ { extern unsigned int rtas_entry, rtas_data, rtas_size; unsigned long status, value; - printk("rtas_entry: %08x rtas_data: %08x rtas_size: %08x\n", + printk("rtas_ent`ry: %08x rtas_data: %08x rtas_size: %08x\n", rtas_entry,rtas_data,rtas_size); - } + } #endif case _MACH_prep: _disable_interrupts(); - - /* set exception prefix high - to the prom */ - save_flags( flags ); - restore_flags( flags|MSR_IP ); - - /* make sure bit 0 (reset) is a 0 */ - outb( inb(0x92) & ~1L , 0x92 ); - /* signal a reset to system control port A - soft reset */ - outb( inb(0x92) | 1 , 0x92 ); - - while ( i != 0 ) i++; - panic("restart failed\n"); - break; + /* set exception prefix high - to the prom */ + save_flags( flags ); + restore_flags( flags|MSR_IP ); + + /* make sure bit 0 (reset) is a 0 */ + outb( inb(0x92) & ~1L , 0x92 ); + /* signal a reset to system control port A - soft reset */ + outb( inb(0x92) | 1 , 0x92 ); + + while ( 1 ) ; + break; + /* + * Not reached + */ case _MACH_apus: cli(); @@ -182,8 +191,16 @@ break; } #else /* CONFIG_MBX */ - extern void MBX_gorom(void); - MBX_gorom(); + extern void __clear_msr_me(void); + __volatile__ unsigned char dummy; + + cli(); + ((immap_t *)IMAP_ADDR)->im_clkrst.car_plprcr |= 0x00000080; + __clear_msr_me(); + dummy = ((immap_t *)IMAP_ADDR)->im_clkrst.res[0]; + + printk("Restart failed\n"); + while(1); #endif /* CONFIG_MBX */ } @@ -226,7 +243,7 @@ } for (;;); #else /* CONFIG_MBX */ - machine_restart(NULL); + machine_halt(); #endif /* CONFIG_MBX */ } @@ -238,7 +255,6 @@ } else /* prep, chrp or apus */ machine_restart(NULL); - } #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) @@ -432,14 +448,14 @@ { len += sprintf(buffer+len,"zero pages\t: total %lu (%luKb) " "current: %lu (%luKb) hits: %lu/%lu (%lu%%)\n", - quicklists.zerototal, - (quicklists.zerototal*PAGE_SIZE)>>10, - quicklists.zero_sz, - (quicklists.zero_sz*PAGE_SIZE)>>10, - quicklists.zeropage_hits,quicklists.zeropage_calls, + zero_cache_total, + (zero_cache_total*PAGE_SIZE)>>10, + zero_cache_sz, + (zero_cache_sz*PAGE_SIZE)>>10, + zero_cache_hits,zero_cache_calls, /* : 1 below is so we don't div by zero */ - (quicklists.zeropage_hits*100) / - ((quicklists.zeropage_calls)?quicklists.zeropage_calls:1)); + (zero_cache_hits*100) / + ((zero_cache_calls)?zero_cache_calls:1)); } #ifndef CONFIG_MBX @@ -590,7 +606,6 @@ if ( r3 ) memcpy((void *)res,(void *)(r3+KERNELBASE), sizeof(RESIDUAL)); - setup_pci_ptrs(); isa_io_base = PREP_ISA_IO_BASE; isa_mem_base = PREP_ISA_MEM_BASE; pci_dram_offset = PREP_PCI_DRAM_OFFSET; @@ -604,11 +619,21 @@ { if ( !strncmp(res->VitalProductData.PrintableModel,"IBM",3) ) _prep_type = _PREP_IBM; + else if (!strncmp(res->VitalProductData.PrintableModel, + "Radstone",8)) + { + extern char *Motherboard_map_name; + + _prep_type = _PREP_Radstone; + Motherboard_map_name= + res->VitalProductData.PrintableModel; + } else _prep_type = _PREP_Motorola; } else /* assume motorola if no residual (netboot?) */ _prep_type = _PREP_Motorola; + setup_pci_ptrs(); #ifdef CONFIG_BLK_DEV_INITRD /* take care of initrd if we have one */ if ( r4 ) @@ -634,9 +659,7 @@ initrd_end = r3 + r4 + KERNELBASE; } #endif /* CONFIG_BLK_DEV_INITRD */ - /* isa_io_base set by setup_pci_ptrs() */ - isa_mem_base = CHRP_ISA_MEM_BASE; - pci_dram_offset = CHRP_PCI_DRAM_OFFSET; + /* pci_dram_offset/isa_io_base/isa_mem_base set by setup_pci_ptrs() */ #if !defined(CONFIG_MACH_SPECIFIC) ISA_DMA_THRESHOLD = ~0L; DMA_MODE_READ = 0x44; @@ -737,7 +760,7 @@ if (strstr(cmd_line, "xmon")) xmon(0); #endif /* CONFIG_XMON */ - + /* reboot on panic */ panic_timeout = 180; diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/signal.c linux/arch/ppc/kernel/signal.c --- v2.2.3/linux/arch/ppc/kernel/signal.c Thu Nov 19 09:56:27 1998 +++ linux/arch/ppc/kernel/signal.c Fri Mar 19 10:50:03 1999 @@ -1,7 +1,7 @@ /* * linux/arch/ppc/kernel/signal.c * - * $Id: signal.c,v 1.21 1998/10/22 19:37:49 paulus Exp $ + * $Id: signal.c,v 1.23 1999/03/01 16:51:53 cort Exp $ * * PowerPC version * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) @@ -374,7 +374,7 @@ if (!oldset) oldset = ¤t->blocked; - newsp = frame = regs->gpr[1] - sizeof(struct sigregs); + newsp = frame = 0; for (;;) { unsigned long signr; @@ -470,6 +470,13 @@ /* NOTREACHED */ } } + + if ( (ka->sa.sa_flags & SA_ONSTACK) + && (! on_sig_stack(regs->gpr[1]))) + newsp = (current->sas_ss_sp + current->sas_ss_size); + else + newsp = regs->gpr[1]; + newsp = frame = newsp - sizeof(struct sigregs); /* Whee! Actually deliver the signal. */ handle_signal(signr, ka, &info, oldset, regs, &newsp, frame); diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/smp.c linux/arch/ppc/kernel/smp.c --- v2.2.3/linux/arch/ppc/kernel/smp.c Thu Dec 31 10:28:59 1998 +++ linux/arch/ppc/kernel/smp.c Fri Mar 19 10:50:03 1999 @@ -1,5 +1,5 @@ /* - * $Id: smp.c,v 1.39 1998/12/28 10:28:51 paulus Exp $ + * $Id: smp.c,v 1.48 1999/03/16 10:40:32 cort Exp $ * * Smp support for ppc. * @@ -18,6 +18,7 @@ #define __KERNEL_SYSCALLS__ #include #include +#include #include #include @@ -29,9 +30,10 @@ #include #include #include +#include #include "time.h" - +int first_cpu_booted = 0; int smp_threads_ready = 0; volatile int smp_commenced = 0; int smp_num_cpus = 1; @@ -42,7 +44,6 @@ spinlock_t kernel_flag = SPIN_LOCK_UNLOCKED; unsigned int prof_multiplier[NR_CPUS]; unsigned int prof_counter[NR_CPUS]; -int first_cpu_booted = 0; cycles_t cacheflush_time; /* all cpu mappings are 1-1 -- Cort */ @@ -51,6 +52,10 @@ int start_secondary(void *); extern int cpu_idle(void *unused); +u_int openpic_read(volatile u_int *addr); + +/* register for interrupting the secondary processor on the powersurge */ +#define PSURGE_INTR ((volatile unsigned *)0xf80000c0) void smp_local_timer_interrupt(struct pt_regs * regs) { @@ -99,29 +104,33 @@ /* * Dirty hack to get smp message passing working. - * Right now it only works for stop cpu's but will be setup - * later for more general message passing. * * As it is now, if we're sending two message at the same time - * we have race conditions. I avoided doing locks here since - * all that works right now is the stop cpu message. + * we have race conditions. The PowerSurge doesn't easily + * allow us to send IPI messages so we put the messages in + * smp_message[]. * + * This is because don't have several IPI's on the PowerSurge even though + * we do on the chrp. It would be nice to use the actual IPI's on the chrp + * rather than this but having two methods of doing IPI isn't a good idea + * right now. * -- Cort */ int smp_message[NR_CPUS]; void smp_message_recv(void) { int msg = smp_message[smp_processor_id()]; - - /* clear interrupt */ - *(volatile unsigned long *)(0xf80000c0) = ~0L; - eieio(); + + if ( _machine == _MACH_Pmac ) + { + /* clear interrupt */ + out_be32(PSURGE_INTR, ~0); + } /* make sure msg is for us */ if ( msg == -1 ) return; ipi_count++; - /*printk("SMP %d: smp_message_recv() msg %x\n", smp_processor_id(),msg);*/ switch( msg ) { @@ -158,12 +167,17 @@ spinlock_t mesg_pass_lock = SPIN_LOCK_UNLOCKED; void smp_message_pass(int target, int msg, unsigned long data, int wait) { - if ( _machine != _MACH_Pmac ) + int i; + if ( !(_machine & (_MACH_Pmac|_MACH_chrp)) ) return; -printk("SMP %d: sending smp message %x\n", current->processor, msg); -if (smp_processor_id() ) printk("pass from cpu 1\n"); + spin_lock(&mesg_pass_lock); -#define OTHER (~smp_processor_id() & 1) + + /* + * We assume here that the msg is not -1. If it is, + * the recipient won't know the message was destined + * for it. -- Cort + */ switch( target ) { @@ -171,105 +185,179 @@ smp_message[smp_processor_id()] = msg; /* fall through */ case MSG_ALL_BUT_SELF: - smp_message[OTHER] = msg; + for ( i = 0 ; i < smp_num_cpus ; i++ ) + if ( i != smp_processor_id () ) + smp_message[i] = msg; break; default: smp_message[target] = msg; break; } - /* interrupt secondary processor */ - *(volatile unsigned long *)(0xf80000c0) = ~0L; - eieio(); - *(volatile unsigned long *)(0xf80000c0) = 0L; - eieio(); - /* interrupt primary */ - /**(volatile unsigned long *)(0xf3019000);*/ - spin_unlock(&mesg_pass_lock); + + if ( _machine == _MACH_Pmac ) + { + /* interrupt secondary processor */ + out_be32(PSURGE_INTR, ~0); + out_be32(PSURGE_INTR, 0); + /* + * Assume for now that the secondary doesn't send + * IPI's -- Cort + */ + /* interrupt primary */ + /**(volatile unsigned long *)(0xf3019000);*/ + } + + if ( _machine == _MACH_chrp ) + { + /* + * There has to be some way of doing this better - + * perhaps a sent-to-all or send-to-all-but-self + * in the openpic. This gets us going for now, though. + * -- Cort + */ + switch ( target ) + { + case MSG_ALL: + for ( i = 0 ; i < smp_num_cpus ; i++ ) + openpic_cause_IPI(i, 0, 0xffffffff ); + break; + case MSG_ALL_BUT_SELF: + for ( i = 0 ; i < smp_num_cpus ; i++ ) + if ( i != smp_processor_id () ) + openpic_cause_IPI(i, 0, + 0xffffffff & ~(1 << smp_processor_id())); + break; + default: + openpic_cause_IPI(target, 0, 1U << target); + break; + } + } + + spin_unlock(&mesg_pass_lock); } void __init smp_boot_cpus(void) { extern struct task_struct *current_set[NR_CPUS]; - extern void __secondary_start(void); + extern void __secondary_start_psurge(void); int i; struct task_struct *p; unsigned long a; printk("Entering SMP Mode...\n"); - + /* let other processors know to not do certain initialization */ first_cpu_booted = 1; - /*dcbf(&first_cpu_booted);*/ + + /* + * assume for now that the first cpu booted is + * cpu 0, the master -- Cort + */ + cpu_callin_map[0] = 1; + smp_store_cpu_info(0); + active_kernel_processor = 0; + current->processor = 0; for (i = 0; i < NR_CPUS; i++) { prof_counter[i] = 1; prof_multiplier[i] = 1; } - cpu_callin_map[0] = 1; - smp_store_cpu_info(0); - active_kernel_processor = 0; - current->processor = 0; - /* * XXX very rough, assumes 20 bus cycles to read a cache line, * timebase increments every 4 bus cycles, 32kB L1 data cache. */ cacheflush_time = 5 * 1024; - if ( _machine != _MACH_Pmac ) + if ( !(_machine & (_MACH_Pmac|_MACH_chrp)) ) { printk("SMP not supported on this machine.\n"); return; } - /* create a process for second processor */ - kernel_thread(start_secondary, NULL, CLONE_PID); - p = task[1]; - if ( !p ) - panic("No idle task for secondary processor\n"); - p->processor = 1; - p->has_cpu = 1; - current_set[1] = p; - - /* need to flush here since secondary bat's aren't setup */ - /* XXX ??? */ - for (a = KERNELBASE; a < KERNELBASE + 0x800000; a += 32) - asm volatile("dcbf 0,%0" : : "r" (a) : "memory"); - asm volatile("sync"); - - /*dcbf((void *)¤t_set[1]);*/ - /* setup entry point of secondary processor */ - *(volatile unsigned long *)(0xf2800000) = - (unsigned long)__secondary_start-KERNELBASE; - eieio(); - /* interrupt secondary to begin executing code */ - *(volatile unsigned long *)(0xf80000c0) = ~0L; - eieio(); - *(volatile unsigned long *)(0xf80000c0) = 0L; - eieio(); + switch ( _machine ) + { + case _MACH_Pmac: + /* assume powersurge board - 2 processors -- Cort */ + smp_num_cpus = 2; + break; + case _MACH_chrp: + smp_num_cpus = ((openpic_read(&OpenPIC->Global.Feature_Reporting0) + & OPENPIC_FEATURE_LAST_PROCESSOR_MASK) >> + OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT)+1; + /* get our processor # - we may not be cpu 0 */ + printk("SMP %d processors, boot CPU is %d (should be 0)\n", + smp_num_cpus, + 10/*openpic_read(&OpenPIC->Processor[0]._Who_Am_I)*/); + break; + } + /* - * wait to see if the secondary made a callin (is actually up). - * udelay() isn't accurate here since we haven't yet called - * calibrate_delay() so use this value that I found through - * experimentation. -- Cort + * only check for cpus we know exist. We keep the callin map + * with cpus at the bottom -- Cort */ - for ( i = 1000; i && !cpu_callin_map[1] ; i-- ) - udelay(100); + for ( i = 1 ; i < smp_num_cpus; i++ ) + { + int c; + + /* create a process for the processor */ + kernel_thread(start_secondary, NULL, CLONE_PID); + p = task[i]; + if ( !p ) + panic("No idle task for secondary processor\n"); + p->processor = i; + p->has_cpu = 1; + current_set[i] = p; + + /* need to flush here since secondary bats aren't setup */ + for (a = KERNELBASE; a < KERNELBASE + 0x800000; a += 32) + asm volatile("dcbf 0,%0" : : "r" (a) : "memory"); + asm volatile("sync"); + + /* wake up cpus */ + switch ( _machine ) + { + case _MACH_Pmac: + /* setup entry point of secondary processor */ + *(volatile unsigned long *)(0xf2800000) = + (unsigned long)__secondary_start_psurge-KERNELBASE; + eieio(); + /* interrupt secondary to begin executing code */ + out_be32(PSURGE_INTR, ~0); + out_be32(PSURGE_INTR, 0); + break; + case _MACH_chrp: + *(unsigned long *)KERNELBASE = i; + asm volatile("dcbf 0,%0"::"r"(KERNELBASE):"memory"); + break; + } + + /* + * wait to see if the cpu made a callin (is actually up). + * use this value that I found through experimentation. + * -- Cort + */ + for ( c = 1000; c && !cpu_callin_map[i] ; c-- ) + udelay(100); + + if ( cpu_callin_map[i] ) + { + printk("Processor %d found.\n", i); + /* this sync's the decr's -- Cort */ + if ( _machine == _MACH_Pmac ) + set_dec(decrementer_count); + } else { + printk("Processor %d is stuck.\n", i); + } + } - if(cpu_callin_map[1]) { - printk("Processor %d found.\n", smp_num_cpus); - smp_num_cpus++; -#if 1 /* this sync's the decr's, but we don't want this now -- Cort */ - set_dec(decrementer_count); -#endif - } else { - printk("Processor %d is stuck. \n", smp_num_cpus); + if ( _machine == _MACH_Pmac ) + { + /* reset the entry point so if we get another intr we won't + * try to startup again */ + *(volatile unsigned long *)(0xf2800000) = 0x100; + /* send interrupt to other processors to start decr's on all cpus */ + smp_message_pass(1,0xf0f0, 0, 0); } - /* reset the entry point so if we get another intr we won't - * try to startup again */ - *(volatile unsigned long *)(0xf2800000) = 0x100; - /* send interrupt to other processors to start decr's on all cpus */ - smp_message_pass(1,0xf0f0, 0, 0); } void __init smp_commence(void) @@ -308,7 +396,6 @@ while(!smp_commenced) barrier(); __sti(); - printk("SMP %d: smp_callin done\n", current->processor); } void __init smp_setup(char *str, int *ints) diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/softemu8xx.c linux/arch/ppc/kernel/softemu8xx.c --- v2.2.3/linux/arch/ppc/kernel/softemu8xx.c Fri May 8 23:14:45 1998 +++ linux/arch/ppc/kernel/softemu8xx.c Wed Mar 10 21:30:32 1999 @@ -38,6 +38,7 @@ #define LFDU 51 #define STFD 54 #define STFDU 55 +#define FMR 63 /* * We return 0 on success, 1 on unimplemented instruction, and EFAULT @@ -49,6 +50,7 @@ uint inst, instword; uint flreg, idxreg, disp; uint retval; + signed short sdisp; uint *ea, *ip; retval = 0; @@ -66,6 +68,11 @@ switch ( inst ) { case LFD: + /* this is a 16 bit quantity that is sign extended + * so use a signed short here -- Cort + */ + sdisp = (instword & 0xffff); + ea = (uint *)(regs->gpr[idxreg] + sdisp); if (copy_from_user(ip, ea, sizeof(double))) retval = EFAULT; break; @@ -77,6 +84,11 @@ regs->gpr[idxreg] = (uint)ea; break; case STFD: + /* this is a 16 bit quantity that is sign extended + * so use a signed short here -- Cort + */ + sdisp = (instword & 0xffff); + ea = (uint *)(regs->gpr[idxreg] + sdisp); if (copy_to_user(ea, ip, sizeof(double))) retval = EFAULT; break; @@ -87,6 +99,11 @@ else regs->gpr[idxreg] = (uint)ea; break; + case FMR: + /* assume this is a fp move -- Cort */ + memcpy( ip, ¤t->tss.fpr[(instword>>11)&0x1f], + sizeof(double) ); + break; default: retval = 1; printk("Bad emulation %s/%d\n" @@ -98,7 +115,7 @@ (instword>>16)&0x1f, (instword>>11)&0x1f, (instword>>6)&0x1f, - (instword>>1)&0x1f, + (instword>>1)&0x3ff, instword&1); { int pa; diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/time.c linux/arch/ppc/kernel/time.c --- v2.2.3/linux/arch/ppc/kernel/time.c Wed Jan 20 23:14:04 1999 +++ linux/arch/ppc/kernel/time.c Fri Mar 19 10:50:03 1999 @@ -1,5 +1,5 @@ /* - * $Id: time.c,v 1.39 1998/12/28 10:28:51 paulus Exp $ + * $Id: time.c,v 1.45 1999/03/03 15:09:59 cort Exp $ * Common time routines among all ppc machines. * * Written by Cort Dougan (cort@cs.nmt.edu) to merge @@ -18,8 +18,8 @@ * Since it is not possible to get a nice 100 Hz clock out of this, without * creating a software PLL, I have set HZ to 128. -- Dan * - * 1997-09-10 Updated NTP code according to technical memorandum Jan '96 - * "A Kernel Model for Precision Timekeeping" by Dave Mills + * 1997-09-10 Updated NTP code according to technical memorandum Jan '96 + * "A Kernel Model for Precision Timekeeping" by Dave Mills */ #include @@ -76,9 +76,6 @@ { int dval, d; unsigned long cpu = smp_processor_id(); - /* save the HID0 in case dcache was off - see idle.c - * this hack should leave for a better solution -- Cort */ - unsigned dcache_locked = unlock_dcache(); hardirq_enter(cpu); #ifdef __SMP__ @@ -136,9 +133,6 @@ } #endif hardirq_exit(cpu); - /* restore the HID0 in case dcache was off - see idle.c - * this hack should leave for a better solution -- Cort */ - lock_dcache(dcache_locked); } #ifdef CONFIG_MBX @@ -198,9 +192,9 @@ xtime.tv_sec = tv->tv_sec; xtime.tv_usec = tv->tv_usec - frac_tick; set_dec(frac_tick * count_period_den / count_period_num); - time_adjust = 0; /* stop active adjtime() */ + time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; - time_state = TIME_ERROR; /* p. 24, (a) */ + time_state = TIME_ERROR; /* p. 24, (a) */ time_maxerror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT; restore_flags(flags); @@ -303,6 +297,7 @@ __initfunc(void prep_calibrate_decr(void)) { unsigned long flags; + unsigned long freq, divisor; /* the Powerstack II's have trouble with the timer so * we use a default value -- Cort @@ -310,7 +305,6 @@ if ( (_prep_type == _PREP_Motorola) && ((inb(0x800) & 0xF0) & 0x40) ) { - unsigned long freq, divisor; static unsigned long t2 = 0; t2 = 998700000/60; @@ -323,7 +317,16 @@ count_period_den = freq / 1000000; return; } - + if ( _prep_type == _PREP_Radstone ) + { + freq = res->VitalProductData.ProcessorBusHz; + divisor = 4; + printk("time_init: decrementer frequency = %d/%d\n", freq, divisor); + decrementer_count = freq / HZ / divisor; + count_period_num = divisor; + count_period_den = freq / 1000000; + return; + } save_flags(flags); @@ -439,6 +442,49 @@ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; +/* + * This only works for the Gregorian calendar - i.e. after 1752 (in the UK) + */ +void GregorianDay(struct rtc_time * tm) +{ + int leapsToDate; + int lastYear; + int day; + int MonthOffset[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; + + lastYear=tm->tm_year-1; + + /* + * Number of leap corrections to apply up to end of last year + */ + leapsToDate = lastYear/4 - lastYear/100 + lastYear/400; + + /* + * This year is a leap year if it is divisible by 4 except when it is + * divisible by 100 unless it is divisible by 400 + * + * e.g. 1904 was a leap year, 1900 was not, 1996 is, and 2000 will be + */ + if((tm->tm_year%4==0) && + ((tm->tm_year%100!=0) || (tm->tm_year%400==0)) && + (tm->tm_mon>2)) + { + /* + * We are past Feb. 29 in a leap year + */ + day=1; + } + else + { + day=0; + } + + day += lastYear*365 + leapsToDate + MonthOffset[tm->tm_mon-1] + + tm->tm_mday; + + tm->tm_wday=day%7; +} + void to_tm(int tim, struct rtc_time * tm) { register int i; @@ -467,6 +513,11 @@ /* Days are what is left over (+1) from all that. */ tm->tm_mday = day + 1; + + /* + * Determine the day of week + */ + GregorianDay(tm); } diff -u --recursive --new-file v2.2.3/linux/arch/ppc/lib/locks.c linux/arch/ppc/lib/locks.c --- v2.2.3/linux/arch/ppc/lib/locks.c Thu Dec 31 10:28:59 1998 +++ linux/arch/ppc/lib/locks.c Wed Mar 10 21:30:32 1999 @@ -1,5 +1,5 @@ /* - * $Id: locks.c,v 1.21 1998/12/28 10:28:53 paulus Exp $ + * $Id: locks.c,v 1.23 1999/02/12 07:06:32 cort Exp $ * * Locks for smp ppc * @@ -26,24 +26,18 @@ #ifdef DEBUG_LOCKS unsigned int stuck = INIT_STUCK; #endif /* DEBUG_LOCKS */ - /* try expensive atomic load/store to get lock */ - while((unsigned long )xchg_u32((void *)&lock->lock,0xffffffff)) { - /* try cheap load until it's free */ - while(lock->lock) { + while (__spin_trylock(&lock->lock)) { #ifdef DEBUG_LOCKS - if(!--stuck) - { - printk("_spin_lock(%p) CPU#%d NIP %p" - " holder: cpu %ld pc %08lX\n", - lock, cpu, __builtin_return_address(0), - lock->owner_cpu,lock->owner_pc); - stuck = INIT_STUCK; - /* steal the lock */ - /*xchg_u32((void *)&lock->lock,0);*/ - } -#endif /* DEBUG_LOCKS */ - barrier(); + if(!--stuck) { + printk("_spin_lock(%p) CPU#%d NIP %p" + " holder: cpu %ld pc %08lX\n", + lock, cpu, __builtin_return_address(0), + lock->owner_cpu,lock->owner_pc); + stuck = INIT_STUCK; + /* steal the lock */ + /*xchg_u32((void *)&lock->lock,0);*/ } +#endif /* DEBUG_LOCKS */ } lock->owner_pc = (unsigned long)__builtin_return_address(0); lock->owner_cpu = cpu; @@ -51,15 +45,11 @@ int spin_trylock(spinlock_t *lock) { - unsigned long result; - - result = (unsigned long )xchg_u32((void *)&lock->lock,0xffffffff); - if ( !result ) - { - lock->owner_cpu = smp_processor_id(); - lock->owner_pc = (unsigned long)__builtin_return_address(0); - } - return (result == 0); + if (__spin_trylock(&lock->lock)) + return 0; + lock->owner_cpu = smp_processor_id(); + lock->owner_pc = (unsigned long)__builtin_return_address(0); + return 1; } @@ -76,11 +66,11 @@ lp->owner_pc,lp->lock); #endif /* DEBUG_LOCKS */ lp->owner_pc = lp->owner_cpu = 0; - eieio(); /* actually I believe eieio only orders */ - lp->lock = 0; /* non-cacheable accesses (on 604 at least) */ - eieio(); /* - paulus. */ + wmb(); + lp->lock = 0; + wmb(); } - + /* * Just like x86, implement read-write locks as a 32-bit counter * with the high bit (sign) being the "write" bit. @@ -95,6 +85,7 @@ again: /* get our read lock in there */ + wmb(); atomic_inc((atomic_t *) &(rw)->lock); if ( (signed long)((rw)->lock) < 0) /* someone has a write lock */ { @@ -114,6 +105,7 @@ /* try to get the read lock again */ goto again; } + wmb(); } void _read_unlock(rwlock_t *rw) @@ -124,7 +116,9 @@ current->comm,current->pid,current->tss.regs->nip, rw->lock); #endif /* DEBUG_LOCKS */ + wmb(); atomic_dec((atomic_t *) &(rw)->lock); + wmb(); } void _write_lock(rwlock_t *rw) @@ -134,7 +128,8 @@ int cpu = smp_processor_id(); #endif /* DEBUG_LOCKS */ -again: +again: + wmb(); if ( test_and_set_bit(31,&(rw)->lock) ) /* someone has a write lock */ { while ( (rw)->lock & (1<<31) ) /* wait for write lock */ @@ -170,6 +165,7 @@ } goto again; } + wmb(); } void _write_unlock(rwlock_t *rw) @@ -180,7 +176,9 @@ current->comm,current->pid,current->tss.regs->nip, rw->lock); #endif /* DEBUG_LOCKS */ + wmb(); clear_bit(31,&(rw)->lock); + wmb(); } void __lock_kernel(struct task_struct *task) @@ -196,24 +194,18 @@ } #endif /* DEBUG_LOCKS */ - if ( atomic_inc_return((atomic_t *) &task->lock_depth) != 1 ) + if (atomic_inc_return((atomic_t *) &task->lock_depth) != 1) return; /* mine! */ - while ( xchg_u32( (void *)&klock_info.kernel_flag, KLOCK_HELD) ) - { - /* try cheap load until it's free */ - while(klock_info.kernel_flag) { + while (__spin_trylock(&klock_info.kernel_flag)) { #ifdef DEBUG_LOCKS - if(!--stuck) - { - printk("_lock_kernel() CPU#%d NIP %p\n", - smp_processor_id(), - __builtin_return_address(0)); - stuck = INIT_STUCK; - } -#endif /* DEBUG_LOCKS */ - barrier(); + if(!--stuck) { + printk("_lock_kernel() CPU#%d NIP %p\n", + smp_processor_id(), + __builtin_return_address(0)); + stuck = INIT_STUCK; } +#endif /* DEBUG_LOCKS */ } klock_info.akp = smp_processor_id(); @@ -223,7 +215,7 @@ void __unlock_kernel(struct task_struct *task) { #ifdef DEBUG_LOCKS - if ( (task->lock_depth == 0) || (klock_info.kernel_flag != KLOCK_HELD) ) + if ((task->lock_depth == 0) || (klock_info.kernel_flag != KLOCK_HELD)) { printk("__unlock_kernel(): %s/%d (nip %08lX) " "lock depth %x flags %lx\n", @@ -234,10 +226,13 @@ return; } #endif /* DEBUG_LOCKS */ - if ( atomic_dec_and_test((atomic_t *) &task->lock_depth) ) + if (atomic_dec_and_test((atomic_t *) &task->lock_depth)) { + wmb(); klock_info.akp = NO_PROC_ID; + wmb(); klock_info.kernel_flag = KLOCK_CLEAR; + wmb(); } } @@ -249,5 +244,5 @@ __lock_kernel(task); task->lock_depth = depth; __sti(); - } + } } diff -u --recursive --new-file v2.2.3/linux/arch/ppc/lib/string.S linux/arch/ppc/lib/string.S --- v2.2.3/linux/arch/ppc/lib/string.S Mon Oct 5 13:13:36 1998 +++ linux/arch/ppc/lib/string.S Wed Mar 10 21:30:32 1999 @@ -321,8 +321,6 @@ .long 75b,76b .text -#undef CLEAR_USE_DCBZ 1 -#undef CLEAR_NO_CACHE 1 .globl __clear_user __clear_user: addi r6,r3,-4 @@ -333,15 +331,6 @@ /* clear a single word */ 11: stwu r5,4(r6) beqlr -#if defined(CLEAR_NO_CACHE) && defined (CONFIG_6xx) - /* - * no reason to turn off the cache for a single word - * or a few bytes -- Cort - */ - mfspr r7,HID0 - ori r8,r7,HID0_DLOCK - mtspr HID0,r8 -#endif /* CLEAR_NO_CACHE */ /* clear word sized chunks */ andi. r0,r6,3 add r4,r0,r4 @@ -353,10 +342,6 @@ 1: stwu r5,4(r6) bdnz 1b 6: andi. r4,r4,3 -#if defined(CLEAR_NO_CACHE) && defined (CONFIG_6xx) - /* restore the original state of HID0 in case cache was off -- Cort */ - mtspr HID0,r7 -#endif /* CLEAR_NO_CACHE */ /* clear byte sized chunks */ 7: cmpwi 0,r4,0 beqlr @@ -371,9 +356,6 @@ .align 2 .long 11b,99b .long 1b,99b -#ifdef CLEAR_USE_DCBZ - /*.long 66b,99b*/ -#endif .long 8b,99b .text diff -u --recursive --new-file v2.2.3/linux/arch/ppc/mbx_defconfig linux/arch/ppc/mbx_defconfig --- v2.2.3/linux/arch/ppc/mbx_defconfig Wed Mar 10 15:29:45 1999 +++ linux/arch/ppc/mbx_defconfig Wed Mar 10 21:30:32 1999 @@ -14,8 +14,8 @@ # CONFIG_ALL_PPC is not set # CONFIG_APUS is not set CONFIG_MBX=y -CONFIG_SMP=n CONFIG_MACH_SPECIFIC=y +# CONFIG_SMP is not set CONFIG_SERIAL_CONSOLE=y # @@ -58,8 +58,26 @@ # Block devices # # CONFIG_BLK_DEV_FD is not set -# CONFIG_BLK_DEV_IDE is not set -# CONFIG_BLK_DEV_HD_ONLY is not set +CONFIG_BLK_DEV_IDE=y +# CONFIG_BLK_DEV_HD_IDE is not set +CONFIG_BLK_DEV_IDEDISK=y +# CONFIG_BLK_DEV_IDECD is not set +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_BLK_DEV_RZ1000 is not set +CONFIG_BLK_DEV_IDEPCI=y +CONFIG_BLK_DEV_IDEDMA=y +# CONFIG_BLK_DEV_OFFBOARD is not set +CONFIG_IDEDMA_AUTO=y +# CONFIG_BLK_DEV_OPTI621 is not set +# CONFIG_BLK_DEV_TRM290 is not set +# CONFIG_BLK_DEV_NS87415 is not set +# CONFIG_BLK_DEV_VIA82C586 is not set +# CONFIG_BLK_DEV_CMD646 is not set +CONFIG_BLK_DEV_SL82C105=y +# CONFIG_IDE_CHIPSETS is not set # CONFIG_BLK_DEV_LOOP is not set # CONFIG_BLK_DEV_NBD is not set # CONFIG_BLK_DEV_MD is not set @@ -90,7 +108,7 @@ # CONFIG_SYN_COOKIES is not set # CONFIG_INET_RARP is not set CONFIG_IP_NOSR=y -# CONFIG_SKB_LARGE is not set +CONFIG_SKB_LARGE=y # CONFIG_IPV6 is not set # CONFIG_IPX is not set # CONFIG_ATALK is not set @@ -102,7 +120,11 @@ # CONFIG_WAN_ROUTER is not set # CONFIG_NET_FASTROUTE is not set # CONFIG_NET_HW_FLOWCONTROL is not set -# CONFIG_CPU_IS_SLOW is not set +CONFIG_CPU_IS_SLOW=y + +# +# QoS and/or fair queueing +# # CONFIG_NET_SCHED is not set # @@ -126,6 +148,7 @@ # CONFIG_NET_VENDOR_RACAL is not set # CONFIG_RTL8139 is not set # CONFIG_YELLOWFIN is not set +# CONFIG_ACENIC is not set # CONFIG_NET_ISA is not set # CONFIG_NET_EISA is not set # CONFIG_NET_POCKET is not set @@ -138,6 +161,8 @@ # CONFIG_TR is not set # CONFIG_SHAPER is not set # CONFIG_HOSTESS_SV11 is not set +# CONFIG_COSA is not set +# CONFIG_RCPCI is not set # # Amateur Radio support @@ -161,7 +186,8 @@ # # Character devices # -# CONFIG_VT is not set +CONFIG_VT=y +# CONFIG_VT_CONSOLE is not set CONFIG_SERIAL=y CONFIG_SERIAL_CONSOLE=y # CONFIG_SERIAL_EXTENDED is not set @@ -170,9 +196,17 @@ # CONFIG_MOUSE is not set # CONFIG_QIC02_TAPE is not set # CONFIG_WATCHDOG is not set +# CONFIG_NVRAM is not set # CONFIG_RTC is not set + +# +# Video For Linux +# # CONFIG_VIDEO_DEV is not set -# CONFIG_NVRAM is not set + +# +# Joystick support +# # CONFIG_JOYSTICK is not set # @@ -184,35 +218,46 @@ # Filesystems # # CONFIG_QUOTA is not set -# CONFIG_MINIX_FS is not set -# CONFIG_EXT2_FS is not set -# CONFIG_ISO9660_FS is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set # CONFIG_FAT_FS is not set # CONFIG_MSDOS_FS is not set # CONFIG_UMSDOS_FS is not set # CONFIG_VFAT_FS is not set +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_MINIX_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_HPFS_FS is not set CONFIG_PROC_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set CONFIG_NFS_FS=y CONFIG_ROOT_NFS=y # CONFIG_NFSD is not set CONFIG_SUNRPC=y CONFIG_LOCKD=y -# CONFIG_CODA_FS is not set # CONFIG_SMB_FS is not set -# CONFIG_HPFS_FS is not set -# CONFIG_NTFS_FS is not set -# CONFIG_SYSV_FS is not set -# CONFIG_AFFS_FS is not set -# CONFIG_HFS_FS is not set -# CONFIG_ROMFS_FS is not set -# CONFIG_AUTOFS_FS is not set -# CONFIG_UFS_FS is not set +# CONFIG_NCP_FS is not set + +# +# Partition Types +# # CONFIG_BSD_DISKLABEL is not set +# CONFIG_MAC_PARTITION is not set # CONFIG_SMD_DISKLABEL is not set # CONFIG_SOLARIS_X86_PARTITION is not set -# CONFIG_ADFS_FS is not set -# CONFIG_QNX4FS_FS is not set -# CONFIG_MAC_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set # CONFIG_NLS is not set # diff -u --recursive --new-file v2.2.3/linux/arch/ppc/mbxboot/Makefile linux/arch/ppc/mbxboot/Makefile --- v2.2.3/linux/arch/ppc/mbxboot/Makefile Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/mbxboot/Makefile Fri Mar 19 10:50:03 1999 @@ -0,0 +1,101 @@ +# +# arch/ppc/boot/Makefile +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file "COPYING" in the main directory of this archive +# for more details. +# +# Copyright (C) 1994 by Linus Torvalds +# Adapted for PowerPC by Gary Thomas +# modified by Cort (cort@cs.nmt.edu) +# +.c.s: + $(CC) $(CFLAGS) -S -o $*.s $< +.s.o: + $(AS) -o $*.o $< +.c.o: + $(CC) $(CFLAGS) -DINITRD_OFFSET=$(IOFF) -DINITRD_SIZE=$(ISZ) -DZIMAGE_OFFSET=$(ZOFF) -DZIMAGE_SIZE=$(ZSZ) -DKERNELBASE=$(KERNELBASE) -c -o $*.o $< +.S.s: + $(CC) -D__ASSEMBLY__ -traditional -E -o $*.o $< +.S.o: + $(CC) -D__ASSEMBLY__ -traditional -c -o $*.o $< + +ZOFF = 0 +ZSZ = 0 +IOFF = 0 +ISZ = 0 + +TFTPIMAGE=/tftpboot/zImage.mbx +ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00100000 +GZIP_FLAGS = -v9 + +OBJECTS := head.o misc.o ../coffboot/zlib.o mbxtty.o +CFLAGS = -O2 -DSTDC_HEADERS -fno-builtin -I$(TOPDIR)/include -DCONFIG_MBX +OBJCOPY = $(CROSS_COMPILE)objcopy +OBJCOPY_ARGS = -O elf32-powerpc + +all: zImage + +zvmlinux.initrd: zvmlinux + $(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS) + $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ + --add-section=initrd=ramdisk.image.gz \ + --add-section=image=../coffboot/vmlinux.gz \ + zvmlinux.initrd.tmp zvmlinux.initrd + $(CC) $(CFLAGS) -DINITRD_OFFSET=`sh offset $(OBJDUMP) zvmlinux.initrd initrd` \ + -DINITRD_SIZE=`sh size $(OBJDUMP) zvmlinux.initrd initrd` \ + -DZIMAGE_OFFSET=`sh offset $(OBJDUMP) zvmlinux.initrd image` \ + -DZIMAGE_SIZE=`sh size $(OBJDUMP) zvmlinux.initrd image` \ + -DKERNELBASE=$(KERNELBASE) -c -o misc.o misc.c + $(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS) + $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ + --add-section=initrd=ramdisk.image.gz \ + --add-section=image=../coffboot/vmlinux.gz \ + zvmlinux.initrd.tmp $@ + rm zvmlinux.initrd.tmp + +zImage: zvmlinux + ln -sf zvmlinux zImage + +zImage.initrd: zvmlinux.initrd + ln -sf zvmlinux.initrd zImage.initrd + +zvmlinux: $(OBJECTS) ../coffboot/vmlinux.gz +# +# build the boot loader image and then compute the offset into it +# for the kernel image +# + $(LD) $(ZLINKFLAGS) -o zvmlinux.tmp $(OBJECTS) + $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment --add-section=image=../coffboot/vmlinux.gz \ + zvmlinux.tmp $@ +# +# then with the offset rebuild the bootloader so we know where the kernel is +# + $(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 \ + -DZIMAGE_OFFSET=`sh offset $(OBJDUMP) zvmlinux image` \ + -DZIMAGE_SIZE=`sh size $(OBJDUMP) zvmlinux image` -DKERNELBASE=$(KERNELBASE) \ + -c -o misc.o misc.c + $(LD) $(ZLINKFLAGS) -o zvmlinux.tmp $(OBJECTS) + $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment --add-section=image=../coffboot/vmlinux.gz \ + zvmlinux.tmp $@ + rm zvmlinux.tmp + +znetboot : zImage + cp zImage $(TFTPIMAGE) + +znetboot.initrd : zImage.initrd + cp zImage.initrd $(TFTPIMAGE) + +clean: + rm -f vmlinux* zvmlinux* zImage* + +fastdep: + $(TOPDIR)/scripts/mkdep *.[Sch] > .depend + +dep: + $(CPP) -M *.S *.c > .depend + +# just here to match coffboot/Makefile +vmlinux.coff: + +vmlinux.coff.initrd: diff -u --recursive --new-file v2.2.3/linux/arch/ppc/mbxboot/head.S linux/arch/ppc/mbxboot/head.S --- v2.2.3/linux/arch/ppc/mbxboot/head.S Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/mbxboot/head.S Fri Mar 19 10:50:03 1999 @@ -0,0 +1,201 @@ +#include +#include "../kernel/ppc_defs.h" +#include "../kernel/ppc_asm.tmpl" +#include +#include + + .text + +/* + * $Id: head.S,v 1.2 1999/02/17 06:29:41 cort Exp $ + * + * This code is loaded by the ROM loader at some arbitrary location. + * Move it to high memory so that it can load the kernel at 0x0000. + * + * The MBX EPPC-Bug understands ELF, so it loads us into the location + * specified in the header. This is a two step process. First, EPPC-Bug + * loads the file into the intermediate buffer memory location specified + * by the environment parameters. When it discovers this is an ELF + * binary, it relocates to the link address for us. Unfortunately, the + * header does not move with the file, so we have to find the + * intermediate load location and read the header from there. From + * information provided by Motorola (thank you), we know this intermediate + * location can be found from the NVRAM environment. + * All of these addresses must be somewhat carefully chosen to make sure + * we don't overlap the regions. I chose to load the kernel at 0, the + * compressed image loads at 0x00100000, and the MBX intermediate buffer + * was set to 0x00200000. Provided the loaded kernel image never grows + * over one megabyte (which I am going to ensure never happens :-), these + * will work fine. When we get called from EPPC-Bug, registers are: + * R1 - Stack pointer at a high memory address. + * R3 - Pointer to Board Information Block. + * R4 - Pointer to argument string. + * Interrupts masked, cache and MMU disabled. + */ + + .globl start +start: + bl start_ +start_: + mr r11,r3 /* Save pointer to residual/board data */ +/* Clear all of BSS */ + lis r3,edata@h + ori r3,r3,edata@l + lis r4,end@h + ori r4,r4,end@l + subi r3,r3,4 + subi r4,r4,4 + li r0,0 +50: stwu r0,4(r3) + cmp 0,r3,r4 + bne 50b +90: mr r9,r1 /* Save old stack pointer (in case it matters) */ + lis r1,.stack@h + ori r1,r1,.stack@l + addi r1,r1,4096*2 + subi r1,r1,256 + li r2,0x000F /* Mask pointer to 16-byte boundary */ + andc r1,r1,r2 +/* Run loader */ + mr r3, r11 + mr r21, r11 + bl serial_init /* Init MBX serial port */ + + lis r8, 0xfa200000@h /* Disable Ethernet SCC */ + li r0, 0 + stw r0, 0x0a00(r8) + + mr r11, r21 + lis r8,start@h + ori r8,r8,start@l + li r9,end@h + ori r9,r9,end@l + sub r7,r8,r9 + srwi r7,r7,2 +#define ILAP_ADDRESS 0xfa000020 + lis r8, ILAP_ADDRESS@h + lwz r8, ILAP_ADDRESS@l(r8) + addis r8, r8, 1 /* Add 64K */ + mr r3,r8 /* Load point */ + mr r4,r7 /* Program length */ + mr r5,r6 /* Checksum */ + mr r6,r11 /* Residual data */ + bl decompress_kernel + + /* changed to use r3 (as firmware does) for kernel + as ptr to residual -- Cort*/ + lis r6,cmd_line@h + ori r6,r6,cmd_line@l + lwz r6, 0(r6) + subi r7,r6,1 +00: lbzu r2,1(r7) + cmpi 0,r2,0 + bne 00b + + /* r4,r5 have initrd_start, size */ + lis r2,initrd_start@h + ori r2,r2,initrd_start@l + lwz r4,0(r2) + lis r2,initrd_end@h + ori r2,r2,initrd_end@l + lwz r5,0(r2) + + /* tell kernel we're prep */ + /* + * get start address of kernel code which is stored as a coff + * entry. see boot/head.S -- Cort + */ + li r9,0x0 + lwz r9,0(r9) + mtlr r9 + blr +hang: + b hang + +/* + * Delay for a number of microseconds + * -- Use the BUS timer (assumes 66MHz) + */ + .globl udelay +udelay: + mfspr r4,PVR + srwi r4,r4,16 + cmpi 0,r4,1 /* 601 ? */ + bne .udelay_not_601 +00: li r0,86 /* Instructions / microsecond? */ + mtctr r0 +10: addi r0,r0,0 /* NOP */ + bdnz 10b + subic. r3,r3,1 + bne 00b + blr + +.udelay_not_601: + mulli r4,r3,1000 /* nanoseconds */ + addi r4,r4,59 + li r5,60 + divw r4,r4,r5 /* BUS ticks */ +1: mftbu r5 + mftb r6 + mftbu r7 + cmp 0,r5,r7 + bne 1b /* Get [synced] base time */ + addc r9,r6,r4 /* Compute end time */ + addze r8,r5 +2: mftbu r5 + cmp 0,r5,r8 + blt 2b + bgt 3f + mftb r6 + cmp 0,r6,r9 + blt 2b +3: blr + +.globl _get_HID0 +_get_HID0: + mfspr r3,HID0 + blr + +.globl _put_HID0 +_put_HID0: + mtspr HID0,r3 + blr + +.globl _get_MSR +_get_MSR: + mfmsr r3 + blr + +.globl _put_MSR +_put_MSR: + mtmsr r3 + blr + +/* + * Flush instruction cache + * *** I'm really paranoid here! + */ +_GLOBAL(flush_instruction_cache) + mflr r5 + bl flush_data_cache + mtlr r5 + blr + +#define NUM_CACHE_LINES 128*8 +#define CACHE_LINE_SIZE 32 +#define cache_flush_buffer 0x1000 + +/* + * Flush data cache + * *** I'm really paranoid here! + */ +_GLOBAL(flush_data_cache) + lis r3,cache_flush_buffer@h + ori r3,r3,cache_flush_buffer@l + li r4,NUM_CACHE_LINES + mtctr r4 +00: lwz r4,0(r3) + addi r3,r3,CACHE_LINE_SIZE /* Next line, please */ + bdnz 00b +10: blr + .comm .stack,4096*2,4 diff -u --recursive --new-file v2.2.3/linux/arch/ppc/mbxboot/mbxtty.c linux/arch/ppc/mbxboot/mbxtty.c --- v2.2.3/linux/arch/ppc/mbxboot/mbxtty.c Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/mbxboot/mbxtty.c Fri Mar 19 10:50:03 1999 @@ -0,0 +1,201 @@ + + +/* Minimal serial functions needed to send messages out the serial + * port on the MBX console. + * + * The MBX uxes SMC1 for the serial port. We reset the port and use + * only the first BD that EPPC-Bug set up as a character FIFO. + * + * Later versions (at least 1.4, maybe earlier) of the MBX EPPC-Bug + * use COM1 instead of SMC1 as the console port. This kinda sucks + * for the rest of the kernel, so here we force the use of SMC1 again. + * I f**ked around for a day trying to figure out how to make EPPC-Bug + * use SMC1, but gave up and decided to fix it here. + */ +#include +#include +#ifdef CONFIG_MBX +#include +#endif +#ifdef CONFIG_FADS +#include +#endif +#include "../8xx_io/commproc.h" + +#ifdef CONFIG_MBX +#define MBX_CSR1 ((volatile u_char *)0xfa100000) +#define CSR1_COMEN (u_char)0x02 +#endif + +static cpm8xx_t *cpmp = (cpm8xx_t *)&(((immap_t *)IMAP_ADDR)->im_cpm); + +void +serial_init(bd_t *bd) +{ + volatile smc_t *sp; + volatile smc_uart_t *up; + volatile cbd_t *tbdf, *rbdf; + volatile cpm8xx_t *cp; + uint dpaddr, memaddr; + + cp = cpmp; + sp = (smc_t*)&(cp->cp_smc[0]); + up = (smc_uart_t *)&cp->cp_dparam[PROFF_SMC1]; + + /* Disable transmitter/receiver. + */ + sp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); + +#ifdef CONFIG_MBX + if (*MBX_CSR1 & CSR1_COMEN) { + /* COM1 is enabled. Initialize SMC1 and use it for + * the console port. + */ + + /* Enable SDMA. + */ + ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sdcr = 1; + + /* Use Port B for SMCs instead of other functions. + */ + cp->cp_pbpar |= 0x00000cc0; + cp->cp_pbdir &= ~0x00000cc0; + cp->cp_pbodr &= ~0x00000cc0; + + /* Allocate space for two buffer descriptors in the DP ram. + * For now, this address seems OK, but it may have to + * change with newer versions of the firmware. + */ + dpaddr = 0x0800; + + /* Grab a few bytes from the top of memory. EPPC-Bug isn't + * running any more, so we can do this. + */ + memaddr = (bd->bi_memsize - 32) & ~15; + + /* Set the physical address of the host memory buffers in + * the buffer descriptors. + */ + rbdf = (cbd_t *)&cp->cp_dpmem[dpaddr]; + rbdf->cbd_bufaddr = memaddr; + rbdf->cbd_sc = 0; + tbdf = rbdf + 1; + tbdf->cbd_bufaddr = memaddr+4; + tbdf->cbd_sc = 0; + + /* Set up the uart parameters in the parameter ram. + */ + up->smc_rbase = dpaddr; + up->smc_tbase = dpaddr+sizeof(cbd_t); + up->smc_rfcr = SMC_EB; + up->smc_tfcr = SMC_EB; + + /* Set UART mode, 8 bit, no parity, one stop. + * Enable receive and transmit. + */ + sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART; + + /* Mask all interrupts and remove anything pending. + */ + sp->smc_smcm = 0; + sp->smc_smce = 0xff; + + /* Set up the baud rate generator. + * See 8xx_io/commproc.c for details. + */ + cp->cp_simode = 0x10000000; + cp->cp_brgc1 = + ((((bd->bi_intfreq * 1000000)/16) / 9600) << 1) | CPM_BRG_EN; + + /* Enable SMC1 for console output. + */ + *MBX_CSR1 &= ~CSR1_COMEN; + } + else { +#endif + /* SMC1 is used as console port. + */ + tbdf = (cbd_t *)&cp->cp_dpmem[up->smc_tbase]; + rbdf = (cbd_t *)&cp->cp_dpmem[up->smc_rbase]; + + /* Issue a stop transmit, and wait for it. + */ + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC1, + CPM_CR_STOP_TX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); +#ifdef CONFIG_MBX + } +#endif + + /* Make the first buffer the only buffer. + */ + tbdf->cbd_sc |= BD_SC_WRAP; + rbdf->cbd_sc |= BD_SC_EMPTY | BD_SC_WRAP; + + /* Single character receive. + */ + up->smc_mrblr = 1; + up->smc_maxidl = 0; + + /* Initialize Tx/Rx parameters. + */ + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC1, CPM_CR_INIT_TRX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + + /* Enable transmitter/receiver. + */ + sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN; +} + +void +serial_putchar(const char c) +{ + volatile cbd_t *tbdf; + volatile char *buf; + volatile smc_uart_t *up; + + up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC1]; + tbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_tbase]; + + /* Wait for last character to go. + */ + buf = (char *)tbdf->cbd_bufaddr; + while (tbdf->cbd_sc & BD_SC_READY); + + *buf = c; + tbdf->cbd_datlen = 1; + tbdf->cbd_sc |= BD_SC_READY; +} + +char +serial_getc() +{ + volatile cbd_t *rbdf; + volatile char *buf; + volatile smc_uart_t *up; + char c; + + up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC1]; + rbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase]; + + /* Wait for character to show up. + */ + buf = (char *)rbdf->cbd_bufaddr; + while (rbdf->cbd_sc & BD_SC_EMPTY); + c = *buf; + rbdf->cbd_sc |= BD_SC_EMPTY; + + return(c); +} + +int +serial_tstc() +{ + volatile cbd_t *rbdf; + volatile smc_uart_t *up; + + up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC1]; + rbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase]; + + return(!(rbdf->cbd_sc & BD_SC_EMPTY)); +} diff -u --recursive --new-file v2.2.3/linux/arch/ppc/mbxboot/misc.c linux/arch/ppc/mbxboot/misc.c --- v2.2.3/linux/arch/ppc/mbxboot/misc.c Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/mbxboot/misc.c Fri Mar 19 10:50:03 1999 @@ -0,0 +1,637 @@ +/* + * misc.c + * + * $Id: misc.c,v 1.1 1999/02/17 05:00:06 cort Exp $ + * + * Adapted for PowerPC by Gary Thomas + * + * Rewritten by Cort Dougan (cort@cs.nmt.edu) + * One day to be replaced by a single bootloader for chrp/prep/pmac. -- Cort + */ + +#include +#include "../coffboot/zlib.h" +#include "asm/residual.h" +#include +#include +#include +#include +#include +#ifdef CONFIG_MBX +#include +#endif +#ifdef CONFIG_FADS +#include +#endif + +/* + * Please send me load/board info and such data for hardware not + * listed here so I can keep track since things are getting tricky + * with the different load addrs with different firmware. This will + * help to avoid breaking the load/boot process. + * -- Cort + */ +char *avail_ram; +char *end_avail; + +/* Because of the limited amount of memory on the MBX, it presents + * loading problems. The biggest is that we load this boot program + * into a relatively low memory address, and the Linux kernel Bss often + * extends into this space when it get loaded. When the kernel starts + * and zeros the BSS space, it also writes over the information we + * save here and pass to the kernel (command line and board info). + * On the MBX we grab some known memory holes to hold this information. + */ +#if defined(CONFIG_SERIAL_CONSOLE) +char cmd_preset[] = "console=ttyS0,9600n8"; +#else +char cmd_preset[] = ""; +#endif +char cmd_buf[256]; +char *cmd_line = cmd_buf; + +char *root_string = "root=/dev/nfs"; +char *nfsaddrs_string = "nfsaddrs="; +char *nfsroot_string = "nfsroot="; +char *defroot_string = "/sys/mbxroot"; +int do_ipaddrs(char **cmd_cp, int echo); +void do_nfsroot(char **cmd_cp, char *dp); +int strncmp(const char * cs,const char * ct,size_t count); +char *strrchr(const char * s, int c); + +RESIDUAL hold_resid_buf; +RESIDUAL *hold_residual = &hold_resid_buf; +unsigned long initrd_start = 0, initrd_end = 0; +char *zimage_start; +int zimage_size; + +char *vidmem = (char *)0xC00B8000; +int lines, cols; +int orig_x, orig_y; + +void puts(const char *); +void putc(const char c); +void puthex(unsigned long val); +void _bcopy(char *src, char *dst, int len); +void * memcpy(void * __dest, __const void * __src, + int __n); +void gunzip(void *, int, unsigned char *, int *); + +void pause() +{ + puts("pause\n"); +} + +void exit() +{ + puts("exit\n"); + while(1); +} + +/* The MBX is just the serial port. +*/ +tstc(void) +{ + return (serial_tstc()); +} + +getc(void) +{ + while (1) { + if (serial_tstc()) return (serial_getc()); + } +} + +void +putc(const char c) +{ + serial_putchar(c); +} + +void puts(const char *s) +{ + char c; + + while ( ( c = *s++ ) != '\0' ) { + serial_putchar(c); + if ( c == '\n' ) + serial_putchar('\r'); + } +} + + +void * memcpy(void * __dest, __const void * __src, + int __n) +{ + int i; + char *d = (char *)__dest, *s = (char *)__src; + + for (i=0;i<__n;i++) d[i] = s[i]; +} + +int memcmp(__const void * __dest, __const void * __src, + int __n) +{ + int i; + char *d = (char *)__dest, *s = (char *)__src; + + for (i=0;i<__n;i++, d++, s++) + { + if (*d != *s) + { + return (*s - *d); + } + } + return (0); +} + +void error(char *x) +{ + puts("\n\n"); + puts(x); + puts("\n\n -- System halted"); + + while(1); /* Halt */ +} + +void *zalloc(void *x, unsigned items, unsigned size) +{ + void *p = avail_ram; + + size *= items; + size = (size + 7) & -8; + avail_ram += size; + if (avail_ram > end_avail) { + puts("oops... out of memory\n"); + pause(); + } + return p; +} + +void zfree(void *x, void *addr, unsigned nb) +{ +} + +#define HEAD_CRC 2 +#define EXTRA_FIELD 4 +#define ORIG_NAME 8 +#define COMMENT 0x10 +#define RESERVED 0xe0 + +#define DEFLATED 8 + + +void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp) +{ + z_stream s; + int r, i, flags; + + /* skip header */ + i = 10; + flags = src[3]; + if (src[2] != DEFLATED || (flags & RESERVED) != 0) { + puts("bad gzipped data\n"); + exit(); + } + if ((flags & EXTRA_FIELD) != 0) + i = 12 + src[10] + (src[11] << 8); + if ((flags & ORIG_NAME) != 0) + while (src[i++] != 0) + ; + if ((flags & COMMENT) != 0) + while (src[i++] != 0) + ; + if ((flags & HEAD_CRC) != 0) + i += 2; + if (i >= *lenp) { + puts("gunzip: ran out of data in header\n"); + exit(); + } + + s.zalloc = zalloc; + s.zfree = zfree; + r = inflateInit2(&s, -MAX_WBITS); + if (r != Z_OK) { + puts("inflateInit2 returned %d\n"); + exit(); + } + s.next_in = src + i; + s.avail_in = *lenp - i; + s.next_out = dst; + s.avail_out = dstlen; + r = inflate(&s, Z_FINISH); + if (r != Z_OK && r != Z_STREAM_END) { + puts("inflate returned %d\n"); + exit(); + } + *lenp = s.next_out - (unsigned char *) dst; + inflateEnd(&s); +} + +unsigned char sanity[0x2000]; + +unsigned long +decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, RESIDUAL *residual) +{ + int timer; + extern unsigned long start; + char *cp, ch; + unsigned long i, motorola_id = 0; + char needs_reloc = 0; + BATU *u; + BATL *l; + char *dp; + + lines = 25; + cols = 80; + orig_x = 0; + orig_y = 24; + + /* Grab some space for the command line and board info. Since + * we no longer use the ELF header, but it was loaded, grab + * that space. + */ + cmd_line = (char *)(load_addr - 0x10000); + hold_residual = (RESIDUAL *)(cmd_line + sizeof(cmd_buf)); + /* copy board data */ + if (residual) + memcpy(hold_residual,residual,sizeof(bd_t)); + + /* MBX/prep sometimes put the residual/board info at the end of mem + * assume 16M for now -- Cort + * To boot on standard MBX boards with 4M, we can't use initrd, + * and we have to assume less memory. -- Dan + */ + if ( INITRD_OFFSET ) + end_avail = (char *)0x01000000; + else + end_avail = (char *)0x00400000; + + /* let residual data tell us it's higher */ + if ( (unsigned long)residual > 0x00800000 ) + end_avail = (char *)PAGE_ALIGN((unsigned long)residual); + + puts("loaded at: "); puthex(load_addr); + puts(" "); puthex((unsigned long)(load_addr + (4*num_words))); puts("\n"); + if ( (unsigned long)load_addr != (unsigned long)&start ) + { + puts("relocated to: "); puthex((unsigned long)&start); + puts(" "); + puthex((unsigned long)((unsigned long)&start + (4*num_words))); + puts("\n"); + } + + if ( residual ) + { + puts("board data at: "); puthex((unsigned long)residual); + puts(" "); + puthex((unsigned long)((unsigned long)residual + sizeof(bd_t))); + puts("\n"); + puts("relocated to: "); + puthex((unsigned long)hold_residual); + puts(" "); + puthex((unsigned long)((unsigned long)hold_residual + sizeof(bd_t))); + puts("\n"); + } + + /* we have to subtract 0x10000 here to correct for objdump including the + size of the elf header which we strip -- Cort */ + zimage_start = (char *)(load_addr - 0x10000 + ZIMAGE_OFFSET); + zimage_size = ZIMAGE_SIZE; + + if ( INITRD_OFFSET ) + initrd_start = load_addr - 0x10000 + INITRD_OFFSET; + else + initrd_start = 0; + initrd_end = INITRD_SIZE + initrd_start; + + /* + * setup avail_ram - this is the first part of ram usable + * by the uncompress code. -- Cort + */ + avail_ram = (char *)PAGE_ALIGN((unsigned long)zimage_start+zimage_size); + if ( ((load_addr+(num_words*4)) > (unsigned long) avail_ram) + && (load_addr <= 0x01000000) ) + avail_ram = (char *)(load_addr+(num_words*4)); + if ( (((unsigned long)&start+(num_words*4)) > (unsigned long) avail_ram) + && (load_addr <= 0x01000000) ) + avail_ram = (char *)((unsigned long)&start+(num_words*4)); + + /* relocate zimage */ + puts("zimage at: "); puthex((unsigned long)zimage_start); + puts(" "); puthex((unsigned long)(zimage_size+zimage_start)); puts("\n"); + /* + * don't relocate the zimage if it was loaded above 16M since + * things get weird if we try to relocate -- Cort + * We don't relocate zimage on a base MBX board because of + * insufficient memory. In this case we don't have initrd either, + * so use that as an indicator. -- Dan + */ + + /* Determine if we have a Motorola board */ + needs_reloc = 0; + if ( (( (unsigned long)zimage_start <= 0x01000000 ) && initrd_start) + || needs_reloc) + { + memcpy ((void *)PAGE_ALIGN(-PAGE_SIZE+(unsigned long)end_avail-zimage_size), + (void *)zimage_start, zimage_size ); + zimage_start = (char *)PAGE_ALIGN(-PAGE_SIZE+(unsigned long)end_avail-zimage_size); + end_avail = (char *)zimage_start; + puts("relocated to: "); puthex((unsigned long)zimage_start); + puts(" "); + puthex((unsigned long)zimage_size+(unsigned long)zimage_start); + puts("\n"); + } + + /* relocate initrd */ + if ( initrd_start ) + { + puts("initrd at: "); puthex(initrd_start); + puts(" "); puthex(initrd_end); puts("\n"); + /* + * Memory is really tight on the MBX (we can assume 4M) + * so put the initrd at the TOP of ram, and set end_avail + * to right after that. + * + * I should do something like this for prep, too and keep + * a variable end_of_DRAM to keep track of what we think the + * max ram is. + * -- Cort + */ + if (needs_reloc) + { + memcpy ((void *)PAGE_ALIGN(-PAGE_SIZE+ + (unsigned long)end_avail-INITRD_SIZE), + (void *)initrd_start, + INITRD_SIZE ); + initrd_start = PAGE_ALIGN(-PAGE_SIZE+ + (unsigned long)end_avail-INITRD_SIZE); + initrd_end = initrd_start + INITRD_SIZE; + end_avail = (char *)initrd_start; + puts("relocated to: "); puthex(initrd_start); + puts(" "); puthex(initrd_end); puts("\n"); + } + } + + puts("avail ram: "); puthex((unsigned long)avail_ram); puts(" "); + puthex((unsigned long)end_avail); puts("\n"); + + puts("\nLinux/PPC load: "); + timer = 0; + cp = cmd_line; + memcpy (cmd_line, cmd_preset, sizeof(cmd_preset)); + while ( *cp ) putc(*cp++); + while (timer++ < 5*1000) { + if (tstc()) { + while ((ch = getc()) != '\n' && ch != '\r') { + if (ch == '\b') { + if (cp != cmd_line) { + cp--; + puts("\b \b"); + } + } else if (ch == '?') { + if (!do_ipaddrs(&cp, 1)) { + *cp++ = ch; + putc(ch); + } + } else { + *cp++ = ch; + putc(ch); + } + } + break; /* Exit 'timer' loop */ + } + udelay(1000); /* 1 msec */ + } + *cp = 0; + /* The MBX does not currently have any default boot strategy. + * If the command line is not filled in, we will automatically + * create the default network boot. + */ + if (cmd_line[0] == 0) { + dp = root_string; + while (*dp != 0) + *cp++ = *dp++; + *cp++ = ' '; + + dp = nfsaddrs_string; + while (*dp != 0) + *cp++ = *dp++; + dp = cp; + do_ipaddrs(&cp, 0); + *cp++ = ' '; + + /* Add the server address to the root file system path. + */ + dp = strrchr(dp, ':'); + dp++; + do_nfsroot(&cp, dp); + *cp = 0; + } + puts("\n"); + + /* mappings on early boot can only handle 16M */ + if ( (int)(cmd_line[0]) > (16<<20)) + puts("cmd_line located > 16M\n"); + if ( (int)hold_residual > (16<<20)) + puts("hold_residual located > 16M\n"); + if ( initrd_start > (16<<20)) + puts("initrd_start located > 16M\n"); + + puts("Uncompressing Linux..."); + + gunzip(0, 0x400000, zimage_start, &zimage_size); + puts("done.\n"); + puts("Now booting the kernel\n"); + return (unsigned long)hold_residual; +} + +int +do_ipaddrs(char **cmd_cp, int echo) +{ + char *cp, *ip, ch; + unsigned char ipd; + int i, j, retval; + + /* We need to create the string: + * : + */ + cp = *cmd_cp; + retval = 0; + + if ((cp - 9) >= cmd_line) { + if (strncmp(cp - 9, "nfsaddrs=", 9) == 0) { + ip = (char *)0xfa000060; + retval = 1; + for (j=0; j<2; j++) { + for (i=0; i<4; i++) { + ipd = *ip++; + + ch = ipd/100; + if (ch) { + ch += '0'; + if (echo) + putc(ch); + *cp++ = ch; + ipd -= 100 * (ch - '0'); + } + + ch = ipd/10; + if (ch) { + ch += '0'; + if (echo) + putc(ch); + *cp++ = ch; + ipd -= 10 * (ch - '0'); + } + + ch = ipd + '0'; + if (echo) + putc(ch); + *cp++ = ch; + + ch = '.'; + if (echo) + putc(ch); + *cp++ = ch; + } + + /* At the end of the string, remove the + * '.' and replace it with a ':'. + */ + *(cp - 1) = ':'; + if (echo) { + putc('\b'); putc(':'); + } + } + + /* At the end of the second string, remove the + * '.' from both the command line and the + * screen. + */ + --cp; + putc('\b'); putc(' '); putc('\b'); + } + } + *cmd_cp = cp; + return(retval); +} + +void +do_nfsroot(char **cmd_cp, char *dp) +{ + char *cp, *rp, *ep; + + /* The boot argument (i.e /sys/mbxroot/zImage) is stored + * at offset 0x0078 in NVRAM. We use this path name to + * construct the root file system path. + */ + cp = *cmd_cp; + + /* build command string. + */ + rp = nfsroot_string; + while (*rp != 0) + *cp++ = *rp++; + + /* Add the server address to the path. + */ + while (*dp != ' ') + *cp++ = *dp++; + *cp++ = ':'; + + rp = (char *)0xfa000078; + ep = strrchr(rp, '/'); + + if (ep != 0) { + while (rp < ep) + *cp++ = *rp++; + } + else { + rp = defroot_string; + while (*rp != 0) + *cp++ = *rp++; + } + + *cmd_cp = cp; +} + +size_t strlen(const char * s) +{ + const char *sc; + + for (sc = s; *sc != '\0'; ++sc) + /* nothing */; + return sc - s; +} + +int strncmp(const char * cs,const char * ct,size_t count) +{ + register signed char __res = 0; + + while (count) { + if ((__res = *cs - *ct++) != 0 || !*cs++) + break; + count--; + } + + return __res; +} + +char * strrchr(const char * s, int c) +{ + const char *p = s + strlen(s); + do { + if (*p == (char)c) + return (char *)p; + } while (--p >= s); + return NULL; +} + +void puthex(unsigned long val) +{ + unsigned char buf[10]; + int i; + for (i = 7; i >= 0; i--) + { + buf[i] = "0123456789ABCDEF"[val & 0x0F]; + val >>= 4; + } + buf[8] = '\0'; + puts(buf); +} + +/* + * PCI/ISA I/O support + */ + +volatile unsigned char *ISA_io = (unsigned char *)0x80000000; +volatile unsigned char *ISA_mem = (unsigned char *)0xC0000000; + +void +outb(int port, char val) +{ + /* Ensure I/O operations complete */ + __asm__ volatile("eieio"); + ISA_io[port] = val; +} + +unsigned char +inb(int port) +{ + /* Ensure I/O operations complete */ + __asm__ volatile("eieio"); + return (ISA_io[port]); +} + +unsigned long +local_to_PCI(unsigned long addr) +{ + return ((addr & 0x7FFFFFFF) | 0x80000000); +} + +void +_bcopy(char *src, char *dst, int len) +{ + while (len--) *dst++ = *src++; +} diff -u --recursive --new-file v2.2.3/linux/arch/ppc/mbxboot/offset linux/arch/ppc/mbxboot/offset --- v2.2.3/linux/arch/ppc/mbxboot/offset Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/mbxboot/offset Fri Mar 19 10:50:03 1999 @@ -0,0 +1,4 @@ +#!/bin/bash + +OFFSET=`$1 -h $2 | grep $3 | grep -v zvmlinux| awk '{print $6}'` +echo "0x"$OFFSET diff -u --recursive --new-file v2.2.3/linux/arch/ppc/mbxboot/size linux/arch/ppc/mbxboot/size --- v2.2.3/linux/arch/ppc/mbxboot/size Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/mbxboot/size Fri Mar 19 10:50:03 1999 @@ -0,0 +1,4 @@ +#!/bin/bash + +OFFSET=`$1 -h $2 | grep $3 | grep -v zvmlinux | awk '{print $3}'` +echo "0x"$OFFSET diff -u --recursive --new-file v2.2.3/linux/arch/ppc/mm/init.c linux/arch/ppc/mm/init.c --- v2.2.3/linux/arch/ppc/mm/init.c Fri Jan 8 22:36:03 1999 +++ linux/arch/ppc/mm/init.c Fri Mar 19 10:50:04 1999 @@ -1,5 +1,5 @@ -/* - * $Id: init.c,v 1.139 1998/12/29 19:53:49 cort Exp $ + /* + * $Id: init.c,v 1.150 1999/03/10 08:16:33 cort Exp $ * * PowerPC version * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) @@ -34,6 +34,7 @@ #include #include #include +#include #ifdef CONFIG_BLK_DEV_INITRD #include /* for initrd_* */ #endif @@ -70,10 +71,12 @@ unsigned long ioremap_base; unsigned long ioremap_bot; unsigned long avail_start; -struct pgtable_cache_struct quicklists; extern int num_memory; extern struct mem_info memory[NUM_MEMINFO]; extern boot_infos_t *boot_infos; +#ifndef __SMP__ +struct pgtable_cache_struct quicklists; +#endif void MMU_init(void); static void *MMU_get_page(void); @@ -883,7 +886,8 @@ } } -__initfunc(static void *MMU_get_page(void)) +/* This can get called from ioremap, so don't make it an initfunc, OK? */ +static void *MMU_get_page(void) { void *p; @@ -926,14 +930,16 @@ break; case _MACH_prep: FREESEC(__pmac_begin,__pmac_end,num_pmac_pages); - FREESEC(__openfirmware_begin,__openfirmware_end,num_openfirmware_pages); break; case _MACH_mbx: FREESEC(__pmac_begin,__pmac_end,num_pmac_pages); - FREESEC(__openfirmware_begin,__openfirmware_end,num_openfirmware_pages); FREESEC(__prep_begin,__prep_end,num_prep_pages); break; } + + if ( !have_of ) + FREESEC( __openfirmware_begin, __openfirmware_end, + num_openfirmware_pages ); printk ("Freeing unused kernel memory: %ldk init", (num_freed_pages * PAGE_SIZE) >> 10); @@ -955,7 +961,6 @@ */ __initfunc(void MMU_init(void)) { - #ifdef __SMP__ if ( first_cpu_booted ) return; #endif /* __SMP__ */ @@ -989,6 +994,7 @@ break; case _MACH_chrp: setbat(0, 0xf8000000, 0xf8000000, 0x08000000, IO_PAGE); + setbat(1, 0x80000000, 0x80000000, 0x10000000, IO_PAGE); break; case _MACH_Pmac: { @@ -1029,6 +1035,7 @@ ioremap(PCI_CSR_ADDR, PCI_CSR_SIZE); /* ide needs to be able to get at PCI space -- Cort */ ioremap(0x80000000, 0x4000); + ioremap(0x81000000, 0x4000); #endif /* CONFIG_8xx */ } @@ -1236,6 +1243,9 @@ unsigned long a, total; unsigned long kstart, ksize; int i; + + /* max amount of RAM we allow -- Cort */ +#define RAM_LIMIT (768<<20) memory_node = find_devices("memory"); if (memory_node == NULL) { @@ -1260,7 +1270,18 @@ a = phys_mem.regions[0].address; if (a != 0) panic("RAM doesn't start at physical address 0"); + /* + * XXX: + * Make sure ram mappings don't stomp on IO space + * This is a temporary hack to keep this from happening + * until we move the KERNELBASE and can allocate RAM up + * to our nearest IO area. + * -- Cort + */ + if ( phys_mem.regions[0].size >= RAM_LIMIT ) + phys_mem.regions[0].size = RAM_LIMIT; total = phys_mem.regions[0].size; + if (phys_mem.n_regions > 1) { printk("RAM starting at 0x%x is not contiguous\n", phys_mem.regions[1].address); @@ -1277,8 +1298,15 @@ } prom_mem = phys_mem; for (i = 0; i < phys_avail.n_regions; ++i) + { + if ( phys_avail.regions[i].address >= RAM_LIMIT ) + continue; + if ( (phys_avail.regions[i].address+phys_avail.regions[i].size) + >= RAM_LIMIT ) + phys_avail.regions[i].size = RAM_LIMIT - phys_avail.regions[i].address; remove_mem_piece(&prom_mem, phys_avail.regions[i].address, phys_avail.regions[i].size, 1); + } /* * phys_avail records memory we can use now. @@ -1292,7 +1320,7 @@ remove_mem_piece(&prom_mem, kstart, ksize, 0); remove_mem_piece(&phys_avail, 0, 0x4000, 0); remove_mem_piece(&prom_mem, 0, 0x4000, 0); - +#undef RAM_LIMIT return __va(total); } diff -u --recursive --new-file v2.2.3/linux/arch/ppc/pmac_defconfig linux/arch/ppc/pmac_defconfig --- v2.2.3/linux/arch/ppc/pmac_defconfig Wed Mar 10 15:29:45 1999 +++ linux/arch/ppc/pmac_defconfig Wed Mar 10 21:30:32 1999 @@ -262,6 +262,7 @@ # CONFIG_SHAPER is not set # CONFIG_HOSTESS_SV11 is not set # CONFIG_COSA is not set +# CONFIG_RCPCI is not set # # Amateur Radio support diff -u --recursive --new-file v2.2.3/linux/arch/sparc/boot/piggyback.c linux/arch/sparc/boot/piggyback.c --- v2.2.3/linux/arch/sparc/boot/piggyback.c Wed Jul 16 19:22:50 1997 +++ linux/arch/sparc/boot/piggyback.c Wed Mar 10 16:53:36 1999 @@ -1,4 +1,4 @@ -/* $Id: piggyback.c,v 1.1 1997/07/11 11:05:17 jj Exp $ +/* $Id: piggyback.c,v 1.2 1998/12/15 12:24:43 jj Exp $ Simple utility to make a single-image install kernel with initial ramdisk for Sparc tftpbooting without need to set up nfs. @@ -46,16 +46,21 @@ struct stat s; int image, tail; + start = end = 0; if (stat (argv[3], &s) < 0) die (argv[3]); map = fopen (argv[2], "r"); if (!map) die(argv[2]); while (fgets (buffer, 1024, map)) { - if (!strcmp (buffer + 11, "start\n")) + if (!strcmp (buffer + 8, " T start\n") || !strcmp (buffer + 16, " T start\n")) start = strtoul (buffer, NULL, 16); - else if (!strcmp (buffer + 11, "end\n")) + else if (!strcmp (buffer + 8, " A end\n") || !strcmp (buffer + 16, " A end\n")) end = strtoul (buffer, NULL, 16); } fclose (map); + if (!start || !end) { + fprintf (stderr, "Could not determine start and end from System.map\n"); + exit(1); + } if ((image = open(argv[1],O_RDWR)) < 0) die(argv[1]); if (read(image,buffer,512) != 512) die(argv[1]); if (!memcmp (buffer, "\177ELF", 4)) { diff -u --recursive --new-file v2.2.3/linux/arch/sparc/config.in linux/arch/sparc/config.in --- v2.2.3/linux/arch/sparc/config.in Tue Jan 19 11:32:51 1999 +++ linux/arch/sparc/config.in Mon Mar 15 16:10:43 1999 @@ -1,4 +1,4 @@ -# $Id: config.in,v 1.63 1998/09/21 05:05:56 jj Exp $ +# $Id: config.in,v 1.68 1999/03/14 03:12:42 anton Exp $ # For a description of the syntax of this configuration file, # see the Configure script. # @@ -25,7 +25,7 @@ define_bool CONFIG_VT_CONSOLE y bool 'Support for AP1000 multicomputer' CONFIG_AP1000 -bool 'Symmetric multi-processing support' CONFIG_SMP +bool 'Symmetric multi-processing support (does not work on sun4/sun4c)' CONFIG_SMP if [ "$CONFIG_AP1000" = "y" ]; then define_bool CONFIG_NO_KEYBOARD y @@ -165,6 +165,9 @@ fi tristate 'Sun LANCE support' CONFIG_SUNLANCE tristate 'Sun Happy Meal 10/100baseT support' CONFIG_HAPPYMEAL + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate 'Sun BigMAC 10/100baseT support' CONFIG_SUNBMAC + fi tristate 'Sun QuadEthernet support' CONFIG_SUNQE tristate 'MyriCOM Gigabit Ethernet support' CONFIG_MYRI_SBUS # bool 'FDDI driver support' CONFIG_FDDI @@ -173,6 +176,15 @@ fi endmenu fi + +# This one must be before the filesystem configs. -DaveM +mainmenu_option next_comment +comment 'Unix98 PTY support' +bool 'Unix98 PTY support' CONFIG_UNIX98_PTYS +if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then + int 'Maximum number of Unix98 PTYs in use (0-2048)' CONFIG_UNIX98_PTY_COUNT 256 +fi +endmenu source fs/Config.in diff -u --recursive --new-file v2.2.3/linux/arch/sparc/defconfig linux/arch/sparc/defconfig --- v2.2.3/linux/arch/sparc/defconfig Wed Mar 10 15:29:45 1999 +++ linux/arch/sparc/defconfig Sun Mar 21 07:23:38 1999 @@ -117,7 +117,6 @@ # (it is safe to leave these untouched) # CONFIG_INET_RARP=m -CONFIG_IP_NOSR=y CONFIG_SKB_LARGE=y CONFIG_IPV6=m # CONFIG_IPV6_EUI64 is not set @@ -138,6 +137,10 @@ # CONFIG_NET_FASTROUTE is not set # CONFIG_NET_HW_FLOWCONTROL is not set # CONFIG_CPU_IS_SLOW is not set + +# +# QoS and/or fair queueing +# # CONFIG_NET_SCHED is not set # @@ -180,11 +183,13 @@ # FC4 drivers # CONFIG_FC4_SOC=m +CONFIG_FC4_SOCAL=m # # FC4 targets # CONFIG_SCSI_PLUTO=m +CONFIG_SCSI_FCAL=m # # Network device support @@ -202,28 +207,51 @@ # CONFIG_SLIP_MODE_SLIP6 is not set CONFIG_SUNLANCE=y CONFIG_HAPPYMEAL=m +CONFIG_SUNBMAC=m CONFIG_SUNQE=m CONFIG_MYRI_SBUS=m # +# Unix98 PTY support +# +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 + +# # Filesystems # # CONFIG_QUOTA is not set -CONFIG_MINIX_FS=m -CONFIG_EXT2_FS=y -CONFIG_ISO9660_FS=m -# CONFIG_JOLIET is not set +CONFIG_AUTOFS_FS=m +# CONFIG_ADFS_FS is not set +CONFIG_AFFS_FS=m +# CONFIG_HFS_FS is not set CONFIG_FAT_FS=m CONFIG_MSDOS_FS=m # CONFIG_UMSDOS_FS is not set CONFIG_VFAT_FS=m +CONFIG_ISO9660_FS=m +# CONFIG_JOLIET is not set +CONFIG_MINIX_FS=m +# CONFIG_NTFS_FS is not set +CONFIG_HPFS_FS=m CONFIG_PROC_FS=y +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +CONFIG_ROMFS_FS=m +CONFIG_EXT2_FS=y +CONFIG_SYSV_FS=m +CONFIG_UFS_FS=m +CONFIG_UFS_FS_WRITE=y + +# +# Network File Systems +# +CONFIG_CODA_FS=m CONFIG_NFS_FS=y CONFIG_NFSD=m # CONFIG_NFSD_SUN is not set CONFIG_SUNRPC=y CONFIG_LOCKD=y -CONFIG_CODA_FS=m CONFIG_SMB_FS=m CONFIG_SMB_WIN95=y CONFIG_NCP_FS=m @@ -233,21 +261,16 @@ # CONFIG_NCPFS_NFS_NS is not set # CONFIG_NCPFS_OS2_NS is not set # CONFIG_NCPFS_MOUNT_SUBDIR is not set -CONFIG_HPFS_FS=m -# CONFIG_NTFS_FS is not set -CONFIG_SYSV_FS=m -CONFIG_AFFS_FS=m -# CONFIG_HFS_FS is not set -CONFIG_ROMFS_FS=m -CONFIG_AUTOFS_FS=m -CONFIG_AMIGA_PARTITION=y -CONFIG_UFS_FS=m + +# +# Partition Types +# CONFIG_BSD_DISKLABEL=y +# CONFIG_MAC_PARTITION is not set CONFIG_SMD_DISKLABEL=y CONFIG_SOLARIS_X86_PARTITION=y -# CONFIG_ADFS_FS is not set -# CONFIG_QNX4FS_FS is not set -# CONFIG_MAC_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +CONFIG_AMIGA_PARTITION=y CONFIG_NLS=y # @@ -278,6 +301,7 @@ # CONFIG_NLS_ISO8859_7 is not set # CONFIG_NLS_ISO8859_8 is not set # CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_15 is not set # CONFIG_NLS_KOI8_R is not set # diff -u --recursive --new-file v2.2.3/linux/arch/sparc/kernel/Makefile linux/arch/sparc/kernel/Makefile --- v2.2.3/linux/arch/sparc/kernel/Makefile Tue Dec 22 14:16:54 1998 +++ linux/arch/sparc/kernel/Makefile Wed Mar 10 16:53:36 1999 @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.48 1998/09/21 05:04:46 jj Exp $ +# $Id: Makefile,v 1.49 1999/01/02 16:45:37 davem Exp $ # Makefile for the linux kernel. # # Note! Dependencies are done automagically by 'make dep', which also diff -u --recursive --new-file v2.2.3/linux/arch/sparc/kernel/devices.c linux/arch/sparc/kernel/devices.c --- v2.2.3/linux/arch/sparc/kernel/devices.c Sun Nov 8 14:02:44 1998 +++ linux/arch/sparc/kernel/devices.c Wed Mar 10 16:53:36 1999 @@ -29,24 +29,25 @@ prom_getstring(prom_root_node, "device_type", node_str, sizeof(node_str)); + prom_printf("Booting Linux...\n"); if(strcmp(node_str, "cpu") == 0) { linux_num_cpus++; } else { int scan; scan = prom_getchild(prom_root_node); - prom_printf("root child is %08lx\n", (unsigned long) scan); + /* One can look it up in PROM instead */ + /* prom_printf("root child is %08lx\n", (unsigned long) scan); */ while((scan = prom_getsibling(scan)) != 0) { prom_getstring(scan, "device_type", node_str, sizeof(node_str)); if(strcmp(node_str, "cpu") == 0) { linux_cpus[linux_num_cpus].prom_node = scan; prom_getproperty(scan, "mid", (char *) &thismid, sizeof(thismid)); linux_cpus[linux_num_cpus].mid = thismid; - prom_printf("Found CPU %d \n", - linux_num_cpus, (unsigned long) scan, - thismid); + /* prom_printf("Found CPU %d \n", linux_num_cpus, (unsigned long) scan, thismid); */ + printk("Found CPU %d \n", linux_num_cpus, (unsigned long) scan, thismid); linux_num_cpus++; } - }; + } if(linux_num_cpus == 0) { if (sparc_cpu_model == sun4d) { scan = prom_getchild(prom_root_node); @@ -59,9 +60,10 @@ prom_getproperty(node, "cpu-id", (char *) &thismid, sizeof(thismid)); linux_cpus[linux_num_cpus].prom_node = node; linux_cpus[linux_num_cpus].mid = thismid; - prom_printf("Found CPU %d \n", - linux_num_cpus, (unsigned long) node, - thismid); + /* prom_printf("Found CPU %d \n", + linux_num_cpus, (unsigned long) node, thismid); */ + printk("Found CPU %d \n", + linux_num_cpus, (unsigned long) node, thismid); linux_num_cpus++; } } diff -u --recursive --new-file v2.2.3/linux/arch/sparc/kernel/entry.S linux/arch/sparc/kernel/entry.S --- v2.2.3/linux/arch/sparc/kernel/entry.S Thu Nov 19 09:56:27 1998 +++ linux/arch/sparc/kernel/entry.S Wed Mar 10 16:53:36 1999 @@ -1,10 +1,10 @@ -/* $Id: entry.S,v 1.153 1998/11/11 15:12:33 jj Exp $ +/* $Id: entry.S,v 1.157 1999/01/19 07:54:32 davem Exp $ * arch/sparc/kernel/entry.S: Sparc trap low-level entry points. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) - * Copyright (C) 1996,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1996-1999 Jakub Jelinek (jj@sunsite.mff.cuni.cz) * Copyright (C) 1997 Anton Blanchard (anton@progsoc.uts.edu.au) */ @@ -1380,11 +1380,13 @@ /* Now that we have a real sys_clone, sys_fork() is * implemented in terms of it. Our _real_ implementation - * of SunOS vfork() will use sys_clone() instead. + * of SunOS vfork() will use sys_vfork(). + * + * XXX These three should be consolidated into mostly shared + * XXX code just like on sparc64... -DaveM */ .align 4 - .globl C_LABEL(sys_fork), C_LABEL(sys_vfork), flush_patch_two -C_LABEL(sys_vfork): + .globl C_LABEL(sys_fork), flush_patch_two C_LABEL(sys_fork): mov %o7, %l5 flush_patch_two: @@ -1422,6 +1424,23 @@ call C_LABEL(do_fork) mov %l5, %o7 + /* Whee, real vfork! */ + .globl C_LABEL(sys_vfork), flush_patch_four +C_LABEL(sys_vfork): +flush_patch_four: + FLUSH_ALL_KERNEL_WINDOWS; + rd %psr, %g4 + WRITE_PAUSE + rd %wim, %g5 + WRITE_PAUSE + std %g4, [%curptr + AOFF_task_tss + AOFF_thread_fork_kpsr] + sethi %hi(0x4000 | 0x0100 | SIGCHLD), %o0 + mov %fp, %o1 + or %o0, %lo(0x4000 | 0x0100 | SIGCHLD), %o0 + sethi %hi(C_LABEL(do_fork)), %l1 + jmpl %l1 + %lo(C_LABEL(do_fork)), %g0 + add %sp, REGWIN_SZ, %o2 + .align 4 linux_sparc_ni_syscall: sethi %hi(C_LABEL(sys_ni_syscall)), %l7 @@ -1454,11 +1473,10 @@ #ifdef __SMP__ .globl C_LABEL(ret_from_smpfork) C_LABEL(ret_from_smpfork): - /* Nowadays all we need to do is drop the scheduler lock. */ - sethi %hi(C_LABEL(scheduler_lock)), %o4 - stb %g0, [%o4 + %lo(C_LABEL(scheduler_lock))] wr %l0, PSR_ET, %psr WRITE_PAUSE + call schedule_tail + nop b C_LABEL(ret_sys_call) ld [%sp + REGWIN_SZ + PT_I0], %o0 #endif diff -u --recursive --new-file v2.2.3/linux/arch/sparc/kernel/head.S linux/arch/sparc/kernel/head.S --- v2.2.3/linux/arch/sparc/kernel/head.S Thu Aug 6 14:06:30 1998 +++ linux/arch/sparc/kernel/head.S Wed Mar 10 16:53:36 1999 @@ -1,4 +1,4 @@ -/* $Id: head.S,v 1.92 1998/06/10 07:21:55 davem Exp $ +/* $Id: head.S,v 1.93 1999/01/10 06:03:14 jj Exp $ * head.S: The initial boot code for the Sparc port of Linux. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -1108,6 +1108,9 @@ st %g4, [%g5 + 0x18] st %g4, [%g5 + 0x1c] set flush_patch_three, %g5 + st %g4, [%g5 + 0x18] + st %g4, [%g5 + 0x1c] + set flush_patch_four, %g5 st %g4, [%g5 + 0x18] st %g4, [%g5 + 0x1c] set flush_patch_exception, %g5 diff -u --recursive --new-file v2.2.3/linux/arch/sparc/kernel/pcic.c linux/arch/sparc/kernel/pcic.c --- v2.2.3/linux/arch/sparc/kernel/pcic.c Sun Nov 8 14:02:45 1998 +++ linux/arch/sparc/kernel/pcic.c Tue Mar 16 21:52:05 1999 @@ -1,4 +1,4 @@ -/* $Id: pcic.c,v 1.3 1998/10/07 11:34:56 jj Exp $ +/* $Id: pcic.c,v 1.5 1999/03/16 00:15:20 davem Exp $ * pcic.c: Sparc/PCI controller support * * Copyright (C) 1998 V. Roganov and G. Raiko @@ -498,9 +498,10 @@ tv->tv_sec--; } xtime = *tv; - time_state = TIME_BAD; - time_maxerror = 0x70000000; - time_esterror = 0x70000000; + time_adjust = 0; /* stop active adjtime() */ + time_status |= STA_UNSYNC; + time_maxerror = NTP_PHASE_LIMIT; + time_esterror = NTP_PHASE_LIMIT; sti(); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc/kernel/process.c linux/arch/sparc/kernel/process.c --- v2.2.3/linux/arch/sparc/kernel/process.c Mon Oct 5 13:13:37 1998 +++ linux/arch/sparc/kernel/process.c Sun Mar 21 18:37:56 1999 @@ -1,4 +1,4 @@ -/* $Id: process.c,v 1.126 1998/09/21 05:05:18 jj Exp $ +/* $Id: process.c,v 1.132 1999/03/22 02:12:13 davem Exp $ * linux/arch/sparc/kernel/process.c * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -62,7 +62,7 @@ /* endless idle loop with no priority at all */ current->priority = 0; - current->counter = 0; + current->counter = -100; for (;;) { if (ARCH_SUN4C_SUN4) { static int count = HZ; @@ -108,13 +108,15 @@ /* This is being executed in task 0 'user space'. */ int cpu_idle(void *unused) { + /* endless idle loop with no priority at all */ current->priority = 0; + current->counter = -100; while(1) { - check_pgt_cache(); - run_task_queue(&tq_scheduler); - /* endless idle loop with no priority at all */ - current->counter = 0; - schedule(); + if(current->need_resched) { + schedule(); + check_pgt_cache(); + } + barrier(); /* or else gcc optimizes... */ } } @@ -440,10 +442,17 @@ size = ((unsigned long)src->fp) - ((unsigned long)src); sp = (struct sparc_stackf *)(((unsigned long)dst) - size); + /* do_fork() grabs the parent semaphore, we must release it + * temporarily so we can build the child clone stack frame + * without deadlocking. + */ + up(¤t->mm->mmap_sem); if (copy_to_user(sp, src, size)) - return 0; - if (put_user(dst, &sp->fp)) - return 0; + sp = (struct sparc_stackf *) 0; + else if (put_user(dst, &sp->fp)) + sp = (struct sparc_stackf *) 0; + down(¤t->mm->mmap_sem); + return sp; } @@ -657,4 +666,38 @@ out: unlock_kernel(); return error; +} + +/* + * This is the mechanism for creating a new kernel thread. + * + * NOTE! Only a kernel-only process(ie the swapper or direct descendants + * who haven't done an "execve()") should use this: it will work within + * a system call from a "real" process, but the process memory space will + * not be free'd until both the parent and the child have exited. + */ +pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) +{ + long retval; + + __asm__ __volatile("mov %4, %%g2\n\t" /* Set aside fn ptr... */ + "mov %5, %%g3\n\t" /* and arg. */ + "mov %1, %%g1\n\t" + "mov %2, %%o0\n\t" /* Clone flags. */ + "mov 0, %%o1\n\t" /* usp arg == 0 */ + "t 0x10\n\t" /* Linux/Sparc clone(). */ + "cmp %%o1, 0\n\t" + "be 1f\n\t" /* The parent, just return. */ + " nop\n\t" /* Delay slot. */ + "jmpl %%g2, %%o7\n\t" /* Call the function. */ + " mov %%g3, %%o0\n\t" /* Get back the arg in delay. */ + "mov %3, %%g1\n\t" + "t 0x10\n\t" /* Linux/Sparc exit(). */ + /* Notreached by child. */ + "1: mov %%o0, %0\n\t" : + "=r" (retval) : + "i" (__NR_clone), "r" (flags | CLONE_VM), + "i" (__NR_exit), "r" (fn), "r" (arg) : + "g1", "g2", "g3", "o0", "o1", "memory", "cc"); + return retval; } diff -u --recursive --new-file v2.2.3/linux/arch/sparc/kernel/ptrace.c linux/arch/sparc/kernel/ptrace.c --- v2.2.3/linux/arch/sparc/kernel/ptrace.c Thu Dec 31 10:28:59 1998 +++ linux/arch/sparc/kernel/ptrace.c Mon Mar 15 16:10:43 1999 @@ -528,6 +528,8 @@ if (((current->personality & PER_BSD) && (request == PTRACE_SUNATTACH)) || (!(current->personality & PER_BSD) && (request == PTRACE_ATTACH))) { + unsigned long flags; + if(child == current) { /* Try this under SunOS/Solaris, bwa haha * You'll never be able to kill the process. ;-) @@ -539,8 +541,9 @@ (current->uid != child->euid) || (current->uid != child->uid) || (current->gid != child->egid) || - (current->gid != child->gid)) && - !capable(CAP_SYS_PTRACE)) { + (current->gid != child->sgid) || + (cap_issubset(child->cap_permitted, current->cap_permitted)) || + (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE)) { pt_error_return(regs, EPERM); goto out; } @@ -550,14 +553,13 @@ goto out; } child->flags |= PF_PTRACED; + write_lock_irqsave(&tasklist_lock, flags); if(child->p_pptr != current) { - unsigned long flags; - write_lock_irqsave(&tasklist_lock, flags); REMOVE_LINKS(child); child->p_pptr = current; SET_LINKS(child); - write_unlock_irqrestore(&tasklist_lock, flags); } + write_unlock_irqrestore(&tasklist_lock, flags); send_sig(SIGSTOP, child, 1); pt_succ_return(regs, 0); goto out; diff -u --recursive --new-file v2.2.3/linux/arch/sparc/kernel/signal.c linux/arch/sparc/kernel/signal.c --- v2.2.3/linux/arch/sparc/kernel/signal.c Sun Nov 8 14:02:45 1998 +++ linux/arch/sparc/kernel/signal.c Wed Mar 10 16:53:36 1999 @@ -1,4 +1,4 @@ -/* $Id: signal.c,v 1.90 1998/10/18 03:31:05 davem Exp $ +/* $Id: signal.c,v 1.91 1999/01/26 11:00:44 jj Exp $ * linux/arch/sparc/kernel/signal.c * * Copyright (C) 1991, 1992 Linus Torvalds @@ -38,6 +38,8 @@ /* This turned off for production... */ /* #define DEBUG_SIGNALS 1 */ +/* #define DEBUG_SIGNALS_TRACE 1 */ +/* #define DEBUG_SIGNALS_MAPS 1 */ /* Signal frames: the original one (compatible with SunOS): * @@ -1004,6 +1006,59 @@ } } +#ifdef DEBUG_SIGNALS_MAPS + +#define MAPS_LINE_FORMAT "%08lx-%08lx %s %08lx %s %lu " + +static inline void read_maps (void) +{ + struct vm_area_struct * map, * next; + char * buffer; + ssize_t i; + + buffer = (char*)__get_free_page(GFP_KERNEL); + if (!buffer) + return; + + for (map = current->mm->mmap ; map ; map = next ) { + /* produce the next line */ + char *line; + char str[5], *cp = str; + int flags; + kdev_t dev; + unsigned long ino; + + /* + * Get the next vma now (but it won't be used if we sleep). + */ + next = map->vm_next; + flags = map->vm_flags; + + *cp++ = flags & VM_READ ? 'r' : '-'; + *cp++ = flags & VM_WRITE ? 'w' : '-'; + *cp++ = flags & VM_EXEC ? 'x' : '-'; + *cp++ = flags & VM_MAYSHARE ? 's' : 'p'; + *cp++ = 0; + + dev = 0; + ino = 0; + if (map->vm_file != NULL) { + dev = map->vm_file->f_dentry->d_inode->i_dev; + ino = map->vm_file->f_dentry->d_inode->i_ino; + line = d_path(map->vm_file->f_dentry, buffer, PAGE_SIZE); + } + printk(MAPS_LINE_FORMAT, map->vm_start, map->vm_end, str, map->vm_offset, + kdevname(dev), ino); + if (map->vm_file != NULL) + printk("%s\n", line); + else + printk("\n"); + } + free_page((unsigned long)buffer); + return; +} +#endif + /* Note that 'init' is a special process: it doesn't get signals it doesn't * want to handle. Thus you cannot kill init even with a SIGKILL even by * mistake. @@ -1115,8 +1170,25 @@ } #ifdef DEBUG_SIGNALS /* Very useful to debug dynamic linker problems */ - printk ("Sig ILL going...\n"); + printk ("Sig %ld going for %s[%d]...\n", signr, current->comm, current->pid); show_regs (regs); +#ifdef DEBUG_SIGNALS_TRACE + { + struct reg_window *rw = (struct reg_window *)regs->u_regs[UREG_FP]; + unsigned int ins[8]; + + while(rw && + !(((unsigned long) rw) & 0x3)) { + copy_from_user(ins, &rw->ins[0], sizeof(ins)); + printk("Caller[%08x](%08x,%08x,%08x,%08x,%08x,%08x)\n", ins[7], ins[0], ins[1], ins[2], ins[3], ins[4], ins[5]); + rw = (struct reg_window *)(unsigned long)ins[6]; + } + } +#endif +#ifdef DEBUG_SIGNALS_MAPS + printk("Maps:\n"); + read_maps(); +#endif #endif /* fall through */ default: diff -u --recursive --new-file v2.2.3/linux/arch/sparc/kernel/smp.c linux/arch/sparc/kernel/smp.c --- v2.2.3/linux/arch/sparc/kernel/smp.c Sun Nov 8 14:02:45 1998 +++ linux/arch/sparc/kernel/smp.c Wed Mar 10 16:53:36 1999 @@ -52,6 +52,7 @@ int smp_activated = 0; volatile int cpu_number_map[NR_CPUS]; volatile int __cpu_logical_map[NR_CPUS]; +cycles_t cacheflush_time = 0; /* XXX */ /* The only guaranteed locking primitive available on all Sparc * processors is 'ldstub [%reg + immediate], %dest_reg' which atomically diff -u --recursive --new-file v2.2.3/linux/arch/sparc/kernel/sparc_ksyms.c linux/arch/sparc/kernel/sparc_ksyms.c --- v2.2.3/linux/arch/sparc/kernel/sparc_ksyms.c Thu Nov 19 09:56:27 1998 +++ linux/arch/sparc/kernel/sparc_ksyms.c Sun Mar 21 07:23:38 1999 @@ -1,4 +1,4 @@ -/* $Id: sparc_ksyms.c,v 1.73 1998/11/06 13:49:54 jj Exp $ +/* $Id: sparc_ksyms.c,v 1.77 1999/03/21 06:37:43 davem Exp $ * arch/sparc/kernel/ksyms.c: Sparc specific ksyms support. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -66,6 +66,7 @@ extern void bcopy (const char *, char *, int); extern int __ashrdi3(int, int); +extern int __lshrdi3(int, int); extern void dump_thread(struct pt_regs *, struct user *); @@ -91,6 +92,7 @@ /* used by various drivers */ EXPORT_SYMBOL(sparc_cpu_model); EXPORT_SYMBOL_PRIVATE(_spinlock_waitfor); +EXPORT_SYMBOL(kernel_thread); #ifdef SPIN_LOCK_DEBUG EXPORT_SYMBOL(_do_spin_lock); EXPORT_SYMBOL(_do_spin_unlock); @@ -118,6 +120,7 @@ #endif EXPORT_SYMBOL(page_offset); +EXPORT_SYMBOL(sparc_valid_addr_bitmap); #ifndef CONFIG_SUN4 EXPORT_SYMBOL(stack_top); @@ -211,6 +214,7 @@ EXPORT_SYMBOL(prom_apply_obio_ranges); EXPORT_SYMBOL(prom_getname); EXPORT_SYMBOL(prom_feval); +EXPORT_SYMBOL(prom_getbool); EXPORT_SYMBOL(prom_getstring); EXPORT_SYMBOL(prom_apply_sbus_ranges); EXPORT_SYMBOL(prom_getint); @@ -268,6 +272,7 @@ EXPORT_SYMBOL_NOVERS(memset); EXPORT_SYMBOL_NOVERS(memmove); EXPORT_SYMBOL_NOVERS(__ashrdi3); +EXPORT_SYMBOL_NOVERS(__lshrdi3); EXPORT_SYMBOL_DOT(rem); EXPORT_SYMBOL_DOT(urem); diff -u --recursive --new-file v2.2.3/linux/arch/sparc/kernel/sys_sparc.c linux/arch/sparc/kernel/sys_sparc.c --- v2.2.3/linux/arch/sparc/kernel/sys_sparc.c Sun Nov 8 14:02:45 1998 +++ linux/arch/sparc/kernel/sys_sparc.c Sun Mar 21 07:23:38 1999 @@ -1,4 +1,4 @@ -/* $Id: sys_sparc.c,v 1.49 1998/10/11 06:57:53 davem Exp $ +/* $Id: sys_sparc.c,v 1.51 1999/03/20 22:02:00 davem Exp $ * linux/arch/sparc/kernel/sys_sparc.c * * This file contains various random system calls that @@ -25,6 +25,8 @@ #include #include +/* #define DEBUG_UNIMP_SYSCALL */ + /* XXX Make this per-binary type, this way we can detect the type of * XXX a binary. Every Sparc executable calls this very early on. */ @@ -189,6 +191,7 @@ goto out; } retval = -ENOMEM; + len = PAGE_ALIGN(len); if(!(flags & MAP_FIXED) && !addr) { addr = get_unmapped_area(addr, len); if(!addr) @@ -202,6 +205,7 @@ if(ARCH_SUN4C_SUN4) { if(((addr >= 0x20000000) && (addr < 0xe0000000))) { + /* VM hole */ retval = current->mm->brk; goto out_putf; } @@ -223,9 +227,14 @@ asmlinkage unsigned long c_sys_nis_syscall (struct pt_regs *regs) { + static int count = 0; + + if (count++ > 5) return -ENOSYS; lock_kernel(); printk ("Unimplemented SPARC system call %d\n",(int)regs->u_regs[1]); +#ifdef DEBUG_UNIMP_SYSCALL show_regs (regs); +#endif unlock_kernel(); return -ENOSYS; } diff -u --recursive --new-file v2.2.3/linux/arch/sparc/kernel/systbls.S linux/arch/sparc/kernel/systbls.S --- v2.2.3/linux/arch/sparc/kernel/systbls.S Mon Oct 5 13:13:37 1998 +++ linux/arch/sparc/kernel/systbls.S Sun Mar 21 07:23:38 1999 @@ -1,4 +1,4 @@ -/* $Id: systbls.S,v 1.80 1998/09/21 05:04:59 jj Exp $ +/* $Id: systbls.S,v 1.82 1999/03/20 22:01:59 davem Exp $ * systbls.S: System call entry point tables for OS compatibility. * The native Linux system call table lives here also. * diff -u --recursive --new-file v2.2.3/linux/arch/sparc/kernel/time.c linux/arch/sparc/kernel/time.c --- v2.2.3/linux/arch/sparc/kernel/time.c Wed Jan 20 23:14:04 1999 +++ linux/arch/sparc/kernel/time.c Mon Mar 15 16:10:43 1999 @@ -1,4 +1,4 @@ -/* $Id: time.c,v 1.39 1998/09/29 09:46:15 davem Exp $ +/* $Id: time.c,v 1.43 1999/03/15 22:13:31 davem Exp $ * linux/arch/sparc/kernel/time.c * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -39,6 +39,8 @@ #include #include +extern rwlock_t xtime_lock; + enum sparc_clock_type sp_clock_typ; struct mostek48t02 *mstk48t02_regs = 0; struct mostek48t08 *mstk48t08_regs = 0; @@ -80,7 +82,7 @@ #ifdef CONFIG_SUN4 if((idprom->id_machtype == (SM_SUN4 | SM_4_260)) || - (idprom->id_machtype == (SM_SUN4 | SM_4_110))) { + (idprom->id_machtype == (SM_SUN4 | SM_4_110))) { int temp; intersil_read_intr(intersil_clock, temp); /* re-enable the irq */ @@ -89,6 +91,8 @@ #endif clear_clock_irq(); + write_lock(&xtime_lock); + do_timer(regs); /* Determine when to update the Mostek clock. */ @@ -101,6 +105,7 @@ else last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ } + write_unlock(&xtime_lock); } /* Converts Gregorian date to seconds since 1970-01-01 00:00:00. @@ -436,6 +441,9 @@ return offset + count; } +/* This need not obtain the xtime_lock as it is coded in + * an implicitly SMP safe way already. + */ void do_gettimeofday(struct timeval *tv) { #if CONFIG_AP1000 @@ -485,12 +493,13 @@ void do_settimeofday(struct timeval *tv) { + write_lock_irq(&xtime_lock); bus_do_settimeofday(tv); + write_unlock_irq(&xtime_lock); } static void sbus_do_settimeofday(struct timeval *tv) { - cli(); #if !CONFIG_AP1000 tv->tv_usec -= do_gettimeoffset(); if(tv->tv_usec < 0) { @@ -501,10 +510,8 @@ xtime = *tv; time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; - time_state = TIME_ERROR; /* p. 24, (a) */ time_maxerror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT; - sti(); } /* @@ -544,7 +551,7 @@ } else { printk(KERN_WARNING "set_rtc_mmss: can't update from %d to %d\n", - cmos_minutes, real_minutes); + mostek_minutes, real_minutes); return -1; } diff -u --recursive --new-file v2.2.3/linux/arch/sparc/kernel/traps.c linux/arch/sparc/kernel/traps.c --- v2.2.3/linux/arch/sparc/kernel/traps.c Mon Oct 5 13:13:37 1998 +++ linux/arch/sparc/kernel/traps.c Wed Mar 10 16:53:36 1999 @@ -1,4 +1,4 @@ -/* $Id: traps.c,v 1.57 1998/09/17 11:04:51 jj Exp $ +/* $Id: traps.c,v 1.59 1999/03/06 12:07:31 anton Exp $ * arch/sparc/kernel/traps.c * * Copyright 1995 David S. Miller (davem@caip.rutgers.edu) @@ -82,8 +82,13 @@ printk("\n"); } +#define __SAVE __asm__ __volatile__("save %sp, -0x40, %sp\n\t") +#define __RESTORE __asm__ __volatile__("restore %g0, %g0, %g0\n\t") + void die_if_kernel(char *str, struct pt_regs *regs) { + int count = 0; + /* Amuse the user. */ printk( " \\|/ ____ \\|/\n" @@ -93,6 +98,27 @@ printk("%s(%d): %s\n", current->comm, current->pid, str); show_regs(regs); + + __SAVE; __SAVE; __SAVE; __SAVE; + __SAVE; __SAVE; __SAVE; __SAVE; + __RESTORE; __RESTORE; __RESTORE; __RESTORE; + __RESTORE; __RESTORE; __RESTORE; __RESTORE; + + { + struct reg_window *rw = (struct reg_window *)regs->u_regs[UREG_FP]; + + /* Stop the back trace when we hit userland or we + * find some badly aligned kernel stack. Set an upper + * bound in case our stack is trashed and we loop. + */ + while(rw && + count++ < 30 && + (((unsigned long) rw) >= PAGE_OFFSET) && + !(((unsigned long) rw) & 0x7)) { + printk("Caller[%08lx]\n", rw->ins[7]); + rw = (struct reg_window *)rw->ins[6]; + } + } printk("Instruction DUMP:"); instruction_dump ((unsigned long *) regs->pc); if(regs->psr & PSR_PS) diff -u --recursive --new-file v2.2.3/linux/arch/sparc/lib/Makefile linux/arch/sparc/lib/Makefile --- v2.2.3/linux/arch/sparc/lib/Makefile Tue Dec 22 14:16:54 1998 +++ linux/arch/sparc/lib/Makefile Sun Mar 21 07:23:38 1999 @@ -1,11 +1,11 @@ -# $Id: Makefile,v 1.26 1998/07/26 03:02:43 davem Exp $ +# $Id: Makefile,v 1.28 1999/03/21 06:37:44 davem Exp $ # Makefile for Sparc library files.. # OBJS = mul.o rem.o sdiv.o udiv.o umul.o urem.o ashrdi3.o memcpy.o memset.o \ strlen.o checksum.o blockops.o memscan.o memcmp.o strncmp.o \ strncpy_from_user.o divdi3.o udivdi3.o strlen_user.o \ - copy_user.o locks.o atomic.o bitops.o debuglocks.o + copy_user.o locks.o atomic.o bitops.o debuglocks.o lshrdi3.o ifdef CONFIG_SMP OBJS += irqlock.o @@ -88,6 +88,9 @@ ashrdi3.o: ashrdi3.S $(CC) -D__ASSEMBLY__ -c -o ashrdi3.o ashrdi3.S + +lshrdi3.o: lshrdi3.S + $(CC) -D__ASSEMBLY__ -c -o lshrdi3.o lshrdi3.S dep: diff -u --recursive --new-file v2.2.3/linux/arch/sparc/lib/atomic.S linux/arch/sparc/lib/atomic.S --- v2.2.3/linux/arch/sparc/lib/atomic.S Thu Apr 23 20:21:30 1998 +++ linux/arch/sparc/lib/atomic.S Wed Mar 10 16:53:36 1999 @@ -51,12 +51,16 @@ 1: ldstub [%g1 + 3], %g7 ! Spin on the byte lock for SMP. orcc %g7, 0x0, %g0 ! Did we get it? bne 1b ! Nope... -#endif ld [%g1], %g7 ! Load locked atomic_t sra %g7, 8, %g7 ! Get signed 24-bit integer add %g7, %g2, %g2 ! Add in argument sll %g2, 8, %g7 ! Transpose back to atomic_t st %g7, [%g1] ! Clever: This releases the lock as well. +#else + ld [%g1], %g7 ! Load locked atomic_t + add %g7, %g2, %g2 ! Add in argument + st %g2, [%g1] ! Store it back +#endif wr %g3, 0x0, %psr ! Restore original PSR_PIL nop; nop; nop; ! Let the bits set jmpl %o7, %g0 ! NOTE: not + 8, see callers in atomic.h @@ -72,12 +76,16 @@ 1: ldstub [%g1 + 3], %g7 ! Spin on the byte lock for SMP. orcc %g7, 0x0, %g0 ! Did we get it? bne 1b ! Nope... -#endif ld [%g1], %g7 ! Load locked atomic_t sra %g7, 8, %g7 ! Get signed 24-bit integer sub %g7, %g2, %g2 ! Subtract argument sll %g2, 8, %g7 ! Transpose back to atomic_t st %g7, [%g1] ! Clever: This releases the lock as well +#else + ld [%g1], %g7 ! Load locked atomic_t + sub %g7, %g2, %g2 ! Subtract argument + st %g2, [%g1] ! Store it back +#endif wr %g3, 0x0, %psr ! Restore original PSR_PIL nop; nop; nop; ! Let the bits set jmpl %o7, %g0 ! NOTE: not + 8, see callers in atomic.h diff -u --recursive --new-file v2.2.3/linux/arch/sparc/lib/debuglocks.c linux/arch/sparc/lib/debuglocks.c --- v2.2.3/linux/arch/sparc/lib/debuglocks.c Sun Nov 8 14:02:45 1998 +++ linux/arch/sparc/lib/debuglocks.c Wed Mar 10 16:53:36 1999 @@ -1,4 +1,4 @@ -/* $Id: debuglocks.c,v 1.5 1998/10/14 09:19:04 jj Exp $ +/* $Id: debuglocks.c,v 1.6 1999/02/23 13:23:55 jj Exp $ * debuglocks.c: Debugging versions of SMP locking primitives. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -28,9 +28,11 @@ static inline void show(char *str, spinlock_t *lock, unsigned long caller) { int cpu = smp_processor_id(); + extern spinlock_t console_lock; - printk("%s(%p) CPU#%d stuck at %08lx, owner PC(%08lx):CPU(%lx)\n",str, - lock, cpu, caller, lock->owner_pc & ~3, lock->owner_pc & 3); + if (lock != &console_lock) + printk("%s(%p) CPU#%d stuck at %08lx, owner PC(%08lx):CPU(%lx)\n",str, + lock, cpu, caller, lock->owner_pc & ~3, lock->owner_pc & 3); } static inline void show_read(char *str, rwlock_t *lock, unsigned long caller) diff -u --recursive --new-file v2.2.3/linux/arch/sparc/lib/lshrdi3.S linux/arch/sparc/lib/lshrdi3.S --- v2.2.3/linux/arch/sparc/lib/lshrdi3.S Wed Dec 31 16:00:00 1969 +++ linux/arch/sparc/lib/lshrdi3.S Sun Mar 21 07:23:38 1999 @@ -0,0 +1,29 @@ +/* $Id: lshrdi3.S,v 1.1 1999/03/21 06:37:45 davem Exp $ */ + +#include + + .globl C_LABEL(__lshrdi3) +C_LABEL(__lshrdi3): + cmp %o2, 0 + be 3f + mov 0x20, %g2 + + sub %g2, %o2, %g2 + cmp %g2, 0 + bg 1f + srl %o0, %o2, %o4 + + clr %o4 + neg %g2 + b 2f + srl %o0, %g2, %o5 +1: + sll %o0, %g2, %g3 + srl %o1, %o2, %g2 + or %g2, %g3, %o5 +2: + mov %o4, %o0 + mov %o5, %o1 +3: + retl + nop diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/fabss.c linux/arch/sparc/math-emu/fabss.c --- v2.2.3/linux/arch/sparc/math-emu/fabss.c Thu Apr 23 20:21:31 1998 +++ linux/arch/sparc/math-emu/fabss.c Wed Mar 10 16:53:36 1999 @@ -2,5 +2,5 @@ { /* Clear the sign bit (high bit of word 0) */ rd[0] = rs2[0] & 0x7fffffffUL; - return 1; + return 0; } diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/fcmpd.c linux/arch/sparc/math-emu/fcmpd.c --- v2.2.3/linux/arch/sparc/math-emu/fcmpd.c Thu Apr 23 20:21:31 1998 +++ linux/arch/sparc/math-emu/fcmpd.c Wed Mar 10 16:53:36 1999 @@ -14,5 +14,5 @@ ret = 2; *fsr = (*fsr & ~0xc00) | (ret << 10); - return 1; + return 0; } diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/fcmped.c linux/arch/sparc/math-emu/fcmped.c --- v2.2.3/linux/arch/sparc/math-emu/fcmped.c Thu Apr 23 20:21:31 1998 +++ linux/arch/sparc/math-emu/fcmped.c Wed Mar 10 16:53:36 1999 @@ -14,5 +14,5 @@ ret = 2; *fsr = (*fsr & ~0xc00) | (ret << 10); - return 1; + return 0; } diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/fcmpeq.c linux/arch/sparc/math-emu/fcmpeq.c --- v2.2.3/linux/arch/sparc/math-emu/fcmpeq.c Thu Apr 23 20:21:31 1998 +++ linux/arch/sparc/math-emu/fcmpeq.c Wed Mar 10 16:53:36 1999 @@ -14,5 +14,5 @@ fsr = *(unsigned long *)rd; fsr &= ~0xc00; fsr |= (ret << 10); *(unsigned long *)rd = fsr; - return 1; + return 0; } diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/fcmpes.c linux/arch/sparc/math-emu/fcmpes.c --- v2.2.3/linux/arch/sparc/math-emu/fcmpes.c Thu Apr 23 20:21:31 1998 +++ linux/arch/sparc/math-emu/fcmpes.c Wed Mar 10 16:53:36 1999 @@ -14,5 +14,5 @@ ret = 2; *fsr = (*fsr & ~0xc00) | (ret << 10); - return 1; + return 0; } diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/fcmpq.c linux/arch/sparc/math-emu/fcmpq.c --- v2.2.3/linux/arch/sparc/math-emu/fcmpq.c Thu Apr 23 20:21:31 1998 +++ linux/arch/sparc/math-emu/fcmpq.c Wed Mar 10 16:53:36 1999 @@ -14,5 +14,5 @@ fsr = *(unsigned long *)rd; fsr &= ~0xc00; fsr |= (ret << 10); *(unsigned long *)rd = fsr; - return 1; + return 0; } diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/fcmps.c linux/arch/sparc/math-emu/fcmps.c --- v2.2.3/linux/arch/sparc/math-emu/fcmps.c Thu Apr 23 20:21:31 1998 +++ linux/arch/sparc/math-emu/fcmps.c Wed Mar 10 16:53:36 1999 @@ -14,5 +14,5 @@ ret = 2; *fsr = (*fsr & ~0xc00) | (ret << 10); - return 1; + return 0; } diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/fdmulq.c linux/arch/sparc/math-emu/fdmulq.c --- v2.2.3/linux/arch/sparc/math-emu/fdmulq.c Thu Apr 23 20:21:31 1998 +++ linux/arch/sparc/math-emu/fdmulq.c Wed Mar 10 16:53:36 1999 @@ -11,6 +11,5 @@ __FP_UNPACK_D(IN, rs2); FP_CONV(Q,D,4,2,B,IN); FP_MUL_Q(R, A, B); - __FP_PACK_Q(rd, R); - return 1; + return __FP_PACK_Q(rd, R); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/fdtoq.c linux/arch/sparc/math-emu/fdtoq.c --- v2.2.3/linux/arch/sparc/math-emu/fdtoq.c Thu Apr 23 20:21:31 1998 +++ linux/arch/sparc/math-emu/fdtoq.c Wed Mar 10 16:53:36 1999 @@ -8,6 +8,5 @@ __FP_UNPACK_D(A, rs2); FP_CONV(Q,D,4,2,R,A); - __FP_PACK_Q(rd, R); - return 1; + return __FP_PACK_Q(rd, R); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/fdtos.c linux/arch/sparc/math-emu/fdtos.c --- v2.2.3/linux/arch/sparc/math-emu/fdtos.c Thu Apr 23 20:21:31 1998 +++ linux/arch/sparc/math-emu/fdtos.c Wed Mar 10 16:53:36 1999 @@ -8,6 +8,5 @@ __FP_UNPACK_D(A, rs2); FP_CONV(S,D,1,2,R,A); - __FP_PACK_S(rd, R); - return 1; + return __FP_PACK_S(rd, R); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/fnegs.c linux/arch/sparc/math-emu/fnegs.c --- v2.2.3/linux/arch/sparc/math-emu/fnegs.c Fri May 8 23:14:45 1998 +++ linux/arch/sparc/math-emu/fnegs.c Wed Mar 10 16:53:36 1999 @@ -2,5 +2,5 @@ { /* just change the sign bit */ rd[0] = rs2[0] ^ 0x80000000UL; - return 1; + return 0; } diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/fqtod.c linux/arch/sparc/math-emu/fqtod.c --- v2.2.3/linux/arch/sparc/math-emu/fqtod.c Thu Apr 23 20:21:31 1998 +++ linux/arch/sparc/math-emu/fqtod.c Wed Mar 10 16:53:36 1999 @@ -8,6 +8,5 @@ __FP_UNPACK_Q(A, rs2); FP_CONV(D,Q,2,4,R,A); - __FP_PACK_D(rd, R); - return 1; + return __FP_PACK_D(rd, R); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/fqtos.c linux/arch/sparc/math-emu/fqtos.c --- v2.2.3/linux/arch/sparc/math-emu/fqtos.c Thu Apr 23 20:21:31 1998 +++ linux/arch/sparc/math-emu/fqtos.c Wed Mar 10 16:53:36 1999 @@ -8,6 +8,5 @@ __FP_UNPACK_Q(A, rs2); FP_CONV(S,Q,1,4,R,A); - __FP_PACK_S(rd, R); - return 1; + return __FP_PACK_S(rd, R); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/fsmuld.c linux/arch/sparc/math-emu/fsmuld.c --- v2.2.3/linux/arch/sparc/math-emu/fsmuld.c Thu Apr 23 20:21:31 1998 +++ linux/arch/sparc/math-emu/fsmuld.c Wed Mar 10 16:53:36 1999 @@ -11,6 +11,5 @@ __FP_UNPACK_S(IN, rs2); FP_CONV(D,S,2,1,B,IN); FP_MUL_D(R, A, B); - __FP_PACK_D(rd, R); - return 1; + return __FP_PACK_D(rd, R); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/fstod.c linux/arch/sparc/math-emu/fstod.c --- v2.2.3/linux/arch/sparc/math-emu/fstod.c Thu Apr 23 20:21:31 1998 +++ linux/arch/sparc/math-emu/fstod.c Wed Mar 10 16:53:36 1999 @@ -8,6 +8,5 @@ __FP_UNPACK_S(A, rs2); FP_CONV(D,S,2,1,R,A); - __FP_PACK_D(rd, R); - return 1; + return __FP_PACK_D(rd, R); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/fstoq.c linux/arch/sparc/math-emu/fstoq.c --- v2.2.3/linux/arch/sparc/math-emu/fstoq.c Thu Apr 23 20:21:31 1998 +++ linux/arch/sparc/math-emu/fstoq.c Wed Mar 10 16:53:36 1999 @@ -8,6 +8,5 @@ __FP_UNPACK_S(A, rs2); FP_CONV(Q,S,4,1,R,A); - __FP_PACK_Q(rd, R); - return 1; + return __FP_PACK_Q(rd, R); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/math.c linux/arch/sparc/math-emu/math.c --- v2.2.3/linux/arch/sparc/math-emu/math.c Thu Apr 23 20:21:31 1998 +++ linux/arch/sparc/math-emu/math.c Wed Mar 10 16:53:36 1999 @@ -124,6 +124,7 @@ #include #include +#include "soft-fp.h" #define FLOATFUNC(x) extern int x(void *,void *,void *) @@ -189,6 +190,13 @@ FLOATFUNC(FITOS); /* v6 */ FLOATFUNC(FITOD); /* v6 */ +#define FSR_TEM_SHIFT 23UL +#define FSR_TEM_MASK (0x1fUL << FSR_TEM_SHIFT) +#define FSR_AEXC_SHIFT 5UL +#define FSR_AEXC_MASK (0x1fUL << FSR_AEXC_SHIFT) +#define FSR_CEXC_SHIFT 0UL +#define FSR_CEXC_MASK (0x1fUL << FSR_CEXC_SHIFT) + static int do_one_mathemu(u32 insn, unsigned long *fsr, unsigned long *fregs); /* Unlike the Sparc64 version (which has a struct fpustate), we @@ -254,12 +262,85 @@ break; } /* Now empty the queue and clear the queue_not_empty flag */ - fpt->tss.fsr &= ~0x3000; + if(retcode) + fpt->tss.fsr &= ~(0x3000 | FSR_CEXC_MASK); + else + fpt->tss.fsr &= ~0x3000; fpt->tss.fpqdepth = 0; return retcode; } +/* All routines returning an exception to raise should detect + * such exceptions _before_ rounding to be consistant with + * the behavior of the hardware in the implemented cases + * (and thus with the recommendations in the V9 architecture + * manual). + * + * We return 0 if a SIGFPE should be sent, 1 otherwise. + */ +static int record_exception(unsigned long *pfsr, int eflag) +{ + unsigned long fsr = *pfsr; + int would_trap; + + /* Determine if this exception would have generated a trap. */ + would_trap = (fsr & ((long)eflag << FSR_TEM_SHIFT)) != 0UL; + + /* If trapping, we only want to signal one bit. */ + if(would_trap != 0) { + eflag &= ((fsr & FSR_TEM_MASK) >> FSR_TEM_SHIFT); + if((eflag & (eflag - 1)) != 0) { + if(eflag & EFLAG_INVALID) + eflag = EFLAG_INVALID; + else if(eflag & EFLAG_DIVZERO) + eflag = EFLAG_DIVZERO; + else if(eflag & EFLAG_INEXACT) + eflag = EFLAG_INEXACT; + } + } + + /* Set CEXC, here are the rules: + * + * 1) In general all FPU ops will set one and only one + * bit in the CEXC field, this is always the case + * when the IEEE exception trap is enabled in TEM. + * + * 2) As a special case, if an overflow or underflow + * is being signalled, AND the trap is not enabled + * in TEM, then the inexact field shall also be set. + */ + fsr &= ~(FSR_CEXC_MASK); + if(would_trap || + (eflag & (EFLAG_OVERFLOW | EFLAG_UNDERFLOW)) == 0) { + fsr |= ((long)eflag << FSR_CEXC_SHIFT); + } else { + fsr |= (((long)eflag << FSR_CEXC_SHIFT) | + (EFLAG_INEXACT << FSR_CEXC_SHIFT)); + } + + /* Set the AEXC field, rules are: + * + * 1) If a trap would not be generated, the + * CEXC just generated is OR'd into the + * existing value of AEXC. + * + * 2) When a trap is generated, AEXC is cleared. + */ + if(would_trap == 0) + fsr |= ((long)eflag << FSR_AEXC_SHIFT); + else + fsr &= ~(FSR_AEXC_MASK); + + /* If trapping, indicate fault trap type IEEE. */ + if(would_trap != 0) + fsr |= (1UL << 14); + + *pfsr = fsr; + + return (would_trap ? 0 : 1); +} + static int do_one_mathemu(u32 insn, unsigned long *fsr, unsigned long *fregs) { /* Emulate the given insn, updating fsr and fregs appropriately. */ @@ -270,7 +351,7 @@ * (this field not used on sparc32 code, as we can't * extract trap type info for ops on the FP queue) */ - int freg; + int freg, eflag; int (*func)(void *,void *,void *) = NULL; void *rs1 = NULL, *rs2 = NULL, *rd = NULL; @@ -411,6 +492,8 @@ #ifdef DEBUG_MATHEMU printk("executing insn...\n"); #endif - func(rd, rs2, rs1); /* do the Right Thing */ - return 1; /* success! */ + eflag = func(rd, rs2, rs1); /* do the Right Thing */ + if(eflag == 0) + return 1; /* success! */ + return record_exception(fsr, eflag); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/sfp-machine.h linux/arch/sparc/math-emu/sfp-machine.h --- v2.2.3/linux/arch/sparc/math-emu/sfp-machine.h Thu Apr 23 20:21:31 1998 +++ linux/arch/sparc/math-emu/sfp-machine.h Wed Mar 10 16:53:36 1999 @@ -115,16 +115,6 @@ X##_s = _flo->bits.sign; \ } while (0) -#define __FP_PACK_RAW_1(fs, val, X) \ - do { \ - union _FP_UNION_##fs *_flo = \ - (union _FP_UNION_##fs *)val; \ - \ - _flo->bits.frac = X##_f; \ - _flo->bits.exp = X##_e; \ - _flo->bits.sign = X##_s; \ - } while (0) - #define __FP_UNPACK_RAW_2(fs, X, val) \ do { \ union _FP_UNION_##fs *_flo = \ @@ -136,17 +126,6 @@ X##_s = _flo->bits.sign; \ } while (0) -#define __FP_PACK_RAW_2(fs, val, X) \ - do { \ - union _FP_UNION_##fs *_flo = \ - (union _FP_UNION_##fs *)val; \ - \ - _flo->bits.frac0 = X##_f0; \ - _flo->bits.frac1 = X##_f1; \ - _flo->bits.exp = X##_e; \ - _flo->bits.sign = X##_s; \ - } while (0) - #define __FP_UNPACK_RAW_4(fs, X, val) \ do { \ union _FP_UNION_##fs *_flo = \ @@ -160,55 +139,103 @@ X##_s = _flo->bits.sign; \ } while (0) -#define __FP_PACK_RAW_4(fs, val, X) \ - do { \ - union _FP_UNION_##fs *_flo = \ - (union _FP_UNION_##fs *)val; \ - \ - _flo->bits.frac0 = X##_f[0]; \ - _flo->bits.frac1 = X##_f[1]; \ - _flo->bits.frac2 = X##_f[2]; \ - _flo->bits.frac3 = X##_f[3]; \ - _flo->bits.exp = X##_e; \ - _flo->bits.sign = X##_s; \ - } while (0) - #define __FP_UNPACK_S(X,val) \ do { \ __FP_UNPACK_RAW_1(S,X,val); \ _FP_UNPACK_CANONICAL(S,1,X); \ } while (0) -#define __FP_PACK_S(val,X) \ - do { \ - _FP_PACK_CANONICAL(S,1,X); \ - __FP_PACK_RAW_1(S,val,X); \ - } while (0) - #define __FP_UNPACK_D(X,val) \ do { \ __FP_UNPACK_RAW_2(D,X,val); \ _FP_UNPACK_CANONICAL(D,2,X); \ } while (0) -#define __FP_PACK_D(val,X) \ - do { \ - _FP_PACK_CANONICAL(D,2,X); \ - __FP_PACK_RAW_2(D,val,X); \ - } while (0) - #define __FP_UNPACK_Q(X,val) \ do { \ __FP_UNPACK_RAW_4(Q,X,val); \ _FP_UNPACK_CANONICAL(Q,4,X); \ } while (0) -#define __FP_PACK_Q(val,X) \ - do { \ - _FP_PACK_CANONICAL(Q,4,X); \ - __FP_PACK_RAW_4(Q,val,X); \ +#define __FP_PACK_RAW_1(fs, val, X) \ + do { \ + union _FP_UNION_##fs *_flo = \ + (union _FP_UNION_##fs *)val; \ + \ + _flo->bits.frac = X##_f; \ + _flo->bits.exp = X##_e; \ + _flo->bits.sign = X##_s; \ + } while (0) + +#define __FP_PACK_RAW_2(fs, val, X) \ + do { \ + union _FP_UNION_##fs *_flo = \ + (union _FP_UNION_##fs *)val; \ + \ + _flo->bits.frac0 = X##_f0; \ + _flo->bits.frac1 = X##_f1; \ + _flo->bits.exp = X##_e; \ + _flo->bits.sign = X##_s; \ + } while (0) + +#define __FP_PACK_RAW_4(fs, val, X) \ + do { \ + union _FP_UNION_##fs *_flo = \ + (union _FP_UNION_##fs *)val; \ + \ + _flo->bits.frac0 = X##_f[0]; \ + _flo->bits.frac1 = X##_f[1]; \ + _flo->bits.frac2 = X##_f[2]; \ + _flo->bits.frac3 = X##_f[3]; \ + _flo->bits.exp = X##_e; \ + _flo->bits.sign = X##_s; \ } while (0) +#include +#include + +/* We only actually write to the destination register + * if exceptions signalled (if any) will not trap. + */ +#ifdef __SMP__ +#define __FPU_TEM \ + (((current->tss.fsr)>>23)&0x1f) +#else +extern struct task_struct *last_task_used_math; +#define __FPU_TEM \ + (((last_task_used_math->tss.fsr)>>23)&0x1f) +#endif +#define __FPU_TRAP_P(bits) \ + ((__FPU_TEM & (bits)) != 0) + +#define __FP_PACK_S(val,X) \ +({ int __exc = _FP_PACK_CANONICAL(S,1,X); \ + if(!__exc || !__FPU_TRAP_P(__exc)) \ + __FP_PACK_RAW_1(S,val,X); \ + __exc; \ +}) + +#define __FP_PACK_D(val,X) \ +({ int __exc = _FP_PACK_CANONICAL(D,2,X); \ + if(!__exc || !__FPU_TRAP_P(__exc)) \ + __FP_PACK_RAW_2(D,val,X); \ + __exc; \ +}) + +#define __FP_PACK_Q(val,X) \ +({ int __exc = _FP_PACK_CANONICAL(Q,4,X); \ + if(!__exc || !__FPU_TRAP_P(__exc)) \ + __FP_PACK_RAW_4(Q,val,X); \ + __exc; \ +}) + +/* Obtain the current rounding mode. */ +#ifdef __SMP__ +#define FP_ROUNDMODE ((current->tss.fsr >> 30) & 0x3) +#else +#define FP_ROUNDMODE ((last_task_used_math->tss.fsr >> 30) & 0x3) +#endif + /* the asm fragments go here: all these are taken from glibc-2.0.5's stdlib/longlong.h */ #include @@ -361,3 +388,9 @@ #define __BYTE_ORDER __LITTLE_ENDIAN #endif +/* Exception flags. */ +#define EFLAG_INVALID (1 << 4) +#define EFLAG_OVERFLOW (1 << 3) +#define EFLAG_UNDERFLOW (1 << 2) +#define EFLAG_DIVZERO (1 << 1) +#define EFLAG_INEXACT (1 << 0) diff -u --recursive --new-file v2.2.3/linux/arch/sparc/mm/Makefile linux/arch/sparc/mm/Makefile --- v2.2.3/linux/arch/sparc/mm/Makefile Tue Dec 22 14:16:54 1998 +++ linux/arch/sparc/mm/Makefile Wed Mar 10 16:53:36 1999 @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.32 1998/08/16 16:02:25 ecd Exp $ +# $Id: Makefile,v 1.33 1999/01/02 16:45:47 davem Exp $ # Makefile for the linux Sparc-specific parts of the memory manager. # # Note! Dependencies are done automagically by 'make dep', which also diff -u --recursive --new-file v2.2.3/linux/arch/sparc/mm/fault.c linux/arch/sparc/mm/fault.c --- v2.2.3/linux/arch/sparc/mm/fault.c Thu Nov 19 09:56:27 1998 +++ linux/arch/sparc/mm/fault.c Wed Mar 10 16:53:36 1999 @@ -1,4 +1,4 @@ -/* $Id: fault.c,v 1.96 1998/11/08 11:13:56 davem Exp $ +/* $Id: fault.c,v 1.101 1999/01/04 06:24:52 jj Exp $ * fault.c: Page fault handlers for the Sparc. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -13,11 +13,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -149,9 +151,7 @@ (unsigned long) tsk->mm->context); printk(KERN_ALERT "tsk->mm->pgd = %08lx\n", (unsigned long) tsk->mm->pgd); - lock_kernel(); die_if_kernel("Oops", regs); - unlock_kernel(); } asmlinkage int lookup_fault(unsigned long pc, unsigned long ret_pc, @@ -201,6 +201,13 @@ if(text_fault) address = regs->pc; + + /* + * If we're in an interrupt or have no user + * context, we must not take the fault.. + */ + if (in_interrupt() || mm == &init_mm) + goto do_kernel_fault; down(&mm->mmap_sem); /* The kernel referencing a bad kernel pointer can lock up diff -u --recursive --new-file v2.2.3/linux/arch/sparc/mm/init.c linux/arch/sparc/mm/init.c --- v2.2.3/linux/arch/sparc/mm/init.c Mon Oct 5 13:13:37 1998 +++ linux/arch/sparc/mm/init.c Sun Mar 21 07:23:38 1999 @@ -1,4 +1,4 @@ -/* $Id: init.c,v 1.60 1998/09/13 04:30:31 davem Exp $ +/* $Id: init.c,v 1.63 1999/03/20 22:02:01 davem Exp $ * linux/arch/sparc/mm/init.c * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -37,6 +37,8 @@ extern void show_net_buffers(void); +unsigned long *sparc_valid_addr_bitmap; + struct sparc_phys_banks sp_banks[SPARC_PHYS_BANKS]; unsigned long sparc_unmapped_base; @@ -215,16 +217,20 @@ unsigned long limit = base + sp_banks[tmp2].num_bytes; if((phys_addr >= base) && (phys_addr < limit) && - ((phys_addr + PAGE_SIZE) < limit)) + ((phys_addr + PAGE_SIZE) < limit)) { mem_map[MAP_NR(addr)].flags &= ~(1<> 8, sparc_valid_addr_bitmap); + } } } } else { if((sparc_cpu_model == sun4m) || (sparc_cpu_model == sun4d)) { srmmu_frob_mem_map(start_mem); } else { - for(addr = start_mem; addr < end_mem; addr += PAGE_SIZE) + for(addr = start_mem; addr < end_mem; addr += PAGE_SIZE) { mem_map[MAP_NR(addr)].flags &= ~(1<> 8, sparc_valid_addr_bitmap); + } } } } @@ -234,6 +240,7 @@ int codepages = 0; int datapages = 0; int initpages = 0; + int i; unsigned long addr; struct page *page, *end; @@ -243,6 +250,12 @@ end_mem &= PAGE_MASK; max_mapnr = MAP_NR(end_mem); high_memory = (void *) end_mem; + + sparc_valid_addr_bitmap = (unsigned long *)start_mem; + i = max_mapnr >> (8 + 5); + i += 1; + memset(sparc_valid_addr_bitmap, 0, i << 2); + start_mem += i << 2; start_mem = PAGE_ALIGN(start_mem); num_physpages = 0; @@ -255,6 +268,7 @@ else #endif mem_map[MAP_NR(addr)].flags |= (1<> 8, sparc_valid_addr_bitmap); addr += PAGE_SIZE; } @@ -266,6 +280,9 @@ if (PageSkip(page)) { unsigned long low, high; + /* See srmmu_frob_mem_map() for why this is done. -DaveM */ + page++; + low = PAGE_ALIGN((unsigned long)(page+1)); if (page->next_hash < page) high = ((unsigned long)end) & PAGE_MASK; @@ -313,11 +330,18 @@ initpages << (PAGE_SHIFT-10), (unsigned long)PAGE_OFFSET, end_mem); - freepages.min = nr_free_pages >> 7; - if(freepages.min < 16) - freepages.min = 16; - freepages.low = freepages.min + (freepages.min >> 1); - freepages.high = freepages.min + freepages.min; + /* NOTE NOTE NOTE NOTE + * Please keep track of things and make sure this + * always matches the code in mm/page_alloc.c -DaveM + */ + i = nr_free_pages >> 7; + if (i < 48) + i = 48; + if (i > 256) + i = 256; + freepages.min = i; + freepages.low = i << 1; + freepages.high = freepages.low + i; } void free_initmem (void) diff -u --recursive --new-file v2.2.3/linux/arch/sparc/mm/srmmu.c linux/arch/sparc/mm/srmmu.c --- v2.2.3/linux/arch/sparc/mm/srmmu.c Wed Jan 20 23:14:04 1999 +++ linux/arch/sparc/mm/srmmu.c Sun Mar 21 07:23:38 1999 @@ -1,4 +1,4 @@ -/* $Id: srmmu.c,v 1.175 1998/08/28 18:57:31 zaitcev Exp $ +/* $Id: srmmu.c,v 1.184 1999/03/20 22:02:03 davem Exp $ * srmmu.c: SRMMU specific routines for memory management. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -216,24 +217,36 @@ mem_map[MAP_NR(pg1)].flags &= ~(1< 2 * PAGE_SIZE) { + /* Making a one or two pages PG_skip holes + * is not necessary. We add one more because + * we must set the PG_skip flag on the first + * two mem_map[] entries for the hole. Go and + * see the mm/filemap.c:shrink_mmap() loop for + * details. -DaveM + */ + if (i && bank_start - bank_end > 3 * PAGE_SIZE) { mem_map[MAP_NR(bank_end)].flags |= (1< KERNBASE && bank_start < KERNBASE) { mem_map[0].flags |= (1<> 8, sparc_valid_addr_bitmap); if((bank_start >= KERNBASE) && (bank_start < start_mem)) { bank_start += PAGE_SIZE; @@ -250,14 +263,19 @@ if (bank_end < KERNBASE) { mem_map[MAP_NR(bank_end)].flags |= (1<mm->context != NO_CONTEXT) { + if(tsk->mm->context != NO_CONTEXT && + tsk->mm->pgd != pgdp) { flush_cache_mm(tsk->mm); ctxd_set(&srmmu_context_table[tsk->mm->context], pgdp); flush_tlb_mm(tsk->mm); @@ -798,13 +817,19 @@ static void srmmu_switch_to_context(struct task_struct *tsk) { + int set = 0; + if(tsk->mm->context == NO_CONTEXT) { alloc_context(tsk->mm); flush_cache_mm(tsk->mm); ctxd_set(&srmmu_context_table[tsk->mm->context], tsk->mm->pgd); flush_tlb_mm(tsk->mm); - } - srmmu_set_context(tsk->mm->context); + set = 1; + } else if(tsk->mm != current->mm) + set = 1; + + if(set != 0) + srmmu_set_context(tsk->mm->context); } static void srmmu_init_new_context(struct mm_struct *mm) @@ -1273,6 +1298,12 @@ unsigned long end); extern void viking_flush_tlb_page(struct vm_area_struct *vma, unsigned long page); +extern void sun4dsmp_flush_tlb_all(void); +extern void sun4dsmp_flush_tlb_mm(struct mm_struct *mm); +extern void sun4dsmp_flush_tlb_range(struct mm_struct *mm, unsigned long start, + unsigned long end); +extern void sun4dsmp_flush_tlb_page(struct vm_area_struct *vma, + unsigned long page); /* hypersparc.S */ extern void hypersparc_flush_cache_all(void); @@ -1311,7 +1342,8 @@ if(pgdp != swapper_pg_dir) hypersparc_flush_page_to_ram(page); - if(tsk->mm->context != NO_CONTEXT) { + if(tsk->mm->context != NO_CONTEXT && + tsk->mm->pgd != pgdp) { flush_cache_mm(tsk->mm); ctxd_set(&srmmu_context_table[tsk->mm->context], pgdp); flush_tlb_mm(tsk->mm); @@ -1320,11 +1352,13 @@ static void viking_update_rootmmu_dir(struct task_struct *tsk, pgd_t *pgdp) { - viking_flush_page((unsigned long)pgdp); - if(tsk->mm->context != NO_CONTEXT) { - flush_cache_mm(current->mm); + if(pgdp != swapper_pg_dir) + viking_flush_page((unsigned long)pgdp); + if(tsk->mm->context != NO_CONTEXT && + tsk->mm->pgd != pgdp) { + flush_cache_mm(tsk->mm); ctxd_set(&srmmu_context_table[tsk->mm->context], pgdp); - flush_tlb_mm(current->mm); + flush_tlb_mm(tsk->mm); } } @@ -1334,6 +1368,9 @@ unsigned long page = ((unsigned long) pgdp) & PAGE_MASK; unsigned long line; + if(pgdp == swapper_pg_dir) + goto skip_flush; + a = 0x20; b = 0x40; c = 0x60; d = 0x80; e = 0xa0; f = 0xc0; g = 0xe0; page &= PAGE_MASK; line = (page + PAGE_SIZE) - 0x100; @@ -1354,16 +1391,19 @@ "r" (a), "r" (b), "r" (c), "r" (d), "r" (e), "r" (f), "r" (g)); } while(line != page); - - if(tsk->mm->context != NO_CONTEXT) { - flush_cache_mm(current->mm); +skip_flush: + if(tsk->mm->context != NO_CONTEXT && + tsk->mm->pgd != pgdp) { + flush_cache_mm(tsk->mm); ctxd_set(&srmmu_context_table[tsk->mm->context], pgdp); - flush_tlb_mm(current->mm); + flush_tlb_mm(tsk->mm); } } static void hypersparc_switch_to_context(struct task_struct *tsk) { + int set = 0; + if(tsk->mm->context == NO_CONTEXT) { ctxd_t *ctxp; @@ -1371,9 +1411,14 @@ ctxp = &srmmu_context_table[tsk->mm->context]; srmmu_set_entry((pte_t *)ctxp, __pte((SRMMU_ET_PTD | (srmmu_v2p((unsigned long) tsk->mm->pgd) >> 4)))); hypersparc_flush_page_to_ram((unsigned long)ctxp); + set = 1; + } else if(tsk->mm != current->mm) + set = 1; + + if(set != 0) { + hyper_flush_whole_icache(); + srmmu_set_context(tsk->mm->context); } - hyper_flush_whole_icache(); - srmmu_set_context(tsk->mm->context); } static void hypersparc_init_new_context(struct mm_struct *mm) @@ -1386,9 +1431,10 @@ srmmu_set_entry((pte_t *)ctxp, __pte((SRMMU_ET_PTD | (srmmu_v2p((unsigned long) mm->pgd) >> 4)))); hypersparc_flush_page_to_ram((unsigned long)ctxp); - hyper_flush_whole_icache(); - if(mm == current->mm) + if(mm == current->mm) { + hyper_flush_whole_icache(); srmmu_set_context(mm->context); + } } static unsigned long mempool; @@ -1998,6 +2044,11 @@ static void srmmu_destroy_context(struct mm_struct *mm) { if(mm->context != NO_CONTEXT && atomic_read(&mm->count) == 1) { + /* XXX This could be drastically improved. + * XXX We are only called from __exit_mm and it just did + * XXX cache/tlb mm flush and right after this will (re-) + * XXX SET_PAGE_DIR to swapper_pg_dir. -DaveM + */ flush_cache_mm(mm); ctxd_set(&srmmu_context_table[mm->context], swapper_pg_dir); flush_tlb_mm(mm); @@ -2028,8 +2079,11 @@ offset = (address & PAGE_MASK) - vma->vm_start; vmaring = inode->i_mmap; do { - vaddr = vmaring->vm_start + offset; + /* Do not mistake ourselves as another mapping. */ + if(vmaring == vma) + continue; + vaddr = vmaring->vm_start + offset; if ((vaddr ^ address) & vac_badbits) { alias_found++; start = vmaring->vm_start; @@ -2042,7 +2096,7 @@ if(!ptep) goto next; if((pte_val(*ptep) & SRMMU_ET_MASK) == SRMMU_VALID) { -#if 1 +#if 0 printk("Fixing USER/USER alias [%ld:%08lx]\n", vmaring->vm_mm->context, start); #endif @@ -2057,11 +2111,12 @@ } } while ((vmaring = vmaring->vm_next_share) != NULL); - if(alias_found && !(pte_val(pte) & _SUN4C_PAGE_NOCACHE)) { + if(alias_found && ((pte_val(pte) & SRMMU_CACHE) != 0)) { pgdp = srmmu_pgd_offset(vma->vm_mm, address); - ptep = srmmu_pte_offset((pmd_t *) pgdp, address); + pmdp = srmmu_pmd_offset(pgdp, address); + ptep = srmmu_pte_offset(pmdp, address); flush_cache_page(vma, address); - *ptep = __pte(pte_val(*ptep) | _SUN4C_PAGE_NOCACHE); + set_pte(ptep, __pte((pte_val(*ptep) & ~SRMMU_CACHE))); flush_tlb_page(vma, address); } done: @@ -2652,15 +2707,8 @@ /* Ahhh, the viking. SRMMU VLSI abortion number two... */ if(mreg & VIKING_MMODE) { - unsigned long bpreg; - srmmu_name = "TI Viking"; viking_mxcc_present = 0; - - bpreg = viking_get_bpreg(); - bpreg &= ~(VIKING_ACTION_MIX); - viking_set_bpreg(bpreg); - msi_set_sync(); BTFIXUPSET_CALL(set_pte, srmmu_set_pte_nocache_viking, BTFIXUPCALL_NORM); @@ -2697,10 +2745,20 @@ BTFIXUPSET_CALL(flush_cache_page, viking_flush_cache_page, BTFIXUPCALL_NOP); BTFIXUPSET_CALL(flush_cache_range, viking_flush_cache_range, BTFIXUPCALL_NOP); - BTFIXUPSET_CALL(flush_tlb_all, viking_flush_tlb_all, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(flush_tlb_mm, viking_flush_tlb_mm, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(flush_tlb_page, viking_flush_tlb_page, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(flush_tlb_range, viking_flush_tlb_range, BTFIXUPCALL_NORM); +#ifdef __SMP__ + if (sparc_cpu_model == sun4d) { + BTFIXUPSET_CALL(flush_tlb_all, sun4dsmp_flush_tlb_all, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_mm, sun4dsmp_flush_tlb_mm, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_page, sun4dsmp_flush_tlb_page, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_range, sun4dsmp_flush_tlb_range, BTFIXUPCALL_NORM); + } else +#endif + { + BTFIXUPSET_CALL(flush_tlb_all, viking_flush_tlb_all, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_mm, viking_flush_tlb_mm, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_page, viking_flush_tlb_page, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_range, viking_flush_tlb_range, BTFIXUPCALL_NORM); + } BTFIXUPSET_CALL(flush_page_to_ram, viking_flush_page_to_ram, BTFIXUPCALL_NOP); BTFIXUPSET_CALL(flush_sig_insns, viking_flush_sig_insns, BTFIXUPCALL_NOP); @@ -3027,10 +3085,12 @@ BTFIXUPSET_CALL(flush_cache_mm, smp_flush_cache_mm, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(flush_cache_range, smp_flush_cache_range, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(flush_cache_page, smp_flush_cache_page, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(flush_tlb_all, smp_flush_tlb_all, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(flush_tlb_mm, smp_flush_tlb_mm, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(flush_tlb_range, smp_flush_tlb_range, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(flush_tlb_page, smp_flush_tlb_page, BTFIXUPCALL_NORM); + if (sparc_cpu_model != sun4d) { + BTFIXUPSET_CALL(flush_tlb_all, smp_flush_tlb_all, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_mm, smp_flush_tlb_mm, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_range, smp_flush_tlb_range, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_tlb_page, smp_flush_tlb_page, BTFIXUPCALL_NORM); + } BTFIXUPSET_CALL(flush_page_to_ram, smp_flush_page_to_ram, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(flush_sig_insns, smp_flush_sig_insns, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(flush_page_for_dma, smp_flush_page_for_dma, BTFIXUPCALL_NORM); diff -u --recursive --new-file v2.2.3/linux/arch/sparc/mm/sun4c.c linux/arch/sparc/mm/sun4c.c --- v2.2.3/linux/arch/sparc/mm/sun4c.c Mon Oct 5 13:13:37 1998 +++ linux/arch/sparc/mm/sun4c.c Wed Mar 10 16:53:36 1999 @@ -1,4 +1,4 @@ -/* $Id: sun4c.c,v 1.171 1998/09/21 05:05:41 jj Exp $ +/* $Id: sun4c.c,v 1.173 1999/01/17 02:20:37 davem Exp $ * sun4c.c: Doing in software what should be done in hardware. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -2688,6 +2688,10 @@ unsigned long vaddr = vmaring->vm_start + offset; unsigned long start; + /* Do not mistake ourselves as another mapping. */ + if(vmaring == vma) + continue; + if (S4CVAC_BADALIAS(vaddr, address)) { alias_found++; start = vmaring->vm_start; @@ -2699,8 +2703,8 @@ if(pte_val(*ptep) & _SUN4C_PAGE_PRESENT) { flush_cache_page(vmaring, start); - pte_val(*ptep) = (pte_val(*ptep) | - _SUN4C_PAGE_NOCACHE); + *ptep = __pte(pte_val(*ptep) | + _SUN4C_PAGE_NOCACHE); flush_tlb_page(vmaring, start); } next: @@ -2712,7 +2716,7 @@ if(alias_found && !(pte_val(pte) & _SUN4C_PAGE_NOCACHE)) { pgdp = sun4c_pgd_offset(vma->vm_mm, address); ptep = sun4c_pte_offset((pmd_t *) pgdp, address); - pte_val(*ptep) = (pte_val(*ptep) | _SUN4C_PAGE_NOCACHE); + *ptep = __pte(pte_val(*ptep) | _SUN4C_PAGE_NOCACHE); pte = pte_val(*ptep); } } diff -u --recursive --new-file v2.2.3/linux/arch/sparc/mm/viking.S linux/arch/sparc/mm/viking.S --- v2.2.3/linux/arch/sparc/mm/viking.S Thu Apr 23 20:21:31 1998 +++ linux/arch/sparc/mm/viking.S Wed Mar 10 16:53:36 1999 @@ -1,8 +1,9 @@ -/* $Id: viking.S,v 1.11 1998/02/20 18:07:50 jj Exp $ +/* $Id: viking.S,v 1.12 1999/02/23 13:23:50 jj Exp $ * viking.S: High speed Viking cache/mmu operations * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) - * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997,1998,1999 Jakub Jelinek (jj@ultra.linux.cz) + * Copyright (C) 1999 Pavel Semerad (semerad@ss1000.ms.mff.cuni.cz) */ #include @@ -15,16 +16,12 @@ #include #include -#define WINDOW_FLUSH(tmp1, tmp2) \ - mov 0, tmp1; \ -98: ld [%g6 + AOFF_task_tss + AOFF_thread_uwinmask], tmp2; \ - orcc %g0, tmp2, %g0; \ - add tmp1, 1, tmp1; \ - bne 98b; \ - save %sp, -64, %sp; \ -99: subcc tmp1, 1, tmp1; \ - bne 99b; \ - restore %g0, %g0, %g0; +#ifdef __SMP__ + .data + .align 4 +sun4dsmp_flush_tlb_spin: + .word 0 +#endif .text .align 4 @@ -176,8 +173,10 @@ sta %g0, [%g2] ASI_M_FLUSH_PROBE retl sta %g5, [%g1] ASI_M_MMUREGS +#ifndef __SMP__ 1: retl nop +#endif viking_flush_tlb_range: mov SRMMU_CTX_REG, %g1 @@ -198,8 +197,10 @@ sta %g0, [%o1] ASI_M_FLUSH_PROBE retl sta %g5, [%g1] ASI_M_MMUREGS +#ifndef __SMP__ 2: retl nop +#endif viking_flush_tlb_page: ld [%o0 + 0x00], %o0 /* XXX vma->vm_mm GROSS XXX */ @@ -215,11 +216,96 @@ sta %g0, [%o1] ASI_M_FLUSH_PROBE retl sta %g5, [%g1] ASI_M_MMUREGS +#ifndef __SMP__ 1: retl nop +#endif viking_flush_page_to_ram: viking_flush_page_for_dma: viking_flush_sig_insns: retl nop + +#ifdef __SMP__ + .globl sun4dsmp_flush_tlb_all, sun4dsmp_flush_tlb_mm + .globl sun4dsmp_flush_tlb_range, sun4dsmp_flush_tlb_page +sun4dsmp_flush_tlb_all: + sethi %hi(sun4dsmp_flush_tlb_spin), %g3 +1: ldstub [%g3 + %lo(sun4dsmp_flush_tlb_spin)], %g5 + tst %g5 + bne 2f + mov 0x400, %g1 + sta %g0, [%g1] ASI_M_FLUSH_PROBE + retl + stb %g0, [%g3 + %lo(sun4dsmp_flush_tlb_spin)] +2: tst %g5 + bne,a 2b + ldub [%g3 + %lo(sun4dsmp_flush_tlb_spin)], %g5 + b,a 1b + +sun4dsmp_flush_tlb_mm: + sethi %hi(sun4dsmp_flush_tlb_spin), %g3 +1: ldstub [%g3 + %lo(sun4dsmp_flush_tlb_spin)], %g5 + tst %g5 + bne 2f + mov SRMMU_CTX_REG, %g1 + ld [%o0 + AOFF_mm_context], %o1 + lda [%g1] ASI_M_MMUREGS, %g5 + mov 0x300, %g2 + sta %o1, [%g1] ASI_M_MMUREGS + sta %g0, [%g2] ASI_M_FLUSH_PROBE + sta %g5, [%g1] ASI_M_MMUREGS + retl + stb %g0, [%g3 + %lo(sun4dsmp_flush_tlb_spin)] +2: tst %g5 + bne,a 2b + ldub [%g3 + %lo(sun4dsmp_flush_tlb_spin)], %g5 + b,a 1b + +sun4dsmp_flush_tlb_range: + sethi %hi(sun4dsmp_flush_tlb_spin), %g3 +1: ldstub [%g3 + %lo(sun4dsmp_flush_tlb_spin)], %g5 + tst %g5 + bne 3f + mov SRMMU_CTX_REG, %g1 + ld [%o0 + AOFF_mm_context], %o3 + lda [%g1] ASI_M_MMUREGS, %g5 + sethi %hi(~((1 << SRMMU_PGDIR_SHIFT) - 1)), %o4 + sta %o3, [%g1] ASI_M_MMUREGS + and %o1, %o4, %o1 + add %o1, 0x200, %o1 + sta %g0, [%o1] ASI_M_FLUSH_PROBE +2: sub %o1, %o4, %o1 + cmp %o1, %o2 + blu,a 2b + sta %g0, [%o1] ASI_M_FLUSH_PROBE + sta %g5, [%g1] ASI_M_MMUREGS + retl + stb %g0, [%g3 + %lo(sun4dsmp_flush_tlb_spin)] +3: tst %g5 + bne,a 3b + ldub [%g3 + %lo(sun4dsmp_flush_tlb_spin)], %g5 + b,a 1b + +sun4dsmp_flush_tlb_page: + sethi %hi(sun4dsmp_flush_tlb_spin), %g3 +1: ldstub [%g3 + %lo(sun4dsmp_flush_tlb_spin)], %g5 + tst %g5 + bne 2f + mov SRMMU_CTX_REG, %g1 + ld [%o0 + 0x00], %o0 /* XXX vma->vm_mm GROSS XXX */ + ld [%o0 + AOFF_mm_context], %o3 + lda [%g1] ASI_M_MMUREGS, %g5 + and %o1, PAGE_MASK, %o1 + sta %o3, [%g1] ASI_M_MMUREGS + sta %g0, [%o1] ASI_M_FLUSH_PROBE + sta %g5, [%g1] ASI_M_MMUREGS + retl + stb %g0, [%g3 + %lo(sun4dsmp_flush_tlb_spin)] +2: tst %g5 + bne,a 2b + ldub [%g3 + %lo(sun4dsmp_flush_tlb_spin)], %g5 + b,a 1b + nop +#endif diff -u --recursive --new-file v2.2.3/linux/arch/sparc/vmlinux.lds linux/arch/sparc/vmlinux.lds --- v2.2.3/linux/arch/sparc/vmlinux.lds Mon Oct 5 13:13:37 1998 +++ linux/arch/sparc/vmlinux.lds Wed Mar 10 16:53:36 1999 @@ -31,6 +31,10 @@ __start___ksymtab = .; __ksymtab : { *(__ksymtab) } __stop___ksymtab = .; + + . = ALIGN(32); + .data.cacheline_aligned : { *(.data.cacheline_aligned) } + . = ALIGN(4096); __init_begin = .; .text.init : { *(.text.init) } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/Makefile linux/arch/sparc64/Makefile --- v2.2.3/linux/arch/sparc64/Makefile Tue Dec 22 14:16:54 1998 +++ linux/arch/sparc64/Makefile Wed Mar 10 16:53:36 1999 @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.33 1998/10/19 07:04:02 jj Exp $ +# $Id: Makefile,v 1.35 1999/01/02 16:45:50 davem Exp $ # sparc64/Makefile # # Makefile for the architecture dependent flags and dependencies on the @@ -46,6 +46,7 @@ else CFLAGS := $(CFLAGS) -m64 -pipe -mno-fpu -mcpu=ultrasparc -mcmodel=medlow \ -ffixed-g4 -fcall-used-g5 -fcall-used-g7 -Wno-sign-compare + AFLAGS += -m64 -mcpu=ultrasparc endif # Uncomment this to get spinlock/rwlock debugging on SMP. diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/config.in linux/arch/sparc64/config.in --- v2.2.3/linux/arch/sparc64/config.in Tue Jan 19 11:32:51 1999 +++ linux/arch/sparc64/config.in Mon Mar 15 16:10:43 1999 @@ -1,8 +1,8 @@ -# $Id: config.in,v 1.58 1998/11/16 04:47:30 davem Exp $ +# $Id: config.in,v 1.65 1999/03/14 03:12:48 anton Exp $ # For a description of the syntax of this configuration file, # see the Configure script. # -mainmenu_name "Linux/SPARC Kernel Configuration" +mainmenu_name "Linux/UltraSPARC Kernel Configuration" mainmenu_option next_comment comment 'Code maturity level options' @@ -24,7 +24,6 @@ define_bool CONFIG_VT y define_bool CONFIG_VT_CONSOLE y -bool 'Support for AP1000 multicomputer' CONFIG_AP1000 bool 'Symmetric multi-processing support' CONFIG_SMP mainmenu_option next_comment @@ -34,28 +33,20 @@ source drivers/video/Config.in endmenu -if [ "$CONFIG_AP1000" = "y" ]; then - define_bool CONFIG_NO_KEYBOARD y - define_bool CONFIG_APFDDI y - define_bool CONFIG_APBLOCK y - define_bool CONFIG_APBIF y - tristate 'OPIU DDV Driver' CONFIG_DDV -else - # Global things across all Sun machines. - define_bool CONFIG_SBUS y - define_bool CONFIG_SBUSCHAR y - define_bool CONFIG_SUN_MOUSE y - define_bool CONFIG_SERIAL y - define_bool CONFIG_SUN_SERIAL y - define_bool CONFIG_SERIAL_CONSOLE y - define_bool CONFIG_SUN_KEYBOARD y - define_bool CONFIG_SUN_CONSOLE y - define_bool CONFIG_SUN_AUXIO y - define_bool CONFIG_SUN_IO y - bool 'PCI support' CONFIG_PCI - source drivers/sbus/char/Config.in - source drivers/sbus/audio/Config.in -fi +# Global things across all Sun machines. +define_bool CONFIG_SBUS y +define_bool CONFIG_SBUSCHAR y +define_bool CONFIG_SUN_MOUSE y +define_bool CONFIG_SERIAL y +define_bool CONFIG_SUN_SERIAL y +define_bool CONFIG_SERIAL_CONSOLE y +define_bool CONFIG_SUN_KEYBOARD y +define_bool CONFIG_SUN_CONSOLE y +define_bool CONFIG_SUN_AUXIO y +define_bool CONFIG_SUN_IO y +bool 'PCI support' CONFIG_PCI +source drivers/sbus/char/Config.in +source drivers/sbus/audio/Config.in tristate 'Openprom tree appears in /proc/openprom (EXPERIMENTAL)' CONFIG_SUN_OPENPROMFS if [ "$CONFIG_PCI" = "y" ]; then @@ -221,14 +212,14 @@ fi bool 'Sun LANCE support' CONFIG_SUNLANCE tristate 'Sun Happy Meal 10/100baseT support' CONFIG_HAPPYMEAL + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate 'Sun BigMAC 10/100baseT support' CONFIG_SUNBMAC + fi tristate 'Sun QuadEthernet support' CONFIG_SUNQE tristate 'MyriCOM Gigabit Ethernet support' CONFIG_MYRI_SBUS if [ "$CONFIG_PCI" = "y" ]; then tristate 'Generic DECchip & DIGITAL EtherWORKS PCI/EISA' CONFIG_DE4X5 -# Turned off until updated 3c59x.c driver -# gets approved by Linus... --DAVEM -# -# tristate '3c590/3c900 series (592/595/597) "Vortex/Boomerang" support' CONFIG_VORTEX + tristate '3c590/3c900 series (592/595/597) "Vortex/Boomerang" support' CONFIG_VORTEX fi # bool 'FDDI driver support' CONFIG_FDDI # if [ "$CONFIG_FDDI" = "y" ]; then @@ -236,6 +227,15 @@ fi endmenu fi + +# This one must be before the filesystem configs. -DaveM +mainmenu_option next_comment +comment 'Unix 98 PTY support' +bool 'Unix98 PTY support' CONFIG_UNIX98_PTYS +if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then + int 'Maximum number of Unix98 PTYs in use (0-2048)' CONFIG_UNIX98_PTY_COUNT 256 +fi +endmenu source fs/Config.in diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/defconfig linux/arch/sparc64/defconfig --- v2.2.3/linux/arch/sparc64/defconfig Wed Mar 10 15:29:45 1999 +++ linux/arch/sparc64/defconfig Sun Mar 21 07:23:38 1999 @@ -19,7 +19,6 @@ # CONFIG_VT=y CONFIG_VT_CONSOLE=y -# CONFIG_AP1000 is not set # CONFIG_SMP is not set # @@ -28,7 +27,9 @@ CONFIG_PROM_CONSOLE=y CONFIG_FB=y CONFIG_DUMMY_CONSOLE=y +# CONFIG_FB_PM2 is not set # CONFIG_FB_MATROX is not set +CONFIG_FB_ATY=y CONFIG_FB_SBUS=y CONFIG_FB_CREATOR=y CONFIG_FB_CGSIX=y @@ -71,9 +72,9 @@ # # Linux/SPARC audio subsystem (EXPERIMENTAL) # -# CONFIG_SPARCAUDIO is not set +CONFIG_SPARCAUDIO=y # CONFIG_SPARCAUDIO_AMD7930 is not set -# CONFIG_SPARCAUDIO_CS4231 is not set +CONFIG_SPARCAUDIO_CS4231=y # CONFIG_SPARCAUDIO_DBRI is not set # CONFIG_SPARCAUDIO_DUMMY is not set CONFIG_SUN_OPENPROMFS=m @@ -144,7 +145,6 @@ # (it is safe to leave these untouched) # CONFIG_INET_RARP=m -CONFIG_IP_NOSR=y CONFIG_SKB_LARGE=y CONFIG_IPV6=m # CONFIG_IPV6_EUI64 is not set @@ -165,6 +165,10 @@ # CONFIG_NET_FASTROUTE is not set # CONFIG_NET_HW_FLOWCONTROL is not set # CONFIG_CPU_IS_SLOW is not set + +# +# QoS and/or fair queueing +# # CONFIG_NET_SCHED is not set # @@ -214,11 +218,13 @@ # FC4 drivers # CONFIG_FC4_SOC=m +CONFIG_FC4_SOCAL=m # # FC4 targets # CONFIG_SCSI_PLUTO=m +CONFIG_SCSI_FCAL=m # # Network device support @@ -236,29 +242,53 @@ # CONFIG_SLIP_MODE_SLIP6 is not set CONFIG_SUNLANCE=y CONFIG_HAPPYMEAL=y +CONFIG_SUNBMAC=m CONFIG_SUNQE=m CONFIG_MYRI_SBUS=m CONFIG_DE4X5=m +CONFIG_VORTEX=m + +# +# Unix 98 PTY support +# +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 # # Filesystems # # CONFIG_QUOTA is not set -CONFIG_MINIX_FS=m -CONFIG_EXT2_FS=y -CONFIG_ISO9660_FS=m -# CONFIG_JOLIET is not set +CONFIG_AUTOFS_FS=m +# CONFIG_ADFS_FS is not set +CONFIG_AFFS_FS=m +# CONFIG_HFS_FS is not set CONFIG_FAT_FS=m CONFIG_MSDOS_FS=m # CONFIG_UMSDOS_FS is not set CONFIG_VFAT_FS=m +CONFIG_ISO9660_FS=m +# CONFIG_JOLIET is not set +CONFIG_MINIX_FS=m +# CONFIG_NTFS_FS is not set +CONFIG_HPFS_FS=m CONFIG_PROC_FS=y +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +CONFIG_ROMFS_FS=m +CONFIG_EXT2_FS=y +CONFIG_SYSV_FS=m +CONFIG_UFS_FS=m +CONFIG_UFS_FS_WRITE=y + +# +# Network File Systems +# +CONFIG_CODA_FS=m CONFIG_NFS_FS=y CONFIG_NFSD=m # CONFIG_NFSD_SUN is not set CONFIG_SUNRPC=y CONFIG_LOCKD=y -CONFIG_CODA_FS=m CONFIG_SMB_FS=m CONFIG_SMB_WIN95=y CONFIG_NCP_FS=m @@ -268,21 +298,16 @@ # CONFIG_NCPFS_NFS_NS is not set # CONFIG_NCPFS_OS2_NS is not set # CONFIG_NCPFS_MOUNT_SUBDIR is not set -CONFIG_HPFS_FS=m -# CONFIG_NTFS_FS is not set -CONFIG_SYSV_FS=m -CONFIG_AFFS_FS=m -# CONFIG_HFS_FS is not set -CONFIG_ROMFS_FS=m -CONFIG_AUTOFS_FS=m -CONFIG_AMIGA_PARTITION=y -CONFIG_UFS_FS=m + +# +# Partition Types +# CONFIG_BSD_DISKLABEL=y +# CONFIG_MAC_PARTITION is not set CONFIG_SMD_DISKLABEL=y CONFIG_SOLARIS_X86_PARTITION=y -# CONFIG_ADFS_FS is not set -# CONFIG_QNX4FS_FS is not set -# CONFIG_MAC_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +CONFIG_AMIGA_PARTITION=y CONFIG_NLS=y # @@ -313,6 +338,7 @@ # CONFIG_NLS_ISO8859_7 is not set # CONFIG_NLS_ISO8859_8 is not set # CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_15 is not set # CONFIG_NLS_KOI8_R is not set # diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/Makefile linux/arch/sparc64/kernel/Makefile --- v2.2.3/linux/arch/sparc64/kernel/Makefile Tue Dec 22 14:16:54 1998 +++ linux/arch/sparc64/kernel/Makefile Wed Mar 10 16:53:37 1999 @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.41 1998/10/11 06:58:14 davem Exp $ +# $Id: Makefile,v 1.43 1999/01/02 16:45:53 davem Exp $ # Makefile for the linux kernel. # # Note! Dependencies are done automagically by 'make dep', which also @@ -20,7 +20,7 @@ traps.o devices.o auxio.o ioport.o \ irq.o ptrace.o time.o sys_sparc.o signal.o \ unaligned.o sys_sunos32.o sunos_ioctl32.o \ - central.o psycho.o + central.o psycho.o starfire.o OX_OBJS := sparc64_ksyms.o ifdef CONFIG_PCI diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/binfmt_aout32.c linux/arch/sparc64/kernel/binfmt_aout32.c --- v2.2.3/linux/arch/sparc64/kernel/binfmt_aout32.c Thu Nov 12 16:21:18 1998 +++ linux/arch/sparc64/kernel/binfmt_aout32.c Sun Mar 21 07:23:38 1999 @@ -9,7 +9,6 @@ #include -#include #include #include #include @@ -18,9 +17,10 @@ #include #include #include +#include +#include #include #include -#include #include #include #include @@ -58,14 +58,13 @@ * macros to write out all the necessary info. */ #define DUMP_WRITE(addr,nr) \ -while (file.f_op->write(&file,(char *)(addr),(nr),&file.f_pos) != (nr)) \ - goto close_coredump +while (file->f_op->write(file,(char *)(addr),(nr),&file->f_pos) != (nr)) goto close_coredump #define DUMP_SEEK(offset) \ -if (file.f_op->llseek) { \ - if (file.f_op->llseek(&file,(offset),0) != (offset)) \ +if (file->f_op->llseek) { \ + if (file->f_op->llseek(file,(offset),0) != (offset)) \ goto close_coredump; \ -} else file.f_pos = (offset) +} else file->f_pos = (offset) /* * Routine writes a core dump image in the current directory. @@ -82,7 +81,7 @@ { struct dentry * dentry = NULL; struct inode * inode = NULL; - struct file file; + struct file * file; mm_segment_t fs; int has_dumped = 0; char corefile[6+sizeof(current->comm)]; @@ -106,29 +105,16 @@ #else corefile[4] = '\0'; #endif - dentry = open_namei(corefile,O_CREAT | 2 | O_TRUNC | O_NOFOLLOW, 0600); - if (IS_ERR(dentry)) { - dentry = NULL; + file = filp_open(corefile,O_CREAT | 2 | O_TRUNC | O_NOFOLLOW, 0600); + if (IS_ERR(file)) goto end_coredump; - } + dentry = file->f_dentry; inode = dentry->d_inode; if (!S_ISREG(inode->i_mode)) - goto end_coredump; + goto close_coredump; if (!inode->i_op || !inode->i_op->default_file_ops) - goto end_coredump; - if (get_write_access(inode)) - goto end_coredump; - file.f_mode = 3; - file.f_flags = 0; - file.f_count = 1; - file.f_dentry = dentry; - file.f_pos = 0; - file.f_reada = 0; - file.f_op = inode->i_op->default_file_ops; - if (file.f_op->open) - if (file.f_op->open(inode,&file)) - goto done_coredump; - if (!file.f_op->write) + goto close_coredump; + if (!file->f_op->write) goto close_coredump; has_dumped = 1; current->flags |= PF_DUMPCORE; @@ -175,13 +161,9 @@ set_fs(KERNEL_DS); DUMP_WRITE(current,sizeof(*current)); close_coredump: - if (file.f_op->release) - file.f_op->release(inode,&file); -done_coredump: - put_write_access(inode); + close_fp(file, NULL); end_coredump: set_fs(fs); - dput(dentry); return has_dumped; } @@ -269,7 +251,6 @@ return -ENOEXEC; } - current->personality = PER_LINUX; fd_offset = N_TXTOFF(ex); /* Check initial limits. This avoids letting people circumvent @@ -288,6 +269,8 @@ return retval; /* OK, This is the point of no return */ + current->personality = PER_LINUX; + current->mm->end_code = ex.a_text + (current->mm->start_code = N_TXTADDR(ex)); current->mm->end_data = ex.a_data + @@ -297,8 +280,7 @@ current->mm->rss = 0; current->mm->mmap = NULL; - current->suid = current->euid = current->fsuid = bprm->e_uid; - current->sgid = current->egid = current->fsgid = bprm->e_gid; + compute_creds(bprm); current->flags &= ~PF_FORKNOEXEC; if (N_MAGIC(ex) == NMAGIC) { /* Fuck me plenty... */ @@ -404,48 +386,44 @@ do_load_aout32_library(int fd) { struct file * file; - struct exec ex; - struct dentry * dentry; struct inode * inode; - unsigned int len; - unsigned int bss; - unsigned int start_addr; + unsigned long bss, start_addr, len; unsigned long error; + int retval; + loff_t offset = 0; + struct exec ex; - file = fcheck(fd); - - if (!file || !file->f_op) - return -EACCES; - - dentry = file->f_dentry; - inode = dentry->d_inode; - - /* Seek into the file */ - if (file->f_op->llseek) { - if ((error = file->f_op->llseek(file, 0, 0)) != 0) - return -ENOEXEC; - } else - file->f_pos = 0; + retval = -EACCES; + file = fget(fd); + if (!file) + goto out; + if (!file->f_op) + goto out_putf; + inode = file->f_dentry->d_inode; + retval = -ENOEXEC; + /* N.B. Save current fs? */ set_fs(KERNEL_DS); - error = file->f_op->read(file, (char *) &ex, sizeof(ex), &file->f_pos); + error = file->f_op->read(file, (char *) &ex, sizeof(ex), &offset); set_fs(USER_DS); if (error != sizeof(ex)) - return -ENOEXEC; + goto out_putf; /* We come in here for the regular a.out style of shared libraries */ if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != QMAGIC) || N_TRSIZE(ex) || N_DRSIZE(ex) || ((ex.a_entry & 0xfff) && N_MAGIC(ex) == ZMAGIC) || inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) { - return -ENOEXEC; + goto out_putf; } + if (N_MAGIC(ex) == ZMAGIC && N_TXTOFF(ex) && (N_TXTOFF(ex) < inode->i_sb->s_blocksize)) { printk("N_TXTOFF < BLOCK_SIZE. Please convert library\n"); - return -ENOEXEC; + goto out_putf; } - if (N_FLAGS(ex)) return -ENOEXEC; + if (N_FLAGS(ex)) + goto out_putf; /* For QMAGIC, the starting address is 0x20 into the page. We mask this off to get the starting address for the page */ @@ -457,18 +435,26 @@ PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE, N_TXTOFF(ex)); + retval = error; if (error != start_addr) - return error; + goto out_putf; + len = PAGE_ALIGN(ex.a_text + ex.a_data); bss = ex.a_text + ex.a_data + ex.a_bss; if (bss > len) { - error = do_mmap(NULL, start_addr + len, bss-len, - PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_PRIVATE|MAP_FIXED, 0); + error = do_mmap(NULL, start_addr + len, bss - len, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_FIXED, 0); + retval = error; if (error != start_addr + len) - return error; + goto out_putf; } - return 0; + retval = 0; + +out_putf: + fput(file); +out: + return retval; } static int diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/central.c linux/arch/sparc64/kernel/central.c --- v2.2.3/linux/arch/sparc64/kernel/central.c Thu Aug 6 14:06:30 1998 +++ linux/arch/sparc64/kernel/central.c Wed Mar 10 16:53:37 1999 @@ -1,4 +1,4 @@ -/* $Id: central.c,v 1.6 1998/05/14 13:35:45 jj Exp $ +/* $Id: central.c,v 1.11 1998/12/14 12:18:16 davem Exp $ * central.c: Central FHC driver for Sunfire/Starfire/Wildfire. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -7,11 +7,17 @@ #include #include #include +#include +#include +#include #include #include struct linux_central *central_bus = NULL; +struct linux_fhc *fhc_list = NULL; + +#define IS_CENTRAL_FHC(__fhc) ((__fhc) == central_bus->child) static inline unsigned long long_align(unsigned long addr) { @@ -22,6 +28,156 @@ extern void prom_central_ranges_init(int cnode, struct linux_central *central); extern void prom_fhc_ranges_init(int fnode, struct linux_fhc *fhc); +static unsigned long probe_other_fhcs(unsigned long memory_start) +{ + struct linux_prom64_registers fpregs[6]; + char namebuf[128]; + int node; + + node = prom_getchild(prom_root_node); + node = prom_searchsiblings(node, "fhc"); + if (node == 0) { + prom_printf("FHC: Cannot find any toplevel firehose controllers.\n"); + prom_halt(); + } + while(node) { + struct linux_fhc *fhc; + int board; + u32 tmp; + + fhc = (struct linux_fhc *)memory_start; + memory_start += sizeof(struct linux_fhc); + memory_start = long_align(memory_start); + + /* Link it into the FHC chain. */ + fhc->next = fhc_list; + fhc_list = fhc; + + /* Toplevel FHCs have no parent. */ + fhc->parent = NULL; + + fhc->prom_node = node; + prom_getstring(node, "name", namebuf, sizeof(namebuf)); + strcpy(fhc->prom_name, namebuf); + prom_fhc_ranges_init(node, fhc); + + /* Non-central FHC's have 64-bit OBP format registers. */ + if(prom_getproperty(node, "reg", + (char *)&fpregs[0], sizeof(fpregs)) == -1) { + prom_printf("FHC: Fatal error, cannot get fhc regs.\n"); + prom_halt(); + } + + /* Only central FHC needs special ranges applied. */ + fhc->fhc_regs.pregs = (struct fhc_internal_regs *) + __va(fpregs[0].phys_addr); + fhc->fhc_regs.ireg = (struct fhc_ign_reg *) + __va(fpregs[1].phys_addr); + fhc->fhc_regs.ffregs = (struct fhc_fanfail_regs *) + __va(fpregs[2].phys_addr); + fhc->fhc_regs.sregs = (struct fhc_system_regs *) + __va(fpregs[3].phys_addr); + fhc->fhc_regs.uregs = (struct fhc_uart_regs *) + __va(fpregs[4].phys_addr); + fhc->fhc_regs.tregs = (struct fhc_tod_regs *) + __va(fpregs[5].phys_addr); + + board = prom_getintdefault(node, "board#", -1); + fhc->board = board; + + tmp = fhc->fhc_regs.pregs->fhc_jtag_ctrl; + if((tmp & FHC_JTAG_CTRL_MENAB) != 0) + fhc->jtag_master = 1; + else + fhc->jtag_master = 0; + + tmp = fhc->fhc_regs.pregs->fhc_id; + printk("FHC(board %d): Version[%x] PartID[%x] Manuf[%x] %s\n", + board, + (tmp & FHC_ID_VERS) >> 28, + (tmp & FHC_ID_PARTID) >> 12, + (tmp & FHC_ID_MANUF) >> 1, + (fhc->jtag_master ? "(JTAG Master)" : "")); + + /* This bit must be set in all non-central FHC's in + * the system. When it is clear, this identifies + * the central board. + */ + fhc->fhc_regs.pregs->fhc_control |= FHC_CONTROL_IXIST; + + /* Look for the next FHC. */ + node = prom_getsibling(node); + if(node == 0) + break; + node = prom_searchsiblings(node, "fhc"); + if(node == 0) + break; + } + + return memory_start; +} + +static void probe_clock_board(struct linux_central *central, + struct linux_fhc *fhc, + int cnode, int fnode) +{ + struct linux_prom_registers cregs[3]; + int clknode, nslots, tmp, nregs; + + clknode = prom_searchsiblings(prom_getchild(fnode), "clock-board"); + if(clknode == 0 || clknode == -1) { + prom_printf("Critical error, central lacks clock-board.\n"); + prom_halt(); + } + nregs = prom_getproperty(clknode, "reg", (char *)&cregs[0], sizeof(cregs)); + if (nregs == -1) { + prom_printf("CENTRAL: Fatal error, cannot map clock-board regs.\n"); + prom_halt(); + } + nregs /= sizeof(struct linux_prom_registers); + prom_apply_fhc_ranges(fhc, &cregs[0], nregs); + prom_apply_central_ranges(central, &cregs[0], nregs); + central->cfreg = (volatile u8 *) + __va((((unsigned long)cregs[0].which_io) << 32) | + (((unsigned long)cregs[0].phys_addr)+0x02)); + central->clkregs = (struct clock_board_regs *) + __va((((unsigned long)cregs[1].which_io) << 32) | + (((unsigned long)cregs[1].phys_addr))); + if(nregs == 2) + central->clkver = NULL; + else + central->clkver = (volatile u8 *) + __va((((unsigned long)cregs[2].which_io) << 32) | + (((unsigned long)cregs[2].phys_addr))); + + tmp = central->clkregs->stat1; + tmp &= 0xc0; + switch(tmp) { + case 0x40: + nslots = 16; + break; + case 0xc0: + nslots = 8; + break; + case 0x80: + if(central->clkver != NULL && + *(central->clkver) != 0) { + if((*(central->clkver) & 0x80) != 0) + nslots = 4; + else + nslots = 5; + break; + } + default: + nslots = 4; + break; + }; + central->slots = nslots; + printk("CENTRAL: Detected %d slot Enterprise system. cfreg[%02x] cver[%02x]\n", + central->slots, *(central->cfreg), + (central->clkver ? *(central->clkver) : 0x00)); +} + unsigned long central_probe(unsigned long memory_start) { struct linux_prom_registers fpregs[6]; @@ -30,9 +186,12 @@ int cnode, fnode, err; cnode = prom_finddevice("/central"); - if(cnode == 0 || cnode == -1) + if(cnode == 0 || cnode == -1) { + extern void starfire_check(void); + + starfire_check(); return memory_start; - printk("CENTRAL: found central PROM node %08x.\n", cnode); + } /* Ok we got one, grab some memory for software state. */ memory_start = long_align(memory_start); @@ -54,7 +213,9 @@ prom_central_ranges_init(cnode, central_bus); /* And then central's FHC. */ - fhc->next = NULL; + fhc->next = fhc_list; + fhc_list = fhc; + fhc->parent = central_bus; fnode = prom_searchsiblings(prom_getchild(cnode), "fhc"); if(fnode == 0 || fnode == -1) { @@ -67,9 +228,9 @@ prom_fhc_ranges_init(fnode, fhc); - /* Finally, map in FHC register set. */ + /* Now, map in FHC register set. */ if (prom_getproperty(fnode, "reg", (char *)&fpregs[0], sizeof(fpregs)) == -1) { - prom_printf("CENTRAL: fatal error, cannot get fhc regs.\n"); + prom_printf("CENTRAL: Fatal error, cannot get fhc regs.\n"); prom_halt(); } prom_apply_central_ranges(central_bus, &fpregs[0], 6); @@ -93,11 +254,144 @@ __va((((unsigned long)fpregs[5].which_io)<<32) | (((unsigned long)fpregs[5].phys_addr))); + /* Obtain board number from board status register, Central's + * FHC lacks "board#" property. + */ + err = fhc->fhc_regs.pregs->fhc_bsr; + fhc->board = (((err >> 16) & 0x01) | + ((err >> 12) & 0x0e)); + + fhc->jtag_master = 0; + + /* Attach the clock board registers for CENTRAL. */ + probe_clock_board(central_bus, fhc, cnode, fnode); + err = fhc->fhc_regs.pregs->fhc_id; - printk("FHC Version[%x] PartID[%x] Manufacturer[%x]\n", + printk("FHC(board %d): Version[%x] PartID[%x] Manuf[%x] (CENTRAL)\n", + fhc->board, ((err & FHC_ID_VERS) >> 28), ((err & FHC_ID_PARTID) >> 12), ((err & FHC_ID_MANUF) >> 1)); - return memory_start; + return probe_other_fhcs(memory_start); +} + +static __inline__ void fhc_ledblink(struct linux_fhc *fhc, int on) +{ + volatile u32 *ctrl = (volatile u32 *) + &fhc->fhc_regs.pregs->fhc_control; + u32 tmp; + + tmp = *ctrl; + + /* NOTE: reverse logic on this bit */ + if (on) + tmp &= ~(FHC_CONTROL_RLED); + else + tmp |= FHC_CONTROL_RLED; + tmp &= ~(FHC_CONTROL_AOFF | FHC_CONTROL_BOFF | FHC_CONTROL_SLINE); + + *ctrl = tmp; + tmp = *ctrl; +} + +static __inline__ void central_ledblink(struct linux_central *central, int on) +{ + volatile u8 *ctrl = (volatile u8 *) ¢ral->clkregs->control; + int tmp; + + tmp = *ctrl; + + /* NOTE: reverse logic on this bit */ + if(on) + tmp &= ~(CLOCK_CTRL_RLED); + else + tmp |= CLOCK_CTRL_RLED; + + *ctrl = tmp; + tmp = *ctrl; +} + +static struct timer_list sftimer; +static int led_state; + +static void sunfire_timer(unsigned long __ignored) +{ + struct linux_fhc *fhc; + + central_ledblink(central_bus, led_state); + for(fhc = fhc_list; fhc != NULL; fhc = fhc->next) + if(! IS_CENTRAL_FHC(fhc)) + fhc_ledblink(fhc, led_state); + led_state = ! led_state; + sftimer.expires = jiffies + (HZ >> 1); + add_timer(&sftimer); +} + +/* After PCI/SBUS busses have been probed, this is called to perform + * final initialization of all FireHose Controllers in the system. + */ +void firetruck_init(void) +{ + struct linux_central *central = central_bus; + struct linux_fhc *fhc; + + /* No central bus, nothing to do. */ + if (central == NULL) + return; + + for(fhc = fhc_list; fhc != NULL; fhc = fhc->next) { + volatile u32 *ctrl = (volatile u32 *) + &fhc->fhc_regs.pregs->fhc_control; + u32 tmp; + + /* Clear all of the interrupt mapping registers + * just in case OBP left them in a foul state. + */ +#define ZAP(REG1, REG2) \ +do { volatile u32 *__iclr = (volatile u32 *)(&(REG1)); \ + volatile u32 *__imap = (volatile u32 *)(&(REG2)); \ + *(__iclr) = 0; \ + (void) *(__iclr); \ + *(__imap) &= ~(0x80000000); \ + (void) *(__imap); \ +} while(0) + + ZAP(fhc->fhc_regs.ffregs->fhc_ff_iclr, + fhc->fhc_regs.ffregs->fhc_ff_imap); + ZAP(fhc->fhc_regs.sregs->fhc_sys_iclr, + fhc->fhc_regs.sregs->fhc_sys_imap); + ZAP(fhc->fhc_regs.uregs->fhc_uart_iclr, + fhc->fhc_regs.uregs->fhc_uart_imap); + ZAP(fhc->fhc_regs.tregs->fhc_tod_iclr, + fhc->fhc_regs.tregs->fhc_tod_imap); + +#undef ZAP + + /* Setup FHC control register. */ + tmp = *ctrl; + + /* All non-central boards have this bit set. */ + if(! IS_CENTRAL_FHC(fhc)) + tmp |= FHC_CONTROL_IXIST; + + /* For all FHCs, clear the firmware synchronization + * line and both low power mode enables. + */ + tmp &= ~(FHC_CONTROL_AOFF | FHC_CONTROL_BOFF | FHC_CONTROL_SLINE); + *ctrl = tmp; + tmp = *ctrl; /* Ensure completion */ + } + + /* OBP leaves it on, turn it off so clock board timer LED + * is in sync with FHC ones. + */ + central->clkregs->control &= ~(CLOCK_CTRL_RLED); + + led_state = 0; + init_timer(&sftimer); + sftimer.data = 0; + sftimer.function = &sunfire_timer; + sftimer.expires = jiffies + (HZ >> 1); + add_timer(&sftimer); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/cpu.c linux/arch/sparc64/kernel/cpu.c --- v2.2.3/linux/arch/sparc64/kernel/cpu.c Thu Apr 23 20:21:31 1998 +++ linux/arch/sparc64/kernel/cpu.c Sun Mar 21 07:11:36 1999 @@ -49,11 +49,11 @@ #define NSPARCCHIPS (sizeof(linux_sparc_chips)/sizeof(struct cpu_iu_info)) #ifdef __SMP__ -char *sparc_cpu_type[NR_CPUS] = { "cpu-oops", "cpu-oops1", "cpu-oops2", "cpu-oops3" }; -char *sparc_fpu_type[NR_CPUS] = { "fpu-oops", "fpu-oops1", "fpu-oops2", "fpu-oops3" }; +char *sparc_cpu_type[64] = { "cpu-oops", "cpu-oops1", "cpu-oops2", "cpu-oops3" }; +char *sparc_fpu_type[64] = { "fpu-oops", "fpu-oops1", "fpu-oops2", "fpu-oops3" }; #else -char *sparc_cpu_type[NR_CPUS] = { "cpu-oops", }; -char *sparc_fpu_type[NR_CPUS] = { "fpu-oops", }; +char *sparc_cpu_type[64] = { "cpu-oops", }; +char *sparc_fpu_type[64] = { "fpu-oops", }; #endif unsigned int fsr_storage; @@ -88,7 +88,7 @@ if(i==NSPARCCHIPS) { printk("DEBUG: manuf = 0x%x impl = 0x%x\n", manuf, impl); - sparc_cpu_type[cpuid] = "Unknow CPU"; + sparc_cpu_type[cpuid] = "Unknown CPU"; } for(i = 0; i #include -struct prom_cpuinfo linux_cpus[NR_CPUS] __initdata = { { 0 } }; -unsigned prom_cpu_nodes[NR_CPUS]; +struct prom_cpuinfo linux_cpus[64] __initdata = { { 0 } }; +unsigned prom_cpu_nodes[64]; int linux_num_cpus = 0; extern void cpu_probe(void); @@ -25,11 +25,12 @@ { char node_str[128]; int nd, prom_node_cpu, thismid; - int cpu_nds[NR_CPUS]; /* One node for each cpu */ + int cpu_nds[64]; /* One node for each cpu */ int cpu_ctr = 0; prom_getstring(prom_root_node, "device_type", node_str, sizeof(node_str)); + prom_printf("Booting Linux...\n"); if(strcmp(node_str, "cpu") == 0) { cpu_nds[0] = prom_root_node; linux_cpus[0].prom_node = prom_root_node; @@ -38,7 +39,7 @@ } else { int scan; scan = prom_getchild(prom_root_node); - prom_printf("root child is %08x\n", (unsigned) scan); + /* prom_printf("root child is %08x\n", (unsigned) scan); */ nd = 0; while((scan = prom_getsibling(scan)) != 0) { prom_getstring(scan, "device_type", node_str, sizeof(node_str)); @@ -49,11 +50,11 @@ (char *) &thismid, sizeof(thismid)); linux_cpus[cpu_ctr].mid = thismid; #ifdef __SMP__ - prom_printf("Found CPU %d (node=%08x,mid=%d)\n", - cpu_ctr, (unsigned) scan, - thismid); - printk("Found CPU %d (node=%08x,mid=%d)\n", - cpu_ctr, (unsigned) scan, thismid); + /* Don't pollute PROM screen with these messages. If the kernel is screwed enough + that console does not start up, then we don't care how many CPUs have been found, + if it starts up, the user can use console=prom to see it. */ + /* prom_printf("Found CPU %d (node=%08x,mid=%d)\n", cpu_ctr, (unsigned) scan, thismid); */ + printk("Found CPU %d (node=%08x,mid=%d)\n", cpu_ctr, (unsigned) scan, thismid); #endif cpu_ctr++; } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/dtlb_backend.S linux/arch/sparc64/kernel/dtlb_backend.S --- v2.2.3/linux/arch/sparc64/kernel/dtlb_backend.S Mon Oct 5 13:13:37 1998 +++ linux/arch/sparc64/kernel/dtlb_backend.S Wed Mar 10 16:53:37 1999 @@ -1,4 +1,4 @@ -/* $Id: dtlb_backend.S,v 1.6 1998/09/24 03:21:32 davem Exp $ +/* $Id: dtlb_backend.S,v 1.7 1998/12/16 04:33:28 davem Exp $ * dtlb_backend.S: Back end to DTLB miss replacement strategy. * This is included directly into the trap table. * @@ -37,28 +37,30 @@ be,pn %xcc, sparc64_vpte_nucleus ! Is it from Nucleus? and %g1, 0xffe, %g1 ! Mask PMD offset bits brnz,pt %g5, sparc64_vpte_continue ! Yep, go like smoke - nop ! Pipe bubble... + add %g1, %g1, %g1 ! Position PMD offset some more srlx %g6, (PGD_SHIFT - 2), %g5 ! Position PGD offset and %g5, 0xffc, %g5 ! Mask PGD offset /* TLB1 ** ICACHE line 3: Quick VPTE miss */ lduwa [%g7 + %g5] ASI_PHYS_USE_EC, %g5! Load PGD - brz,pn %g5, 2f ! Valid? + brz,pn %g5, vpte_noent ! Valid? +sparc64_kpte_continue: + sllx %g5, 11, %g5 ! Shift into place sparc64_vpte_continue: - add %g1, %g1, %g1 ! Position PMD offset once again lduwa [%g5 + %g1] ASI_PHYS_USE_EC, %g5! Load PMD - brz,pn %g5, 2f ! Valid? + sllx %g5, 11, %g5 ! Shift into place + brz,pn %g5, vpte_noent ! Valid? sllx %g2, 62, %g1 ! Put _PAGE_VALID into %g1 or %g5, VPTE_BITS, %g5 ! Prepare VPTE data - or %g5, %g1, %g5 ! ... /* TLB1 ** ICACHE line 4: Quick VPTE miss */ + or %g5, %g1, %g5 ! ... mov TLB_SFSR, %g1 ! Restore %g1 value stxa %g5, [%g0] ASI_DTLB_DATA_IN ! Load VPTE into TLB - membar #Sync ! Synchronize ASI stores stxa %g4, [%g1 + %g1] ASI_DMMU ! Restore previous TAG_ACCESS retry ! Load PTE once again -2: mov TLB_SFSR, %g1 ! Restore %g1 value +vpte_noent: + mov TLB_SFSR, %g1 ! Restore %g1 value stxa %g4, [%g1 + %g1] ASI_DMMU ! Restore previous TAG_ACCESS done ! Slick trick diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/dtlb_prot.S linux/arch/sparc64/kernel/dtlb_prot.S --- v2.2.3/linux/arch/sparc64/kernel/dtlb_prot.S Thu Aug 6 14:06:30 1998 +++ linux/arch/sparc64/kernel/dtlb_prot.S Wed Mar 10 16:53:37 1999 @@ -1,4 +1,4 @@ -/* $Id: dtlb_prot.S,v 1.17 1998/05/25 16:59:11 davem Exp $ +/* $Id: dtlb_prot.S,v 1.18 1999/03/02 15:42:14 jj Exp $ * dtlb_prot.S: DTLB protection trap strategy. * This is included directly into the trap table. * @@ -45,13 +45,13 @@ mov TLB_TAG_ACCESS, %g4 ! Prepare reload of vaddr bgu,pn %xcc, winfix_trampoline ! Yes, perform winfixup ldxa [%g4] ASI_DMMU, %g5 ! Put tagaccess in %g5 - sethi %hi(1f), %g7 ! Nope, normal fault + ba,pt %xcc, sparc64_realfault_common ! Nope, normal fault /* PROT ** ICACHE line 4: More real fault processing */ - ba,pt %xcc, etrap ! Save state -1: or %g7, %lo(1b), %g7 ! ... - ba,pt %xcc, sparc64_realfault_continue! Now call the fault handler - mov 1, %o2 ! Indicate this was a write + mov 1, %g4 ! Indicate this was a write + nop + nop + nop nop nop nop diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/ebus.c linux/arch/sparc64/kernel/ebus.c --- v2.2.3/linux/arch/sparc64/kernel/ebus.c Mon Oct 5 13:13:37 1998 +++ linux/arch/sparc64/kernel/ebus.c Wed Mar 10 16:53:37 1999 @@ -1,4 +1,4 @@ -/* $Id: ebus.c,v 1.33 1998/09/21 05:06:03 jj Exp $ +/* $Id: ebus.c,v 1.35 1999/01/26 14:34:11 jj Exp $ * ebus.c: PCI to EBus bridge device. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) @@ -263,6 +263,31 @@ ebus->next = 0; while (ebusnd) { + /* SUNW,pci-qfe uses four empty ebuses on it. + I think we should not consider them here, + as they have half of the properties this + code expects and once we do PCI hot-plug, + we'd have to tweak with the ebus_chain + in the runtime after initialization. -jj */ + if (!prom_getchild (ebusnd)) { + pdev = pci_find_device(PCI_VENDOR_ID_SUN, + PCI_DEVICE_ID_SUN_EBUS, pdev); + if (!pdev) { + if (ebus == ebus_chain) { + ebus_chain = NULL; + printk("ebus: No EBus's found.\n"); +#ifdef PROM_DEBUG + dprintf("ebus: No EBus's found.\n"); +#endif + return; + } + break; + } + + cookie = pdev->sysdata; + ebusnd = cookie->prom_node; + continue; + } printk("ebus%d:", num_ebus); #ifdef PROM_DEBUG dprintf("ebus%d:", num_ebus); @@ -279,6 +304,12 @@ pci_read_config_word(pdev, PCI_COMMAND, &pci_command); pci_command |= PCI_COMMAND_MASTER; pci_write_config_word(pdev, PCI_COMMAND, pci_command); + + /* Set reasonable cache line size and latency timer values. */ + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 64); + + /* NOTE: Cache line size is in 32-bit word units. */ + pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 0x10); len = prom_getproperty(ebusnd, "reg", (void *)regs, sizeof(regs)); diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/entry.S linux/arch/sparc64/kernel/entry.S --- v2.2.3/linux/arch/sparc64/kernel/entry.S Sun Nov 8 14:02:46 1998 +++ linux/arch/sparc64/kernel/entry.S Wed Mar 10 16:53:37 1999 @@ -1,4 +1,4 @@ -/* $Id: entry.S,v 1.91 1998/10/07 01:27:08 davem Exp $ +/* $Id: entry.S,v 1.101 1999/01/19 07:54:38 davem Exp $ * arch/sparc64/kernel/entry.S: Sparc64 trap low-level entry points. * * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu) @@ -29,8 +29,17 @@ .text .align 32 + .globl sparc64_vpte_patchme1 + .globl sparc64_vpte_patchme2 +sparc64_vpte_nucleus: +sparc64_vpte_patchme1: + sethi %hi(0), %g5 ! This has to be patched +sparc64_vpte_patchme2: + or %g5, %lo(0), %g5 ! This is patched too + ba,pt %xcc, sparc64_kpte_continue ! Part of dtlb_backend + add %g1, %g1, %g1 ! Finish PMD offset adjustment + /* This is trivial with the new code... */ - .align 32 .globl do_fpdis do_fpdis: ldub [%g6 + AOFF_task_tss + AOFF_thread_fpsaved], %g5 ! Load Group @@ -164,69 +173,57 @@ * * With this method we can do most of the cross-call tlb/cache * flushing very quickly. + * + * Current CPU's IRQ worklist table is locked into %g1, + * don't touch. */ - .data - .align 8 - .globl ivec_spurious_cookie -ivec_spurious_cookie: .xword 0 - .text - .align 32 - .globl do_ivec + .align 32 + .globl do_ivec do_ivec: - ldxa [%g0] ASI_INTR_RECEIVE, %g5 - andcc %g5, 0x20, %g0 - be,pn %xcc, do_ivec_return - mov 0x40, %g2 - - /* Load up Interrupt Vector Data 0 register. */ + wr %g0, ASI_UDB_INTR_R, %asi + ldxa [%g0 + 0x40] %asi, %g3 sethi %hi(KERNBASE), %g4 - ldxa [%g2] ASI_UDB_INTR_R, %g3 cmp %g3, %g4 bgeu,pn %xcc, do_ivec_xcall - nop - and %g3, 0x7ff, %g3 - sllx %g3, 3, %g3 - ldx [%g1 + %g3], %g2 - brz,pn %g2, do_ivec_spurious - sethi %hi(0x80000000), %g5 + srlx %g3, 32, %g5 + stxa %g0, [%g0] ASI_INTR_RECEIVE + membar #Sync - or %g2, %g5, %g2 - stx %g2, [%g1 + %g3] + sethi %hi(ivector_table), %g2 + sllx %g3, 5, %g3 + or %g2, %lo(ivector_table), %g2 + add %g2, %g3, %g3 + ldx [%g3 + 0x08], %g2 /* irq_info */ + ldub [%g3 + 0x04], %g4 /* pil */ + brz,pn %g2, do_ivec_spurious + mov 1, %g2 - /* No branches, worse case we don't know about this interrupt - * yet, so we would just write a zero into the softint register - * which is completely harmless. - */ + sllx %g2, %g4, %g2 + sllx %g4, 2, %g4 + lduw [%g1 + %g4], %g5 /* g5 = irq_work(cpu, pil) */ + stw %g5, [%g3 + 0x00] /* bucket->irq_chain = g5 */ + stw %g3, [%g1 + %g4] /* irq_work(cpu, pil) = bucket */ wr %g2, 0x0, %set_softint -do_ivec_return: - stxa %g0, [%g0] ASI_INTR_RECEIVE - membar #Sync retry do_ivec_xcall: - srlx %g3, 32, %g5 - add %g2, 0x10, %g2 + ldxa [%g0 + 0x50] %asi, %g6 + srl %g3, 0, %g3 - ldxa [%g2] ASI_UDB_INTR_R, %g6 - add %g2, 0x10, %g2 - ldxa [%g2] ASI_UDB_INTR_R, %g7 + ldxa [%g0 + 0x60] %asi, %g7 stxa %g0, [%g0] ASI_INTR_RECEIVE membar #Sync jmpl %g3, %g0 nop - do_ivec_spurious: - srl %g3, 3, %g3 - sethi %hi(ivec_spurious_cookie), %g2 - stx %g3, [%g2 + %lo(ivec_spurious_cookie)] - stxa %g0, [%g0] ASI_INTR_RECEIVE - membar #Sync + stw %g3, [%g1 + 0x00] /* irq_work(cpu, 0) = bucket */ rdpr %pstate, %g5 + wrpr %g5, PSTATE_IG | PSTATE_AG, %pstate sethi %hi(109f), %g7 ba,pt %xcc, etrap 109: or %g7, %lo(109b), %g7 - call report_spurious_ivec + call catch_disabled_ivec add %sp, STACK_BIAS + REGWIN_SZ, %o0 ba,pt %xcc, rtrap clr %l6 @@ -337,7 +334,7 @@ or %g1, %lo(irq_action), %g1 ldx [%g1 + (11 << 3)], %g3 ! irqaction[floppy_irq] ldx [%g3 + 0x10], %g4 ! action->mask == ino_bucket ptr - ldx [%g4 + 0x18], %g4 ! bucket->iclr + ldx [%g4 + 0x10], %g4 ! bucket->iclr stw %g0, [%g4] ! SYSIO_ICLR_IDLE membar #Sync ! probably not needed... retry @@ -588,12 +585,20 @@ /* SunOS's execv() call only specifies the argv argument, the * environment settings are the same as the calling processes. */ - .globl sunos_execv + .globl sunos_execv, sys_execve, sys32_execve +sys_execve: + sethi %hi(sparc_execve), %g1 + ba,pt %xcc, execve_merge + or %g1, %lo(sparc_execve), %g1 sunos_execv: - sethi %hi(sparc32_execve), %g1 - stx %g0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I2] - jmpl %g1 + %lo(sparc32_execve), %g0 - add %sp, STACK_BIAS + REGWIN_SZ, %o0 + stx %g0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I2] +sys32_execve: + sethi %hi(sparc32_execve), %g1 + or %g1, %lo(sparc32_execve), %g1 +execve_merge: + flushw + jmpl %g1, %g0 + add %sp, STACK_BIAS + REGWIN_SZ, %o0 .globl sys_pipe, sys_execve, sys_sigpause, sys_nis_syscall .globl sys_sigsuspend, sys_rt_sigsuspend, sys32_rt_sigsuspend @@ -612,14 +617,6 @@ jmpl %g1 + %lo(c_sys_nis_syscall), %g0 nop -sys_execve: sethi %hi(sparc_execve), %g1 - add %sp, STACK_BIAS + REGWIN_SZ, %o0 - jmpl %g1 + %lo(sparc_execve), %g0 - nop -sys32_execve: sethi %hi(sparc32_execve), %g1 - add %sp, STACK_BIAS + REGWIN_SZ, %o0 - jmpl %g1 + %lo(sparc32_execve), %g0 - nop sys_memory_ordering: sethi %hi(sparc_memory_ordering), %g1 add %sp, STACK_BIAS + REGWIN_SZ, %o1 @@ -719,27 +716,30 @@ .globl sys_fork, sys_vfork, sys_clone, sparc_exit .globl ret_from_syscall .align 32 -sys_fork: -sys_vfork: mov SIGCHLD, %o0 - clr %o1 +sys_vfork: /* Under Linux, vfork and fork are just special cases of clone. */ + sethi %hi(0x4000 | 0x0100 | SIGCHLD), %o0 + or %o0, %lo(0x4000 | 0x0100 | SIGCHLD), %o0 + ba,pt %xcc, sys_clone +sys_fork: clr %o1 + mov SIGCHLD, %o0 sys_clone: flushw - mov %o7, %l5 - add %sp, STACK_BIAS + REGWIN_SZ, %o2 movrz %o1, %fp, %o1 - call do_fork - mov %l5, %o7 + nop + ba,pt %xcc, do_fork + add %sp, STACK_BIAS + REGWIN_SZ, %o2 ret_from_syscall: /* Clear SPARC_FLAG_NEWCHILD, switch_to leaves tss.flags in * %o7 for us. Check performance counter stuff too. */ - andn %o7, 0x100, %o7 - sth %o7, [%g6 + AOFF_task_tss + AOFF_thread_flags] #ifdef __SMP__ - sethi %hi(scheduler_lock), %o4 - membar #StoreStore | #LoadStore - stb %g0, [%o4 + %lo(scheduler_lock)] + andn %o7, 0x100, %l0 + call schedule_tail + sth %l0, [%g6 + AOFF_task_tss + AOFF_thread_flags] +#else + andn %o7, 0x100, %l0 + sth %l0, [%g6 + AOFF_task_tss + AOFF_thread_flags] #endif - andcc %o7, 0x200, %g0 + andcc %l0, 0x200, %g0 be,pt %icc, 1f nop ldx [%g6 + AOFF_task_tss + AOFF_thread_pcr_reg], %o7 diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/head.S linux/arch/sparc64/kernel/head.S --- v2.2.3/linux/arch/sparc64/kernel/head.S Sun Nov 8 14:02:46 1998 +++ linux/arch/sparc64/kernel/head.S Wed Mar 10 16:53:37 1999 @@ -1,4 +1,4 @@ -/* $Id: head.S,v 1.54 1998/10/06 20:48:30 ecd Exp $ +/* $Id: head.S,v 1.59 1999/01/06 01:37:35 davem Exp $ * head.S: Initial boot code for the Sparc64 port of Linux. * * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu) @@ -7,6 +7,7 @@ * Copyright (C) 1997 Miguel de Icaza (miguel@nuclecu.unam.mx) */ +#include #include #include #include @@ -330,7 +331,7 @@ /* IMPORTANT NOTE: Whenever making changes here, check * trampoline.S as well. -jj */ .globl setup_tba -setup_tba: +setup_tba: /* i0 = is_starfire */ save %sp, -160, %sp rdpr %tba, %g7 @@ -376,9 +377,34 @@ /* Setup Interrupt globals */ wrpr %o1, (PSTATE_IG|PSTATE_IE), %pstate - sethi %hi(ivector_to_mask), %g5 - or %g5, %lo(ivector_to_mask), %g1 /* IVECTOR table */ - mov 0x40, %g2 /* INTR data 0 register */ +#ifndef __SMP__ + sethi %hi(__up_workvec), %g5 + or %g5, %lo(__up_workvec), %g1 +#else + /* By definition of where we are, this is boot_cpu. */ + sethi %hi(cpu_data), %g5 + or %g5, %lo(cpu_data), %g5 + + brz,pt %i0, not_starfire + sethi %hi(0x1fff4000), %g1 + or %g1, %lo(0x1fff4000), %g1 + sllx %g1, 12, %g1 + or %g1, 0xd0, %g1 + lduwa [%g1] ASI_PHYS_BYPASS_EC_E, %g1 + b,pt %xcc, set_worklist + nop + +not_starfire: + ldxa [%g0] ASI_UPA_CONFIG, %g1 + srlx %g1, 17, %g1 + and %g1, 0x1f, %g1 + + /* In theory this is: &(cpu_data[boot_cpu_id].irq_worklists[0]) */ +set_worklist: + sllx %g1, 7, %g1 + add %g5, %g1, %g5 + add %g5, 64, %g1 +#endif /* Kill PROM timer */ wr %g0, 0, %tick_cmpr @@ -407,6 +433,13 @@ .globl empty_bad_page empty_bad_page: .skip 0x2000 + +#ifdef CONFIG_SBUS +/* This is just a hack to fool make depend config.h discovering + strategy: As the .S files below need config.h, but + make depend does not find it for them, we include config.h + in head.S */ +#endif ! 0x0000000000408000 diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/ioctl32.c linux/arch/sparc64/kernel/ioctl32.c --- v2.2.3/linux/arch/sparc64/kernel/ioctl32.c Thu Nov 19 09:56:27 1998 +++ linux/arch/sparc64/kernel/ioctl32.c Mon Mar 15 16:10:43 1999 @@ -1,4 +1,4 @@ -/* $Id: ioctl32.c,v 1.55 1998/11/17 07:43:17 davem Exp $ +/* $Id: ioctl32.c,v 1.59 1999/03/12 13:30:21 jj Exp $ * ioctl32.c: Conversion between 32bit and 64bit native ioctls. * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -52,6 +52,7 @@ #include #include #include +#include #include @@ -253,11 +254,23 @@ case SIOCGPPPSTATS: case SIOCGPPPCSTATS: case SIOCGPPPVER: + case SIOCETHTOOL: if (copy_from_user(&ifr, (struct ifreq32 *)arg, sizeof(struct ifreq32))) return -EFAULT; ifr.ifr_data = (__kernel_caddr_t)get_free_page(GFP_KERNEL); if (!ifr.ifr_data) return -EAGAIN; + if(cmd == SIOCETHTOOL) { + u32 data; + + __get_user(data, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_data)); + if(copy_from_user(ifr.ifr_data, + (char *)A(data), + sizeof(struct ethtool_cmd))) { + free_page((unsigned long)ifr.ifr_data); + return -EFAULT; + } + } break; default: if (copy_from_user(&ifr, (struct ifreq32 *)arg, sizeof(struct ifreq32))) @@ -280,17 +293,21 @@ case SIOCGIFBRDADDR: case SIOCGIFDSTADDR: case SIOCGIFNETMASK: + case SIOCGIFTXQLEN: if (copy_to_user((struct ifreq32 *)arg, &ifr, sizeof(struct ifreq32))) return -EFAULT; break; case SIOCGPPPSTATS: case SIOCGPPPCSTATS: case SIOCGPPPVER: + case SIOCETHTOOL: { u32 data; int len; __get_user(data, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_data)); + if(cmd == SIOCETHTOOL) + len = sizeof(struct ethtool_cmd); if(cmd == SIOCGPPPVER) len = strlen(PPP_VERSION) + 1; else if(cmd == SIOCGPPPCSTATS) @@ -298,7 +315,9 @@ else len = sizeof(struct ppp_stats); - if (copy_to_user((char *)A(data), ifr.ifr_data, len)) + len = copy_to_user((char *)A(data), ifr.ifr_data, len); + free_page((unsigned long)ifr.ifr_data); + if(len) return -EFAULT; break; } @@ -1458,6 +1477,9 @@ case SIOCGPPPSTATS: case SIOCGPPPCSTATS: case SIOCGPPPVER: + case SIOCGIFTXQLEN: + case SIOCSIFTXQLEN: + case SIOCETHTOOL: error = dev_ifsioc(fd, cmd, arg); goto out; @@ -1583,7 +1605,7 @@ * compatable types passed or none at all... */ - /* Bit T */ + /* Big T */ case TCGETA: case TCSETA: case TCSETAW: @@ -1618,6 +1640,8 @@ case TIOCSPGRP: case TIOCGPGRP: case TIOCSCTTY: + case TIOCGPTN: + case TIOCSPTLCK: /* Big F */ case FBIOGTYPE: diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/irq.c linux/arch/sparc64/kernel/irq.c --- v2.2.3/linux/arch/sparc64/kernel/irq.c Sun Nov 8 14:02:46 1998 +++ linux/arch/sparc64/kernel/irq.c Wed Mar 10 16:53:37 1999 @@ -1,4 +1,4 @@ -/* $Id: irq.c,v 1.66 1998/10/21 15:02:25 ecd Exp $ +/* $Id: irq.c,v 1.75 1999/01/28 12:37:56 jj Exp $ * irq.c: UltraSparc IRQ handling/init/registry. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -41,20 +41,30 @@ #define SA_DMA_SYNC 0x200 #ifdef __SMP__ -void distribute_irqs(void); -static int irqs_have_been_distributed = 0; +static void distribute_irqs(void); #endif -/* UPA nodes send interrupt packet to UltraSparc with first data reg value - * low 5 bits holding the IRQ identifier being delivered. We must translate - * this into a non-vector IRQ so we can set the softint on this cpu. To - * make things even more swift we store the complete mask here. +/* UPA nodes send interrupt packet to UltraSparc with first data reg + * value low 5 (7 on Starfire) bits holding the IRQ identifier being + * delivered. We must translate this into a non-vector IRQ so we can + * set the softint on this cpu. + * + * To make processing these packets efficient and race free we use + * an array of irq buckets below. The interrupt vector handler in + * entry.S feeds incoming packets into per-cpu pil-indexed lists. + * The IVEC handler does not need to act atomically, the PIL dispatch + * code uses CAS to get an atomic snapshot of the list and clear it + * at the same time. */ -#define NUM_HARD_IVECS 2048 -#define NUM_IVECS (NUM_HARD_IVECS + 64) /* For SMP IRQ distribution alg. */ +struct ino_bucket ivector_table[NUM_IVECS] __attribute__ ((aligned (64))); -unsigned long ivector_to_mask[NUM_IVECS]; +#ifndef __SMP__ +unsigned int __up_workvec[16] __attribute__ ((aligned (64))); +#define irq_work(__cpu, __pil) &(__up_workvec[(__pil)]) +#else +#define irq_work(__cpu, __pil) &(cpu_data[(__cpu)].irq_worklists[(__pil)]) +#endif /* This is based upon code in the 32-bit Sparc kernel written mostly by * David Redman (djhr@tadpole.co.uk). @@ -63,30 +73,21 @@ static struct irqaction static_irqaction[MAX_STATIC_ALLOC]; static int static_irq_count = 0; -/* XXX Must be exported so that fast IRQ handlers can get at it... -DaveM */ +/* This is exported so that fast IRQ handlers can get at it... -DaveM */ struct irqaction *irq_action[NR_IRQS+1] = { NULL, NULL, NULL, NULL, NULL, NULL , NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL , NULL, NULL }; -#define IBF_DMA_SYNC 0x01 -#define IBF_PCI 0x02 -#define IBF_ACTIVE 0x04 +/* Only 8-bits are available, be careful. -DaveM */ +#define IBF_DMA_SYNC 0x01 /* DMA synchronization behind PCI bridge needed. */ +#define IBF_PCI 0x02 /* Indicates PSYCHO/SCHIZO PCI interrupt. */ +#define IBF_ACTIVE 0x04 /* This interrupt is active and has a handler. */ +#define IBF_MULTI 0x08 /* On PCI, indicates shared bucket. */ -#define __imap(bucket) ((bucket)->iclr + (bucket)->imap_off) #define __bucket(irq) ((struct ino_bucket *)(unsigned long)(irq)) #define __irq(bucket) ((unsigned int)(unsigned long)(bucket)) -static struct ino_bucket *bucket_base, *buckets, *endbuckets; - -__initfunc(unsigned long irq_init(unsigned long start_mem, unsigned long end_mem)) -{ - start_mem = (start_mem + 15) & ~15; - bucket_base = buckets = (struct ino_bucket *)start_mem; - endbuckets = buckets + 2048; - return (unsigned long)endbuckets; -} - int get_irq_list(char *buf) { int i, len = 0; @@ -104,7 +105,7 @@ #else for (j = 0; j < smp_num_cpus; j++) len += sprintf(buf + len, "%10u ", - kstat.irqs[cpu_logical_map(j)][i]); + kstat.irqs[cpu_logical_map(j)][i]); #endif len += sprintf(buf + len, "%c %s", (action->flags & SA_INTERRUPT) ? '+' : ' ', @@ -224,8 +225,7 @@ 1, /* Power Management */ }; -/* INO number to IMAP register offset for PSYCHO external IRQ's. - */ +/* INO number to IMAP register offset for PSYCHO external IRQ's. */ #define psycho_offset(x) ((unsigned long)(&(((struct psycho_regs *)0)->x))) #define psycho_imap_offset(ino) \ @@ -241,16 +241,27 @@ /* Now these are always passed a true fully specified sun4u INO. */ void enable_irq(unsigned int irq) { + extern int this_is_starfire; struct ino_bucket *bucket = __bucket(irq); - unsigned long tid; unsigned int *imap; + unsigned long tid; - imap = __imap(bucket); - if (!imap) return; + imap = bucket->imap; + if (!imap) + return; - /* We send it to our UPA MID, for SMP this will be different. */ - __asm__ __volatile__("ldxa [%%g0] %1, %0" : "=r" (tid) : "i" (ASI_UPA_CONFIG)); - tid = ((tid & UPA_CONFIG_MID) << 9); + if(this_is_starfire == 0) { + /* We set it to our UPA MID. */ + __asm__ __volatile__("ldxa [%%g0] %1, %0" + : "=r" (tid) + : "i" (ASI_UPA_CONFIG)); + tid = ((tid & UPA_CONFIG_MID) << 9); + } else { + extern unsigned int starfire_translate(unsigned int *imap, + unsigned int upaid); + + tid = (starfire_translate(imap, current->processor) << 26); + } /* NOTE NOTE NOTE, IGN and INO are read-only, IGN is a product * of this SYSIO's preconfigured IGN in the SYSIO Control @@ -269,35 +280,83 @@ struct ino_bucket *bucket = __bucket(irq); unsigned int *imap; - imap = __imap(bucket); - if (!imap) return; - - /* NOTE: We do not want to futz with the IRQ clear registers - * and move the state to IDLE, the SCSI code does call - * disable_irq() to assure atomicity in the queue cmd - * SCSI adapter driver code. Thus we'd lose interrupts. - */ - *imap &= ~(SYSIO_IMAP_VALID); + imap = bucket->imap; + if (imap != NULL) { + /* NOTE: We do not want to futz with the IRQ clear registers + * and move the state to IDLE, the SCSI code does call + * disable_irq() to assure atomicity in the queue cmd + * SCSI adapter driver code. Thus we'd lose interrupts. + */ + *imap &= ~(SYSIO_IMAP_VALID); + } } +/* The timer is the one "weird" interrupt which is generated by + * the CPU %tick register and not by some normal vectored interrupt + * source. To handle this special case, we use this dummy INO bucket. + */ +static struct ino_bucket pil0_dummy_bucket = { + 0, /* irq_chain */ + 0, /* pil */ + 0, /* pending */ + 0, /* flags */ + 0, /* __unused */ + NULL, /* irq_info */ + NULL, /* iclr */ + NULL, /* imap */ +}; + unsigned int build_irq(int pil, int inofixup, unsigned int *iclr, unsigned int *imap) { - if (buckets == endbuckets) - panic("Out of IRQ buckets. Should not happen.\n"); - buckets->pil = pil; - if (pil && (!iclr || !imap)) { - prom_printf("Invalid build_irq %d %d %016lx %016lx\n", pil, inofixup, iclr, imap); + struct ino_bucket *bucket; + unsigned short ino; + + if(pil == 0) { + if(iclr != NULL || imap != NULL) { + prom_printf("Invalid dummy bucket for PIL0 (%p:%p)\n", + iclr, imap); + prom_halt(); + } + return __irq(&pil0_dummy_bucket); + } + + /* RULE: Both must be specified in all other cases. */ + if (iclr == NULL || imap == NULL) { + prom_printf("Invalid build_irq %d %d %016lx %016lx\n", + pil, inofixup, iclr, imap); prom_halt(); } - if (imap) - buckets->ino = (*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO)) + inofixup; - else - buckets->ino = 0; - - buckets->iclr = iclr; - buckets->flags = 0; - buckets->imap_off = imap - iclr; - return __irq(buckets++); + + ino = (*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO)) + inofixup; + if(ino > NUM_IVECS) { + prom_printf("Invalid INO %04x (%d:%d:%016lx:%016lx)\n", + ino, pil, inofixup, iclr, imap); + prom_halt(); + } + + /* Ok, looks good, set it up. Don't touch the irq_chain or + * the pending flag. + */ + bucket = &ivector_table[ino]; + if ((bucket->flags & IBF_ACTIVE) || + (bucket->irq_info != NULL)) { + /* This is a gross fatal error if it happens here. */ + prom_printf("IRQ: Trying to reinit INO bucket, fatal error.\n"); + prom_printf("IRQ: Request INO %04x (%d:%d:%016lx:%016lx)\n", + ino, pil, inofixup, iclr, imap); + prom_printf("IRQ: Existing (%d:%016lx:%016lx)\n", + bucket->pil, bucket->iclr, bucket->imap); + prom_printf("IRQ: Cannot continue, halting...\n"); + prom_halt(); + } + bucket->imap = imap; + bucket->iclr = iclr; + bucket->pil = pil; + bucket->flags = 0; + + bucket->irq_info = NULL; + + return __irq(bucket); } unsigned int sbus_build_irq(void *buscookie, unsigned int ino) @@ -382,8 +441,44 @@ if(!(ino & 0x20)) inofixup = ino & 0x03; - bucket = __bucket(build_irq(pil, inofixup, iclr, imap)); - + /* First check for sharing. */ + ino = (*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO)) + inofixup; + if (ino > NUM_IVECS) { + prom_printf("PSYCHO: Invalid INO %04x (%d:%d:%016lx:%016lx)\n", + ino, pil, inofixup, iclr, imap); + prom_halt(); + } + bucket = &ivector_table[ino]; + if(bucket->flags & IBF_ACTIVE) { + void *old_handler = bucket->irq_info; + unsigned long flags; + + if(old_handler == NULL) { + prom_printf("PSYCHO: Active bucket, but no handler.\n"); + prom_halt(); + } + save_and_cli(flags); + if((bucket->flags & IBF_MULTI) == 0) { + void **vector; + + vector = kmalloc(sizeof(void *) * 4, + GFP_KERNEL); + + /* We might have slept. */ + if((bucket->flags & IBF_MULTI) != 0) { + kfree(vector); + } else { + vector[0] = old_handler; + vector[1] = vector[2] = vector[3] = NULL; + bucket->irq_info = vector; + bucket->flags |= IBF_MULTI; + } + } + restore_flags(flags); + } else { + /* Just init the bucket */ + bucket = __bucket(build_irq(pil, inofixup, iclr, imap)); + } if (need_dma_sync) bucket->flags |= IBF_DMA_SYNC; @@ -392,6 +487,20 @@ } #endif +static void atomic_bucket_insert(struct ino_bucket *bucket) +{ + unsigned long pstate; + unsigned int *ent; + + __asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate)); + __asm__ __volatile__("wrpr %0, %1, %%pstate" + : : "r" (pstate), "i" (PSTATE_IE)); + ent = irq_work(smp_processor_id(), bucket->pil); + bucket->irq_chain = *ent; + *ent = __irq(bucket); + __asm__ __volatile__("wrpr %0, 0x0, %%pstate" : : "r" (pstate)); +} + int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), unsigned long irqflags, const char *name, void *dev_id) { @@ -400,11 +509,16 @@ unsigned long flags; int pending = 0; - if (irq < 0x400000 || (irq & 0x80000000)) { - prom_printf("request_irq with old style irq %08x %016lx\n", irq, handler); - prom_halt(); - } - + if ((bucket != &pil0_dummy_bucket) && + (bucket < &ivector_table[0] || + bucket >= &ivector_table[NUM_IVECS])) { + unsigned int *caller; + + __asm__ __volatile__("mov %%i7, %0" : "=r" (caller)); + printk(KERN_CRIT "request_irq: Old style IRQ registry attempt " + "from %p, irq %08x.\n", caller, irq); + return -EINVAL; + } if(!handler) return -EINVAL; @@ -429,24 +543,26 @@ } } + save_and_cli(flags); + action = *(bucket->pil + irq_action); if(action) { if((action->flags & SA_SHIRQ) && (irqflags & SA_SHIRQ)) for (tmp = action; tmp->next; tmp = tmp->next) ; - else + else { + restore_flags(flags); return -EBUSY; - + } if((action->flags & SA_INTERRUPT) ^ (irqflags & SA_INTERRUPT)) { printk("Attempt to mix fast and slow interrupts on IRQ%d " "denied\n", bucket->pil); + restore_flags(flags); return -EBUSY; } action = NULL; /* Or else! */ } - save_and_cli(flags); - /* If this is flagged as statically allocated then we use our * private struct which is never freed. */ @@ -466,12 +582,65 @@ return -ENOMEM; } - if (irqflags & SA_IMAP_MASKED) { - pending = ((ivector_to_mask[bucket->ino] & 0x80000000) != 0); - ivector_to_mask[bucket->ino] = (1 << bucket->pil); - if(pending) - ivector_to_mask[bucket->ino] |= 0x80000000; + if ((irqflags & SA_IMAP_MASKED) == 0) { + bucket->irq_info = action; bucket->flags |= IBF_ACTIVE; + } else { + if((bucket->flags & IBF_ACTIVE) != 0) { + void *orig = bucket->irq_info; + void **vector = NULL; + + if((bucket->flags & IBF_PCI) == 0) { + printk("IRQ: Trying to share non-PCI bucket.\n"); + goto free_and_ebusy; + } + if((bucket->flags & IBF_MULTI) == 0) { + vector = kmalloc(sizeof(void *) * 4, GFP_KERNEL); + if(vector == NULL) + goto free_and_enomem; + + /* We might have slept. */ + if ((bucket->flags & IBF_MULTI) != 0) { + int ent; + + kfree(vector); + vector = (void **)bucket->irq_info; + for(ent = 0; ent < 4; ent++) { + if (vector[ent] == NULL) { + vector[ent] = action; + break; + } + } + if (ent == 4) + goto free_and_ebusy; + } else { + vector[0] = orig; + vector[1] = action; + vector[2] = NULL; + vector[3] = NULL; + bucket->irq_info = vector; + bucket->flags |= IBF_MULTI; + } + } else { + int ent; + + vector = (void **)orig; + for(ent = 0; ent < 4; ent++) { + if(vector[ent] == NULL) { + vector[ent] = action; + break; + } + } + if (ent == 4) + goto free_and_ebusy; + } + } else { + bucket->irq_info = action; + bucket->flags |= IBF_ACTIVE; + } + pending = bucket->pending; + if(pending) + bucket->pending = 0; } action->mask = (unsigned long) bucket; @@ -489,15 +658,26 @@ enable_irq(irq); /* We ate the IVEC already, this makes sure it does not get lost. */ - if(pending) + if(pending) { + atomic_bucket_insert(bucket); set_softint(1 << bucket->pil); - + } restore_flags(flags); + #ifdef __SMP__ - if(irqs_have_been_distributed) - distribute_irqs(); + distribute_irqs(); #endif return 0; + +free_and_ebusy: + kfree(action); + restore_flags(flags); + return -EBUSY; + +free_and_enomem: + kfree(action); + restore_flags(flags); + return -ENOMEM; } void free_irq(unsigned int irq, void *dev_id) @@ -507,9 +687,15 @@ unsigned long flags; struct ino_bucket *bucket = __bucket(irq), *bp; - if (irq < 0x400000 || (irq & 0x80000000)) { - prom_printf("free_irq with old style irq %08x\n", irq); - prom_halt(); + if ((bucket != &pil0_dummy_bucket) && + (bucket < &ivector_table[0] || + bucket >= &ivector_table[NUM_IVECS])) { + unsigned int *caller; + + __asm__ __volatile__("mov %%i7, %0" : "=r" (caller)); + printk(KERN_CRIT "free_irq: Old style IRQ removal attempt " + "from %p, irq %08x.\n", caller, irq); + return; } action = *(bucket->pil + irq_action); @@ -545,27 +731,59 @@ *(bucket->pil + irq_action) = action->next; if(action->flags & SA_IMAP_MASKED) { - unsigned int *imap = __imap(bucket); + unsigned int *imap = bucket->imap; + void **vector, *orig; + int ent; + + orig = bucket->irq_info; + vector = (void **)orig; + + if ((bucket->flags & IBF_MULTI) != 0) { + int other = 0; + void *orphan = NULL; + for(ent = 0; ent < 4; ent++) { + if(vector[ent] == action) + vector[ent] = NULL; + else if(vector[ent] != NULL) { + orphan = vector[ent]; + other++; + } + } - /* - * Only free when no other shared irq uses this bucket. - */ - tmp = *(bucket->pil + irq_action); - for( ; tmp; tmp = tmp->next) - if ((struct ino_bucket *)tmp->mask == bucket) + /* Only free when no other shared irq + * uses this bucket. + */ + if(other) { + if (other == 1) { + /* Convert back to non-shared bucket. */ + bucket->irq_info = orphan; + bucket->flags &= ~(IBF_MULTI); + kfree(vector); + } goto out; + } + } else { + bucket->irq_info = NULL; + } - ivector_to_mask[bucket->ino] = 0; - + /* This unique interrupt source is now inactive. */ bucket->flags &= ~IBF_ACTIVE; - for (bp = bucket_base; bp < endbuckets; bp++) - if (__imap(bp) == imap && (bp->flags & IBF_ACTIVE)) + + /* See if any other buckets share this bucket's IMAP + * and are still active. + */ + for(ent = 0; ent < NUM_IVECS; ent++) { + bp = &ivector_table[ent]; + if(bp != bucket && + bp->imap == imap && + (bp->flags & IBF_ACTIVE) != 0) break; - /* - * Only disable when no other sub-irq levels of - * the same imap are active. + } + + /* Only disable when no other sub-irq levels of + * the same IMAP are active. */ - if (bp == endbuckets) + if (ent == NUM_IVECS) disable_irq(irq); } @@ -607,10 +825,10 @@ int cpu = smp_processor_id(); printk("\n%s, CPU %d:\n", str, cpu); - printk("irq: %d [%d %d]\n", + printk("irq: %d [%ld %ld]\n", atomic_read(&global_irq_count), cpu_data[0].irq_count, cpu_data[1].irq_count); - printk("bh: %d [%d %d]\n", + printk("bh: %d [%ld %ld]\n", (spin_is_locked(&global_bh_count) ? 1 : 0), cpu_data[0].bh_count, cpu_data[1].bh_count); } @@ -755,57 +973,56 @@ #endif /* __SMP__ */ -void report_spurious_ivec(struct pt_regs *regs) +void catch_disabled_ivec(struct pt_regs *regs) { - extern unsigned long ivec_spurious_cookie; - -#if 0 - printk("IVEC: Spurious interrupt vector (%016lx) received at (%016lx)\n", - ivec_spurious_cookie, regs->tpc); -#endif + int cpu = smp_processor_id(); + struct ino_bucket *bucket = __bucket(*irq_work(cpu, 0)); /* We can actually see this on Ultra/PCI PCI cards, which are bridges * to other devices. Here a single IMAP enabled potentially multiple * unique interrupt sources (which each do have a unique ICLR register. * * So what we do is just register that the IVEC arrived, when registered - * for real the request_irq() code will check the high bit and signal + * for real the request_irq() code will check the bit and signal * a local CPU interrupt for it. */ - ivector_to_mask[ivec_spurious_cookie] |= (0x80000000); +#if 0 + printk("IVEC: Spurious interrupt vector (%x) received at (%016lx)\n", + bucket - &ivector_table[0], regs->tpc); +#endif + *irq_work(cpu, 0) = 0; + bucket->pending = 1; } -void unexpected_irq(int irq, void *dev_cookie, struct pt_regs *regs) -{ - int i; - struct irqaction *action; - unsigned int cpu_irq; - - cpu_irq = irq & NR_IRQS; - action = *(cpu_irq + irq_action); - - prom_printf("Unexpected IRQ[%d]: ", irq); - prom_printf("PC[%016lx] NPC[%016lx] FP[%016lx]\n", - regs->tpc, regs->tnpc, regs->u_regs[14]); - - if(action) { - prom_printf("Expecting: "); - for(i = 0; i < 16; i++) { - if(action->handler) - prom_printf("[%s:%d:0x%016lx] ", action->name, - i, (unsigned long) action->handler); - } - } - prom_printf("AIEEE\n"); - prom_printf("bogus interrupt received\n"); - prom_cmdline (); -} +/* Tune this... */ +#define FORWARD_VOLUME 12 void handler_irq(int irq, struct pt_regs *regs) { - struct ino_bucket *bucket = NULL; - struct irqaction *action, *act; + struct ino_bucket *bp, *nbp; int cpu = smp_processor_id(); +#ifdef __SMP__ + extern int this_is_starfire; + int should_forward = (this_is_starfire == 0 && + irq < 10 && + current->pid != 0); + unsigned int buddy = 0; + + /* 'cpu' is the MID (ie. UPAID), calculate the MID + * of our buddy. + */ + if(should_forward != 0) { + buddy = cpu_number_map[cpu] + 1; + if (buddy >= NR_CPUS || + (buddy = cpu_logical_map(buddy)) == -1) + buddy = cpu_logical_map(0); + + /* Voo-doo programming. */ + if(cpu_data[buddy].idle_volume < FORWARD_VOLUME) + should_forward = 0; + buddy <<= 26; + } +#endif #ifndef __SMP__ /* @@ -817,30 +1034,55 @@ clear_softint(1 << irq); irq_enter(cpu, irq); - action = *(irq + irq_action); kstat.irqs[cpu][irq]++; - if(!action) { - unexpected_irq(irq, 0, regs); - } else { - act = action; - do { - if(act->flags & SA_IMAP_MASKED) { - bucket = (struct ino_bucket *)act->mask; - if(!(ivector_to_mask[bucket->ino] & 0x80000000)) - continue; + + /* Sliiiick... */ +#ifndef __SMP__ + bp = ((irq != 0) ? + __bucket(xchg32(irq_work(cpu, irq), 0)) : + &pil0_dummy_bucket); +#else + bp = __bucket(xchg32(irq_work(cpu, irq), 0)); +#endif + for( ; bp != NULL; bp = nbp) { + unsigned char flags = bp->flags; + + nbp = __bucket(bp->irq_chain); + if((flags & IBF_ACTIVE) != 0) { + if((flags & IBF_MULTI) == 0) { + struct irqaction *ap = bp->irq_info; + ap->handler(__irq(bp), ap->dev_id, regs); + } else { + void **vector = (void **)bp->irq_info; + int ent; + for(ent = 0; ent < 4; ent++) { + struct irqaction *ap = vector[ent]; + if(ap != NULL) + ap->handler(__irq(bp), ap->dev_id, regs); + } } - act->handler(__irq(bucket), act->dev_id, regs); - } while((act = act->next) != NULL); - act = action; - do { - if(act->flags & SA_IMAP_MASKED) { - bucket = (struct ino_bucket *)act->mask; - if(!(ivector_to_mask[bucket->ino] & 0x80000000)) - continue; - ivector_to_mask[bucket->ino] &= ~(0x80000000); - *(bucket->iclr) = SYSIO_ICLR_IDLE; + /* Only the dummy bucket lacks IMAP/ICLR. */ + if(bp->pil != 0) { +#ifdef __SMP__ + /* Ok, here is what is going on: + * 1) Retargeting IRQs on Starfire is very + * expensive so just forget about it on them. + * 2) Moving around very high priority interrupts + * is a losing game. + * 3) If the current cpu is idle, interrupts are + * useful work, so keep them here. But do not + * pass to our neighbour if he is not very idle. + */ + if (should_forward != 0) { + /* Push it to our buddy. */ + should_forward = 0; + *(bp->imap) = (buddy | SYSIO_IMAP_VALID); + } +#endif + *(bp->iclr) = SYSIO_ICLR_IDLE; } - } while((act = act->next) != NULL); + } else + bp->pending = 1; } irq_exit(cpu, irq); } @@ -856,10 +1098,13 @@ irq_enter(cpu, irq); kstat.irqs[cpu][irq]++; + + *(irq_work(cpu, irq)) = 0; bucket = (struct ino_bucket *)action->mask; + floppy_interrupt(irq, dev_cookie, regs); - ivector_to_mask[bucket->ino] &= ~(0x80000000); *(bucket->iclr) = SYSIO_ICLR_IDLE; + irq_exit(cpu, irq); } #endif @@ -897,11 +1142,21 @@ struct ino_bucket *bucket = __bucket(irq); unsigned long flags; - if (irq < 0x400000 || (irq & 0x80000000)) { - prom_printf("request_irq with old style irq %08x %016lx\n", irq, handler); - prom_halt(); - } + /* No pil0 dummy buckets allowed here. */ + if (bucket < &ivector_table[0] || + bucket >= &ivector_table[NUM_IVECS]) { + unsigned int *caller; + + __asm__ __volatile__("mov %%i7, %0" : "=r" (caller)); + printk(KERN_CRIT "request_fast_irq: Old style IRQ registry attempt " + "from %p, irq %08x.\n", caller, irq); + return -EINVAL; + } + /* Only IMAP style interrupts can be registered as fast. */ + if(bucket->pil == 0) + return -EINVAL; + if(!handler) return -EINVAL; @@ -919,6 +1174,7 @@ printk("request_fast_irq: Trying to register yet already owned.\n"); return -EBUSY; } + save_and_cli(flags); if(irqflags & SA_STATIC_ALLOC) { if(static_irq_count < MAX_STATIC_ALLOC) @@ -936,7 +1192,8 @@ } install_fast_irq(bucket->pil, handler); - ivector_to_mask[bucket->ino] = (1 << bucket->pil); + bucket->irq_info = action; + bucket->flags |= IBF_ACTIVE; action->mask = (unsigned long) bucket; action->handler = handler; @@ -949,9 +1206,9 @@ enable_irq(irq); restore_flags(flags); + #ifdef __SMP__ - if(irqs_have_been_distributed) - distribute_irqs(); + distribute_irqs(); #endif return 0; } @@ -1025,50 +1282,51 @@ } #ifdef __SMP__ -/* Called from smp_commence, when we know how many cpus are in the system - * and can have device IRQ's directed at them. - */ -/* #define SMP_IRQ_VERBOSE */ -void distribute_irqs(void) +static int retarget_one_irq(struct irqaction *p, int goal_cpu) +{ + extern int this_is_starfire; + struct ino_bucket *bucket = __bucket(p->mask); + unsigned int *imap = bucket->imap; + unsigned int tid; + + /* Never change this, it causes problems on Ex000 systems. */ + if (bucket->pil == 12) + return goal_cpu; + + if(this_is_starfire == 0) { + tid = __cpu_logical_map[goal_cpu] << 26; + } else { + extern unsigned int starfire_translate(unsigned int *imap, + unsigned int upaid); + + tid = (starfire_translate(imap, __cpu_logical_map[goal_cpu]) << 26); + } + *imap = SYSIO_IMAP_VALID | (tid & SYSIO_IMAP_TID); + + goal_cpu++; + if(goal_cpu >= NR_CPUS || + __cpu_logical_map[goal_cpu] == -1) + goal_cpu = 0; + return goal_cpu; +} + +/* Called from request_irq. */ +static void distribute_irqs(void) { unsigned long flags; int cpu, level; -#ifdef SMP_IRQ_VERBOSE - printk("SMP: redistributing interrupts...\n"); -#endif save_and_cli(flags); cpu = 0; for(level = 0; level < NR_IRQS; level++) { struct irqaction *p = irq_action[level]; - while(p) { - if(p->flags & SA_IMAP_MASKED) { - struct ino_bucket *bucket = (struct ino_bucket *)p->mask; - unsigned int *imap = __imap(bucket); - unsigned int val; - unsigned long tid = __cpu_logical_map[cpu] << 26; - - val = *imap; - *imap = SYSIO_IMAP_VALID | (tid & SYSIO_IMAP_TID); - -#ifdef SMP_IRQ_VERBOSE - printk("SMP: Redirecting IGN[%x] INO[%x] " - "to cpu %d [%s]\n", - (val & SYSIO_IMAP_IGN) >> 6, - (val & SYSIO_IMAP_INO), cpu, - p->name); -#endif - - cpu++; - if (cpu >= NR_CPUS || __cpu_logical_map[cpu] == -1) - cpu = 0; - } + if(p->flags & SA_IMAP_MASKED) + cpu = retarget_one_irq(p, cpu); p = p->next; } } restore_flags(flags); - irqs_have_been_distributed = 1; } #endif @@ -1146,13 +1404,13 @@ static int called = 0; if (called == 0) { - int i; - called = 1; map_prom_timers(); kill_prom_timer(); - for(i = 0; i < NUM_IVECS; i++) - ivector_to_mask[i] = 0; + memset(&ivector_table[0], 0, sizeof(ivector_table)); +#ifndef __SMP__ + memset(&__up_workvec[0], 0, sizeof(__up_workvec)); +#endif } /* We need to clear any IRQ's pending in the soft interrupt diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/itlb_base.S linux/arch/sparc64/kernel/itlb_base.S --- v2.2.3/linux/arch/sparc64/kernel/itlb_base.S Thu Aug 6 14:06:30 1998 +++ linux/arch/sparc64/kernel/itlb_base.S Wed Mar 10 16:53:37 1999 @@ -1,4 +1,4 @@ -/* $Id: itlb_base.S,v 1.5 1998/06/15 16:59:32 jj Exp $ +/* $Id: itlb_base.S,v 1.7 1999/03/02 15:42:12 jj Exp $ * itlb_base.S: Front end to ITLB miss replacement strategy. * This is included directly into the trap table. * @@ -15,11 +15,9 @@ * 2) All user instruction misses. * * All real page faults merge their code paths to the - * sparc64_realfault_* labels below. + * sparc64_realfault_common label below. */ - .globl sparc64_vpte_patchme - /* ITLB ** ICACHE line 1: Quick user TLB misses */ ldxa [%g1 + %g1] ASI_IMMU, %g4 ! Get TAG_ACCESS srax %g4, VPTE_SHIFT, %g6 ! Create VPTE offset @@ -42,28 +40,25 @@ /* ITLB ** ICACHE line 3: Real faults */ rdpr %tpc, %g5 ! And load faulting VA + clr %g4 ! It was read sparc64_realfault_common: ! Called by TL0 dtlb_miss too sethi %hi(1f), %g7 ! Save state ba,pt %xcc, etrap ! ... 1: or %g7, %lo(1b), %g7 ! ... - clr %o2 ! It was read -sparc64_realfault_continue: ! Called by dtlb_prot handler + mov %l4, %o2 ! Read/Write/No idea srlx %l5, PAGE_SHIFT, %o1 ! Page align faulting VA add %sp, STACK_BIAS + REGWIN_SZ, %o0! Compute pt_regs arg - call do_sparc64_fault ! Call fault handler /* ITLB ** ICACHE line 4: Call fault processing code */ + call do_sparc64_fault ! Call fault handler sllx %o1, PAGE_SHIFT, %o1 ! Finish page alignment ba,a,pt %xcc, rtrap_clr_l6 ! Restore cpu state + nop winfix_trampoline: rdpr %tpc, %g3 ! Prepare winfixup TNPC or %g3, 0x7c, %g3 ! Compute offset to branch wrpr %g3, %tnpc ! Write it into TNPC done ! Do it to it -sparc64_vpte_nucleus: - ba,pt %xcc, sparc64_vpte_continue ! Part of dtlb_backend -sparc64_vpte_patchme: - sethi %hi(0), %g5 ! This has to be patched #undef TAG_CONTEXT_BITS #undef VPTE_SHIFT diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/process.c linux/arch/sparc64/kernel/process.c --- v2.2.3/linux/arch/sparc64/kernel/process.c Sun Nov 8 14:02:46 1998 +++ linux/arch/sparc64/kernel/process.c Sun Mar 21 18:37:56 1999 @@ -1,4 +1,4 @@ -/* $Id: process.c,v 1.82 1998/10/19 21:52:23 davem Exp $ +/* $Id: process.c,v 1.90 1999/03/22 02:12:16 davem Exp $ * arch/sparc64/kernel/process.c * * Copyright (C) 1995, 1996 David S. Miller (davem@caip.rutgers.edu) @@ -53,11 +53,17 @@ /* endless idle loop with no priority at all */ current->priority = 0; - current->counter = 0; + current->counter = -100; for (;;) { - check_pgt_cache(); - run_task_queue(&tq_scheduler); + /* If current->need_resched is zero we should really + * setup for a system wakup event and execute a shutdown + * instruction. + * + * But this requires writing back the contents of the + * L2 cache etc. so implement this later. -DaveM + */ schedule(); + check_pgt_cache(); } return 0; } @@ -67,20 +73,25 @@ /* * the idle loop on a UltraMultiPenguin... */ +#define idle_me_harder() (cpu_data[current->processor].idle_volume += 1) +#define unidle_me() (cpu_data[current->processor].idle_volume = 0) asmlinkage int cpu_idle(void) { current->priority = 0; + current->counter = -100; while(1) { - struct task_struct *p; - - check_pgt_cache(); - run_task_queue(&tq_scheduler); - current->counter = 0; - if (current->need_resched != 0 || - ((p = init_task.next_run) != NULL && - (p->processor == smp_processor_id() || - (p->tss.flags & SPARC_FLAG_NEWCHILD) != 0))) + if (current->need_resched != 0) { + unidle_me(); schedule(); + check_pgt_cache(); + } + idle_me_harder(); + + /* The store ordering is so that IRQ handlers on + * other cpus see our increasing idleness for the buddy + * redistribution algorithm. -DaveM + */ + membar("#StoreStore | #StoreLoad"); } } @@ -158,12 +169,12 @@ } rw = &r_w; set_fs (old_fs); - printk("l0: %016x l1: %016x l2: %016x l3: %016x\n" - "l4: %016x l5: %016x l6: %016x l7: %016x\n", + printk("l0: %08x l1: %08x l2: %08x l3: %08x " + "l4: %08x l5: %08x l6: %08x l7: %08x\n", rw->locals[0], rw->locals[1], rw->locals[2], rw->locals[3], rw->locals[4], rw->locals[5], rw->locals[6], rw->locals[7]); - printk("i0: %016x i1: %016x i2: %016x i3: %016x\n" - "i4: %016x i5: %016x i6: %016x i7: %016x\n", + printk("i0: %08x i1: %08x i2: %08x i3: %08x " + "i4: %08x i5: %08x i6: %08x i7: %08x\n", rw->ins[0], rw->ins[1], rw->ins[2], rw->ins[3], rw->ins[4], rw->ins[5], rw->ins[6], rw->ins[7]); } @@ -340,13 +351,13 @@ { printk("PSR: %08x PC: %08x NPC: %08x Y: %08x\n", regs->psr, regs->pc, regs->npc, regs->y); - printk("g0: %08x g1: %08x g2: %08x g3: %08x\n", + printk("g0: %08x g1: %08x g2: %08x g3: %08x ", regs->u_regs[0], regs->u_regs[1], regs->u_regs[2], regs->u_regs[3]); printk("g4: %08x g5: %08x g6: %08x g7: %08x\n", regs->u_regs[4], regs->u_regs[5], regs->u_regs[6], regs->u_regs[7]); - printk("o0: %08x o1: %08x o2: %08x o3: %08x\n", + printk("o0: %08x o1: %08x o2: %08x o3: %08x ", regs->u_regs[8], regs->u_regs[9], regs->u_regs[10], regs->u_regs[11]); printk("o4: %08x o5: %08x sp: %08x ret_pc: %08x\n", @@ -427,9 +438,7 @@ /* exec_mmap() set context to NO_CONTEXT, here is * where we grab a new one. */ - current->mm->cpu_vm_mask = 0; activate_context(current); - current->mm->cpu_vm_mask = (1UL<tss.flags & SPARC_FLAG_32BIT) __asm__ __volatile__("stxa %%g0, [%0] %1" @@ -447,6 +456,11 @@ { unsigned long fp, distance, rval; + /* do_fork() grabs the parent semaphore, we must release it + * temporarily so we can build the child clone stack frame + * without deadlocking. + */ + up(¤t->mm->mmap_sem); if(!(current->tss.flags & SPARC_FLAG_32BIT)) { csp += STACK_BIAS; psp += STACK_BIAS; @@ -463,17 +477,20 @@ distance = fp - psp; rval = (csp - distance); if(copy_in_user(rval, psp, distance)) - return 0; - if(current->tss.flags & SPARC_FLAG_32BIT) { + rval = 0; + else if(current->tss.flags & SPARC_FLAG_32BIT) { if(put_user(((u32)csp), &(((struct reg_window32 *)rval)->ins[6]))) - return 0; - return rval; + rval = 0; } else { if(put_user(((u64)csp - STACK_BIAS), &(((struct reg_window *)rval)->ins[6]))) - return 0; - return rval - STACK_BIAS; + rval = 0; + else + rval = rval - STACK_BIAS; } + down(¤t->mm->mmap_sem); + + return rval; } /* Standard stuff. */ @@ -621,6 +638,37 @@ /* Set the second return value for the parent. */ regs->u_regs[UREG_I1] = 0; return 0; +} + +/* + * This is the mechanism for creating a new kernel thread. + * + * NOTE! Only a kernel-only process(ie the swapper or direct descendants + * who haven't done an "execve()") should use this: it will work within + * a system call from a "real" process, but the process memory space will + * not be free'd until both the parent and the child have exited. + */ +pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) +{ + long retval; + + __asm__ __volatile("mov %1, %%g1\n\t" + "mov %2, %%o0\n\t" /* Clone flags. */ + "mov 0, %%o1\n\t" /* usp arg == 0 */ + "t 0x6d\n\t" /* Linux/Sparc clone(). */ + "brz,a,pn %%o1, 1f\n\t" /* Parent, just return. */ + " mov %%o0, %0\n\t" + "jmpl %4, %%o7\n\t" /* Call the function. */ + " mov %5, %%o0\n\t" /* Set arg in delay. */ + "mov %3, %%g1\n\t" + "t 0x6d\n\t" /* Linux/Sparc exit(). */ + /* Notreached by child. */ + "1:" : + "=r" (retval) : + "i" (__NR_clone), "r" (flags | CLONE_VM), + "i" (__NR_exit), "r" (fn), "r" (arg) : + "g1", "o0", "o1", "memory", "cc"); + return retval; } /* diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/psycho.c linux/arch/sparc64/kernel/psycho.c --- v2.2.3/linux/arch/sparc64/kernel/psycho.c Thu Nov 19 09:56:27 1998 +++ linux/arch/sparc64/kernel/psycho.c Fri Mar 19 07:57:45 1999 @@ -1,8 +1,9 @@ -/* $Id: psycho.c,v 1.66 1998/11/02 22:27:45 davem Exp $ +/* $Id: psycho.c,v 1.79 1999/03/19 05:38:46 davem Exp $ * psycho.c: Ultra/AX U2P PCI controller support. * * Copyright (C) 1997 David S. Miller (davem@caipfs.rutgers.edu) * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) + * Copyright (C) 1999 Jakub Jelinek (jj@ultra.linux.cz) */ #include @@ -29,14 +30,13 @@ #define dprintf printk #endif - unsigned long pci_dvma_offset = 0x00000000UL; unsigned long pci_dvma_mask = 0xffffffffUL; +#define PCI_DVMA_HASH_NONE 0xffffffffffffffffUL unsigned long pci_dvma_v2p_hash[PCI_DVMA_HASHSZ]; unsigned long pci_dvma_p2v_hash[PCI_DVMA_HASHSZ]; - #ifndef CONFIG_PCI int pcibios_present(void) @@ -74,9 +74,12 @@ #include #include +#define PSYCHO_REORDER_ONBOARDFIRST 1 + struct linux_psycho *psycho_root = NULL; int linux_num_psycho = 0; static struct linux_pbm_info *bus2pbm[256]; +static int psycho_reorder __initdata = 0; static int pbm_read_config_byte(struct linux_pbm_info *pbm, unsigned char bus, unsigned char devfn, @@ -112,8 +115,10 @@ pci_dvma_p2v_hash[pci_dvma_ahashfn(dvma_addr)] = vaddr - dvma_addr; } -__initfunc(static void psycho_iommu_init(struct linux_psycho *psycho, int tsbsize)) +static void __init psycho_iommu_init(struct linux_psycho *psycho, int tsbsize) { + extern int this_is_starfire; + extern void *starfire_hookup(int); struct linux_mlist_p1275 *mlist; unsigned long tsbbase; unsigned long control, i, n; @@ -137,37 +142,77 @@ break; } tsbbase = __get_free_pages(GFP_DMA, order); + if (!tsbbase) { + prom_printf("IOMMU: Error, kmalloc(tsb) failed.\n"); + prom_halt(); + } iopte = (unsigned long *)tsbbase; - memset(pci_dvma_v2p_hash, 0, sizeof(pci_dvma_v2p_hash)); - memset(pci_dvma_p2v_hash, 0, sizeof(pci_dvma_p2v_hash)); + /* Initialize to "none" settings. */ + for(i = 0; i < PCI_DVMA_HASHSZ; i++) { + pci_dvma_v2p_hash[i] = PCI_DVMA_HASH_NONE; + pci_dvma_p2v_hash[i] = PCI_DVMA_HASH_NONE; + } n = 0; mlist = *prom_meminfo()->p1275_totphys; while (mlist) { unsigned long paddr = mlist->start_adr; + unsigned long num_bytes = mlist->num_bytes; + + if(paddr >= (((unsigned long) high_memory) - PAGE_OFFSET)) + goto next; + + if((paddr + num_bytes) >= (((unsigned long) high_memory) - PAGE_OFFSET)) + num_bytes = (((unsigned long) high_memory) - PAGE_OFFSET) - paddr; + + /* Align base and length so we map whole hash table sized chunks + * at a time (and therefore full 64K IOMMU pages). + */ + paddr &= ~((1UL << 24UL) - 1); + num_bytes = (num_bytes + ((1UL << 24UL) - 1)) & ~((1UL << 24) - 1); - for (i = 0; i < (mlist->num_bytes >> 16); i++) { + /* Move up the base for mappings already created. */ + while(pci_dvma_v2p_hash[pci_dvma_ahashfn(paddr)] != + PCI_DVMA_HASH_NONE) { + paddr += (1UL << 24UL); + num_bytes -= (1UL << 24UL); + if(num_bytes == 0UL) + goto next; + } + + /* Move down the size for tail mappings already created. */ + while(pci_dvma_v2p_hash[pci_dvma_ahashfn(paddr + num_bytes - (1UL << 24UL))] != + PCI_DVMA_HASH_NONE) { + num_bytes -= (1UL << 24UL); + if(num_bytes == 0UL) + goto next; + } + /* Now map the rest. */ + for (i = 0; i < ((num_bytes + ((1 << 16) - 1)) >> 16); i++) { *iopte = (IOPTE_VALID | IOPTE_64K | IOPTE_CACHE | IOPTE_WRITE); *iopte |= paddr; if (!(n & 0xff)) set_dvma_hash(paddr, (n << 16)); - + if (++n > (tsbsize * 1024)) goto out; paddr += (1 << 16); iopte++; } - + next: mlist = mlist->theres_more; } out: - if (mlist) - printk("WARNING: not all physical memory mapped in IOMMU\n"); + if (mlist) { + prom_printf("WARNING: not all physical memory mapped in IOMMU\n"); + prom_printf("Try booting with mem=xxxM or similar\n"); + prom_halt(); + } psycho->psycho_regs->iommu_tsbbase = __pa(tsbbase); @@ -193,6 +238,12 @@ break; } psycho->psycho_regs->iommu_control = control; + + /* If necessary, hook us up for starfire IRQ translations. */ + if(this_is_starfire) + psycho->starfire_cookie = starfire_hookup(psycho->upa_portid); + else + psycho->starfire_cookie = NULL; } extern void prom_pbm_ranges_init(int node, struct linux_pbm_info *pbm); @@ -201,7 +252,7 @@ /* * Poor man's PCI... */ -__initfunc(void sabre_init(int pnode)) +void __init sabre_init(int pnode) { struct linux_prom64_registers pr_regs[2]; struct linux_psycho *sabre; @@ -213,6 +264,10 @@ int bus; sabre = kmalloc(sizeof(struct linux_psycho), GFP_ATOMIC); + if (!sabre) { + prom_printf("SABRE: Error, kmalloc(sabre) failed.\n"); + prom_halt(); + } portid = prom_getintdefault(pnode, "upa-portid", 0xff); @@ -248,9 +303,11 @@ prom_halt(); } - printk("PCI: Found SABRE, main regs at %p\n", sabre->psycho_regs); + printk("PCI: Found SABRE, main regs at %p CTRL[%016lx]\n", + sabre->psycho_regs, sabre->psycho_regs->control); #ifdef PROM_DEBUG - dprintf("PCI: Found SABRE, main regs at %p\n", sabre->psycho_regs); + dprintf("PCI: Found SABRE, main regs at %p CTRL[%016lx]\n", + sabre->psycho_regs, sabre->psycho_regs->control); #endif ctrl = sabre->psycho_regs->pci_a_control; @@ -382,7 +439,7 @@ return psycho->pci_bus ? 1 : 0; } -__initfunc(void pcibios_init(void)) +void __init pcibios_init(void) { struct linux_prom64_registers pr_regs[3]; struct linux_psycho *psycho; @@ -408,8 +465,6 @@ goto next_pci; } - psycho = kmalloc(sizeof(struct linux_psycho), GFP_ATOMIC); - portid = prom_getintdefault(node, "upa-portid", 0xff); for(search = psycho_root; search; search = search->next) { if(search->upa_portid == portid) { @@ -424,6 +479,11 @@ } } + psycho = kmalloc(sizeof(struct linux_psycho), GFP_ATOMIC); + if (!psycho) { + prom_printf("PSYCHO: Error, kmalloc(psycho) failed.\n"); + prom_halt(); + } memset(psycho, 0, sizeof(*psycho)); psycho->next = psycho_root; @@ -494,8 +554,14 @@ is_pbm_a = ((pr_regs[0].phys_addr & 0x6000) == 0x2000); /* Enable arbitration for all PCI slots. */ - psycho->psycho_regs->pci_a_control |= 0x3f; - psycho->psycho_regs->pci_b_control |= 0x3f; + psycho->psycho_regs->pci_a_control |= PSYCHO_PCICTRL_AEN; + psycho->psycho_regs->pci_b_control |= PSYCHO_PCICTRL_AEN; + + /* Disable DMA write / PIO rd synchronization on both + * PCI bus segments. + */ + psycho->psycho_regs->pci_a_diag |= PSYCHO_PCIDIAG_DDWSYNC; + psycho->psycho_regs->pci_b_diag |= PSYCHO_PCIDIAG_DDWSYNC; other_pbm: if(is_pbm_a) @@ -609,8 +675,8 @@ } -__initfunc(static void -pbm_reconfigure_bridges(struct linux_pbm_info *pbm, unsigned char bus)) +static void __init +pbm_reconfigure_bridges(struct linux_pbm_info *pbm, unsigned char bus) { unsigned int devfn, l, class; unsigned char hdr_type = 0; @@ -657,7 +723,7 @@ } } -__initfunc(static void pbm_fixup_busno(struct linux_pbm_info *pbm, unsigned char bus)) +static void __init pbm_fixup_busno(struct linux_pbm_info *pbm, unsigned char bus) { unsigned int nbus; @@ -682,8 +748,7 @@ } while (nbus--); } - -__initfunc(static void apb_init(struct linux_psycho *sabre)) +static void __init apb_init(struct linux_psycho *sabre) { struct pci_dev *pdev; unsigned short stmp; @@ -692,21 +757,20 @@ for(pdev = pci_devices; pdev; pdev = pdev->next) { if(pdev->vendor == PCI_VENDOR_ID_SUN && pdev->device == PCI_DEVICE_ID_SUN_SABRE) { - /* Increase latency timer on top level bridge. */ - pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xf8); + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 128); break; } } for (pdev = sabre->pci_bus->devices; pdev; pdev = pdev->sibling) { if (pdev->vendor == PCI_VENDOR_ID_SUN && pdev->device == PCI_DEVICE_ID_SUN_SIMBA) { - pci_read_config_word(pdev, PCI_COMMAND, &stmp); stmp |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO; pci_write_config_word(pdev, PCI_COMMAND, stmp); + /* Status register bits are "write 1 to clear". */ pci_write_config_word(pdev, PCI_STATUS, 0xffff); pci_write_config_word(pdev, PCI_SEC_STATUS, 0xffff); @@ -721,28 +785,25 @@ APB_PCI_CTL_HIGH_ARBITER_EN; pci_write_config_dword(pdev, APB_PCI_CONTROL_HIGH, itmp); + /* Systems with SIMBA are usually workstations, so + * we configure to park to SIMBA not to the previous + * bus owner. + */ pci_read_config_dword(pdev, APB_PCI_CONTROL_LOW, &itmp); - itmp = APB_PCI_CTL_LOW_ARB_PARK | - APB_PCI_CTL_LOW_ERRINT_EN | 0x0f; + itmp = APB_PCI_CTL_LOW_ERRINT_EN | 0x0f; pci_write_config_dword(pdev, APB_PCI_CONTROL_LOW, itmp); - /* - * Setup Registers for Guaranteed Completion. + /* Don't mess with the retry limit and PIO/DMA latency + * timer settings. But do set primary and secondary + * latency timers. */ - pci_write_config_byte(pdev, APB_PRIMARY_MASTER_RETRY_LIMIT, 0); - pci_write_config_byte(pdev, APB_SECONDARY_MASTER_RETRY_LIMIT, 0); - pci_write_config_byte(pdev, APB_PIO_TARGET_RETRY_LIMIT, 0x80); - pci_write_config_byte(pdev, APB_PIO_TARGET_LATENCY_TIMER, 0); - pci_write_config_byte(pdev, APB_DMA_TARGET_RETRY_LIMIT, 0x80); - pci_write_config_byte(pdev, APB_DMA_TARGET_LATENCY_TIMER, 0); - - /* Increase primary latency timer. */ - pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xf8); + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 128); + pci_write_config_byte(pdev, PCI_SEC_LATENCY_TIMER, 128); } } } -__initfunc(static void sabre_probe(struct linux_psycho *sabre)) +static void __init sabre_probe(struct linux_psycho *sabre) { struct pci_bus *pbus = sabre->pci_bus; static unsigned char busno = 0; @@ -764,7 +825,7 @@ } -__initfunc(static void pbm_probe(struct linux_pbm_info *pbm)) +static void __init pbm_probe(struct linux_pbm_info *pbm) { static struct pci_bus *pchain = NULL; struct pci_bus *pbus = &pbm->pci_bus; @@ -803,9 +864,9 @@ } } -__initfunc(static int pdev_to_pnode_sibtraverse(struct linux_pbm_info *pbm, - struct pci_dev *pdev, - int pnode)) +static int __init pdev_to_pnode_sibtraverse(struct linux_pbm_info *pbm, + struct pci_dev *pdev, + int pnode) { struct linux_prom_pci_registers pregs[PROMREG_MAX]; int node; @@ -827,8 +888,8 @@ return 0; } -__initfunc(static void pdev_cookie_fillin(struct linux_pbm_info *pbm, - struct pci_dev *pdev, int pnode)) +static void __init pdev_cookie_fillin(struct linux_pbm_info *pbm, + struct pci_dev *pdev, int pnode) { struct pcidev_cookie *pcp; int node; @@ -846,9 +907,9 @@ #endif } -__initfunc(static void fill_in_pbm_cookies(struct pci_bus *pbus, - struct linux_pbm_info *pbm, - int node)) +static void __init fill_in_pbm_cookies(struct pci_bus *pbus, + struct linux_pbm_info *pbm, + int node) { struct pci_dev *pdev; @@ -868,7 +929,7 @@ } } -__initfunc(static void sabre_cookie_fillin(struct linux_psycho *sabre)) +static void __init sabre_cookie_fillin(struct linux_psycho *sabre) { struct pci_bus *pbus = sabre->pci_bus; @@ -886,9 +947,9 @@ * properties, and recording them in pci_vma's linked in via * PBM->assignments. */ -__initfunc(static int gimme_ebus_assignments(int node, struct linux_prom_pci_registers *aregs)) +static int __init gimme_ebus_assignments(int node, struct linux_prom_pci_registers *aregs) { - struct linux_prom_ebus_ranges erng[PROMREG_MAX]; + struct linux_prom_ebus_ranges erng[PROM_PCIRNG_MAX]; int err, iter; err = prom_getproperty(node, "ranges", (char *)&erng[0], sizeof(erng)); @@ -911,7 +972,7 @@ return err; } -__initfunc(static void assignment_process(struct linux_pbm_info *pbm, int node)) +static void __init assignment_process(struct linux_pbm_info *pbm, int node) { struct linux_prom_pci_registers aregs[PROMREG_MAX]; char pname[256]; @@ -968,7 +1029,7 @@ } } -__initfunc(static void assignment_walk_siblings(struct linux_pbm_info *pbm, int node)) +static void __init assignment_walk_siblings(struct linux_pbm_info *pbm, int node) { while(node) { int child = prom_getchild(node); @@ -1077,12 +1138,12 @@ #endif } -__initfunc(static void fixup_regs(struct pci_dev *pdev, - struct linux_pbm_info *pbm, - struct linux_prom_pci_registers *pregs, - int nregs, - struct linux_prom_pci_registers *assigned, - int numaa)) +static void __init fixup_regs(struct pci_dev *pdev, + struct linux_pbm_info *pbm, + struct linux_prom_pci_registers *pregs, + int nregs, + struct linux_prom_pci_registers *assigned, + int numaa) { int preg, rng; int IO_seen = 0; @@ -1415,7 +1476,7 @@ #define imap_offset(__member) \ ((unsigned long)(&(((struct psycho_regs *)0)->__member))) -__initfunc(static unsigned long psycho_pcislot_imap_offset(unsigned long ino)) +static unsigned long __init psycho_pcislot_imap_offset(unsigned long ino) { unsigned int bus, slot; @@ -1431,11 +1492,8 @@ case 2: return imap_offset(imap_a_slot2); case 3: - return imap_offset(imap_a_slot3); default: - prom_printf("pcislot_imap: IMPOSSIBLE [%d:%d]\n", - bus, slot); - prom_halt(); + return imap_offset(imap_a_slot3); } } else { switch(slot) { @@ -1446,19 +1504,16 @@ case 2: return imap_offset(imap_b_slot2); case 3: - return imap_offset(imap_b_slot3); default: - prom_printf("pcislot_imap: IMPOSSIBLE [%d:%d]\n", - bus, slot); - prom_halt(); + return imap_offset(imap_b_slot3); } } } /* Exported for EBUS probing layer. */ -__initfunc(unsigned int psycho_irq_build(struct linux_pbm_info *pbm, - struct pci_dev *pdev, - unsigned int ino)) +unsigned int __init psycho_irq_build(struct linux_pbm_info *pbm, + struct pci_dev *pdev, + unsigned int ino) { unsigned long imap_off; int need_dma_sync = 0; @@ -1554,37 +1609,78 @@ return psycho_build_irq(pbm->parent, imap_off, ino, need_dma_sync); } -__initfunc(static int pbm_intmap_match(struct linux_pbm_info *pbm, - struct pci_dev *pdev, - struct linux_prom_pci_registers *preg, - unsigned int *interrupt)) +static int __init pbm_intmap_match(struct linux_pbm_info *pbm, + struct pci_dev *pdev, + struct linux_prom_pci_registers *preg, + unsigned int *interrupt) { struct linux_prom_pci_registers ppreg; unsigned int hi, mid, lo, irq; int i; - if (!pbm->num_pbm_intmap) +#ifdef FIXUP_IRQ_DEBUG + dprintf("pbm_intmap_match: "); +#endif + if (!pbm->num_pbm_intmap) { +#ifdef FIXUP_IRQ_DEBUG + dprintf("No intmap UPA[%x:%c]\n", + pbm->parent->upa_portid, + (pbm == &pbm->parent->pbm_A) ? 'A' : 'B'); +#endif return 0; - + } /* * Underneath a bridge, use register of parent bridge. */ if (pdev->bus->number != pbm->pci_first_busno) { - struct pcidev_cookie *pcp = pdev->bus->self->sysdata; - int node; + struct pcidev_cookie *pcp; + int node, offset; + char prom_name[64]; - if (!pcp) +#ifdef FIXUP_IRQ_DEBUG + dprintf("UnderBridge, "); +#endif + pcp = pdev->bus->self->sysdata; + if (!pcp) { +#ifdef FIXUP_IRQ_DEBUG + dprintf("No bus PCP\n"); +#endif goto out; - + } node = pcp->prom_node; i = prom_getproperty(node, "reg", (char*)&ppreg, sizeof(ppreg)); - if(i == 0 || i == -1) + if(i == 0 || i == -1) { +#ifdef FIXUP_IRQ_DEBUG + dprintf("No reg property.\n"); +#endif goto out; + } + /* + * Did PROM know better and assign an interrupt different + * to #INTA to the device? - We test here for presence of + * FCODE on the card, in this case we assume PROM has set + * correct 'interrupts' property, unless it is quadhme. + */ + pcp = pdev->sysdata; + if (!pcp) { +#ifdef FIXUP_IRQ_DEBUG + dprintf("No dev PCP\n"); +#endif + goto out; + } + node = pcp->prom_node; - /* Use low slot number bits of child as IRQ line. */ - *interrupt = ((pdev->devfn >> 3) & 3) + 1; - + offset = prom_getint(node, "fcode-rom-offset"); + prom_getstring(node, "name", prom_name, sizeof(prom_name)); + if (offset == -1 || + !strcmp(prom_name, "SUNW,qfe") || + !strcmp(prom_name, "qfe")) { + /* + * No, use low slot number bits of child as IRQ line. + */ + *interrupt = ((*interrupt - 1 + PCI_SLOT(pdev->devfn)) & 3) + 1; + } preg = &ppreg; } @@ -1618,10 +1714,10 @@ prom_halt(); } -__initfunc(static void fixup_irq(struct pci_dev *pdev, - struct linux_pbm_info *pbm, - struct linux_prom_pci_registers *preg, - int node)) +static void __init fixup_irq(struct pci_dev *pdev, + struct linux_pbm_info *pbm, + struct linux_prom_pci_registers *preg, + int node) { unsigned int prom_irq, portid = pbm->parent->upa_portid; unsigned char pci_irq_line = pdev->irq; @@ -1721,11 +1817,11 @@ #endif } -__initfunc(static void fixup_doit(struct pci_dev *pdev, - struct linux_pbm_info *pbm, - struct linux_prom_pci_registers *pregs, - int nregs, - int node)) +static void __init fixup_doit(struct pci_dev *pdev, + struct linux_pbm_info *pbm, + struct linux_prom_pci_registers *pregs, + int nregs, + int node) { struct linux_prom_pci_registers assigned[PROMREG_MAX]; int numaa, err; @@ -1745,9 +1841,9 @@ fixup_irq(pdev, pbm, &pregs[0], node); } -__initfunc(static void fixup_pci_dev(struct pci_dev *pdev, - struct pci_bus *pbus, - struct linux_pbm_info *pbm)) +static void __init fixup_pci_dev(struct pci_dev *pdev, + struct pci_bus *pbus, + struct linux_pbm_info *pbm) { struct linux_prom_pci_registers pregs[PROMREG_MAX]; struct pcidev_cookie *pcp = pdev->sysdata; @@ -1762,8 +1858,12 @@ cmd |= PCI_COMMAND_MASTER; pci_write_config_word(pdev, PCI_COMMAND, cmd); - /* Now, set cache line size to 64-bytes. */ - pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 64); + /* Now, set cache line size to 64-bytes. + * NOTE: Cache line size is in 32-bit word units. + */ + pci_write_config_byte(pdev, + PCI_CACHE_LINE_SIZE, + (64 / sizeof(u32))); } /* Ignore if this is one of the PBM's, EBUS, or a @@ -1808,7 +1908,7 @@ } } -__initfunc(static void fixup_pci_bus(struct pci_bus *pbus, struct linux_pbm_info *pbm)) +static void __init fixup_pci_bus(struct pci_bus *pbus, struct linux_pbm_info *pbm) { struct pci_dev *pdev; @@ -1819,7 +1919,7 @@ fixup_pci_bus(pbus, pbm); } -__initfunc(static void fixup_addr_irq(struct linux_pbm_info *pbm)) +static void __init fixup_addr_irq(struct linux_pbm_info *pbm) { struct pci_bus *pbus = &pbm->pci_bus; @@ -1832,7 +1932,7 @@ /* Walk all PCI devices probes, fixing up base registers and IRQ registers. * We use OBP for most of this work. */ -__initfunc(static void psycho_final_fixup(struct linux_psycho *psycho)) +static void __init psycho_final_fixup(struct linux_psycho *psycho) { /* Second, fixup base address registers and IRQ lines... */ if (psycho->pbm_A.parent) @@ -1841,7 +1941,33 @@ fixup_addr_irq(&psycho->pbm_B); } -__initfunc(void pcibios_fixup(void)) +/* Reorder the pci_dev chain, so that onboard devices come first + and then come the pluggable cards. */ +void __init psycho_reorder_devs(void) +{ + struct pci_dev **pci_onboard = &pci_devices; + struct pci_dev **pci_tail = &pci_devices; + struct pci_dev *pdev = pci_devices, *pci_other = NULL; + + while (pdev) { + if (pdev->irq && (__irq_ino(pdev->irq) & 0x20)) { + if (pci_other) { + *pci_onboard = pdev; + pci_onboard = &pdev->next; + pdev = pdev->next; + *pci_onboard = pci_other; + *pci_tail = pdev; + continue; + } else + pci_onboard = &pdev->next; + } else if (!pci_other) + pci_other = pdev; + pci_tail = &pdev->next; + pdev = pdev->next; + } +} + +void __init pcibios_fixup(void) { struct linux_psycho *psycho; @@ -1861,9 +1987,9 @@ for (psycho = psycho_root; psycho; psycho = psycho->next) { /* Probe bus on builtin PCI. */ - if (apb_present(psycho)) + if (apb_present(psycho)) { sabre_probe(psycho); - else { + } else { /* Probe busses under PBM B. */ pbm_probe(&psycho->pbm_B); @@ -1896,6 +2022,9 @@ psycho_final_fixup(psycho); } + if (psycho_reorder & PSYCHO_REORDER_ONBOARDFIRST) + psycho_reorder_devs(); + return ebus_init(); } @@ -2418,12 +2547,20 @@ return err; } -__initfunc(void pcibios_fixup_bus(struct pci_bus *bus)) +void __init pcibios_fixup_bus(struct pci_bus *bus) { } -__initfunc(char *pcibios_setup(char *str)) +char * __init pcibios_setup(char *str) { + if (!strcmp(str, "onboardfirst")) { + psycho_reorder |= PSYCHO_REORDER_ONBOARDFIRST; + return NULL; + } + if (!strcmp(str, "noreorder")) { + psycho_reorder = 0; + return NULL; + } return str; } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/ptrace.c linux/arch/sparc64/kernel/ptrace.c --- v2.2.3/linux/arch/sparc64/kernel/ptrace.c Sun Nov 8 14:02:46 1998 +++ linux/arch/sparc64/kernel/ptrace.c Mon Mar 15 16:10:43 1999 @@ -591,6 +591,8 @@ if (((current->personality & PER_BSD) && (request == PTRACE_SUNATTACH)) || (!(current->personality & PER_BSD) && (request == PTRACE_ATTACH))) { + unsigned long flags; + if(child == current) { /* Try this under SunOS/Solaris, bwa haha * You'll never be able to kill the process. ;-) @@ -602,8 +604,9 @@ (current->uid != child->euid) || (current->uid != child->uid) || (current->gid != child->egid) || - (current->gid != child->gid)) && - !capable(CAP_SYS_PTRACE)) { + (current->gid != child->sgid) || + (cap_issubset(child->cap_permitted, current->cap_permitted)) || + (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE)) { pt_error_return(regs, EPERM); goto out; } @@ -613,15 +616,13 @@ goto out; } child->flags |= PF_PTRACED; + write_lock_irqsave(&tasklist_lock, flags); if(child->p_pptr != current) { - unsigned long flags; - - write_lock_irqsave(&tasklist_lock, flags); REMOVE_LINKS(child); child->p_pptr = current; SET_LINKS(child); - write_unlock_irqrestore(&tasklist_lock, flags); } + write_unlock_irqrestore(&tasklist_lock, flags); send_sig(SIGSTOP, child, 1); pt_succ_return(regs, 0); goto out; @@ -670,14 +671,18 @@ pt_error_return(regs, EINVAL); goto out; } + down(&child->mm->mmap_sem); res = read_int(child, addr, &x); + up(&child->mm->mmap_sem); tmp = x; } else { if(addr & (sizeof(unsigned long) - 1)) { pt_error_return(regs, EINVAL); goto out; } + down(&child->mm->mmap_sem); res = read_long(child, addr, &tmp); + up(&child->mm->mmap_sem); } if (res < 0) { pt_error_return(regs, -res); @@ -709,13 +714,17 @@ pt_error_return(regs, EINVAL); goto out; } + down(&child->mm->mmap_sem); res = write_int(child, addr, data); + up(&child->mm->mmap_sem); } else { if(addr & (sizeof(unsigned long) - 1)) { pt_error_return(regs, EINVAL); goto out; } + down(&child->mm->mmap_sem); res = write_long(child, addr, data); + up(&child->mm->mmap_sem); } if(res < 0) pt_error_return(regs, -res); @@ -944,12 +953,15 @@ unsigned long page; while(len) { + down(&child->mm->mmap_sem); vma = find_extend_vma(child, src); if (!vma) { + up(&child->mm->mmap_sem); pt_error_return(regs, EIO); goto flush_and_out; } pgtable = get_page (child, vma, src, 0); + up(&child->mm->mmap_sem); if (src & ~PAGE_MASK) { curlen = PAGE_SIZE - (src & ~PAGE_MASK); if (curlen > len) curlen = len; @@ -988,12 +1000,15 @@ unsigned long page; while(len) { + down(&child->mm->mmap_sem); vma = find_extend_vma(child, dest); if (!vma) { + up(&child->mm->mmap_sem); pt_error_return(regs, EIO); goto flush_and_out; } pgtable = get_page (child, vma, dest, 1); + up(&child->mm->mmap_sem); if (dest & ~PAGE_MASK) { curlen = PAGE_SIZE - (dest & ~PAGE_MASK); if (curlen > len) curlen = len; diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/setup.c linux/arch/sparc64/kernel/setup.c --- v2.2.3/linux/arch/sparc64/kernel/setup.c Sun Nov 8 14:02:47 1998 +++ linux/arch/sparc64/kernel/setup.c Wed Mar 10 16:53:37 1999 @@ -1,4 +1,4 @@ -/* $Id: setup.c,v 1.37 1998/10/14 15:49:09 ecd Exp $ +/* $Id: setup.c,v 1.41 1999/01/04 20:12:25 davem Exp $ * linux/arch/sparc64/kernel/setup.c * * Copyright (C) 1995,1996 David S. Miller (davem@caip.rutgers.edu) @@ -277,6 +277,22 @@ #endif static unsigned long memory_size = 0; +#ifdef PROM_DEBUG_CONSOLE +static struct console prom_debug_console = { + "debug", + prom_console_write, + NULL, + NULL, + NULL, + NULL, + NULL, + CON_PRINTBUFFER, + -1, + 0, + NULL +}; +#endif + /* XXX Implement this at some point... */ void kernel_enter_debugger(void) { @@ -430,6 +446,10 @@ *cmdline_p = prom_getbootargs(); strcpy(saved_command_line, *cmdline_p); +#ifdef PROM_DEBUG_CONSOLE + register_console(&prom_debug_console); +#endif + printk("ARCH: SUN4U\n"); #ifdef CONFIG_DUMMY_CONSOLE @@ -531,7 +551,7 @@ ic_servaddr = sv; if (gw) ic_gateway = gw; - ic_bootp_flag = ic_rarp_flag = 0; + ic_proto_enabled = 0; } } #endif diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/smp.c linux/arch/sparc64/kernel/smp.c --- v2.2.3/linux/arch/sparc64/kernel/smp.c Sun Nov 8 14:02:47 1998 +++ linux/arch/sparc64/kernel/smp.c Mon Mar 15 16:10:43 1999 @@ -5,6 +5,8 @@ #include #include +#include +#include #include #include #include @@ -34,24 +36,23 @@ extern void calibrate_delay(void); extern unsigned prom_cpu_nodes[]; -volatile int smp_processors_ready = 0; -unsigned long cpu_present_map = 0; -int smp_num_cpus = 1; -int smp_threads_ready = 0; +struct cpuinfo_sparc cpu_data[NR_CPUS] __attribute__ ((aligned (64))); -struct cpuinfo_sparc cpu_data[NR_CPUS] __attribute__ ((aligned (64))); +volatile int cpu_number_map[NR_CPUS] __attribute__ ((aligned (64))); +volatile int __cpu_logical_map[NR_CPUS] __attribute__ ((aligned (64))); -/* Please don't make this initdata!!! --DaveM */ +/* Please don't make this stuff initdata!!! --DaveM */ static unsigned char boot_cpu_id = 0; - static int smp_activated = 0; -volatile int cpu_number_map[NR_CPUS]; -volatile int __cpu_logical_map[NR_CPUS]; - /* Kernel spinlock */ spinlock_t kernel_flag = SPIN_LOCK_UNLOCKED; +volatile int smp_processors_ready = 0; +unsigned long cpu_present_map = 0; +int smp_num_cpus = 1; +int smp_threads_ready = 0; + __initfunc(void smp_setup(char *str, int *ints)) { /* XXX implement me XXX */ @@ -84,6 +85,8 @@ __initfunc(void smp_store_cpu_info(int id)) { + int i; + cpu_data[id].irq_count = 0; cpu_data[id].bh_count = 0; /* multiplier and counter set by @@ -94,16 +97,18 @@ cpu_data[id].pte_cache = NULL; cpu_data[id].pgdcache_size = 0; cpu_data[id].pgd_cache = NULL; -} + cpu_data[id].idle_volume = 0; -extern void distribute_irqs(void); + for(i = 0; i < 16; i++) + cpu_data[id].irq_worklists[i] = 0; +} __initfunc(void smp_commence(void)) { - distribute_irqs(); } static void smp_setup_percpu_timer(void); +static void smp_tune_scheduling(void); static volatile unsigned long callin_flag = 0; @@ -173,10 +178,16 @@ panic("SMP bolixed\n"); } -extern struct prom_cpuinfo linux_cpus[NR_CPUS]; +extern struct prom_cpuinfo linux_cpus[64]; extern unsigned long smp_trampoline; +/* The OBP cpu startup callback truncates the 3rd arg cookie to + * 32-bits (I think) so to be safe we have it read the pointer + * contained here so we work on >4GB machines. -DaveM + */ +static struct task_struct *cpu_new_task = NULL; + __initfunc(void smp_boot_cpus(void)) { int cpucount = 0, i; @@ -184,6 +195,7 @@ printk("Entering UltraSMPenguin Mode...\n"); __sti(); smp_store_cpu_info(boot_cpu_id); + smp_tune_scheduling(); if(linux_num_cpus == 1) return; @@ -194,12 +206,14 @@ if(cpu_present_map & (1UL << i)) { unsigned long entry = (unsigned long)(&smp_trampoline); + unsigned long cookie = (unsigned long)(&cpu_new_task); struct task_struct *p; int timeout; int no; extern unsigned long phys_base; entry += phys_base - KERNBASE; + cookie += phys_base - KERNBASE; kernel_thread(start_secondary, NULL, CLONE_PID); p = task[++cpucount]; p->processor = i; @@ -207,8 +221,9 @@ for (no = 0; no < linux_num_cpus; no++) if (linux_cpus[no].mid == i) break; + cpu_new_task = p; prom_startcpu(linux_cpus[no].prom_node, - entry, ((unsigned long)p)); + entry, cookie); for(timeout = 0; timeout < 5000000; timeout++) { if(callin_flag) break; @@ -216,8 +231,8 @@ } if(callin_flag) { cpu_number_map[i] = cpucount; - prom_cpu_nodes[i] = linux_cpus[no].prom_node; __cpu_logical_map[cpucount] = i; + prom_cpu_nodes[i] = linux_cpus[no].prom_node; } else { cpucount--; printk("Processor %d is stuck.\n", i); @@ -228,6 +243,7 @@ cpu_number_map[i] = -1; } } + cpu_new_task = NULL; if(cpucount == 0) { printk("Error: only one processor found.\n"); cpu_present_map = (1UL << smp_processor_id()); @@ -249,17 +265,6 @@ membar("#StoreStore | #StoreLoad"); } -/* We don't even need to do anything, the only generic message pass done - * anymore is to stop all cpus during a panic(). When the user drops to - * the PROM prompt, the firmware will send the other cpu's it's MONDO - * vector anyways, so doing anything special here is pointless. - * - * This whole thing should go away anyways... - */ -void smp_message_pass(int target, int msg, unsigned long data, int wait) -{ -} - /* #define XCALL_DEBUG */ static inline void xcall_deliver(u64 data0, u64 data1, u64 data2, u64 pstate, unsigned long cpu) @@ -642,6 +647,100 @@ __cpu_logical_map[0] = boot_cpu_id; current->processor = boot_cpu_id; prof_counter(boot_cpu_id) = prof_multiplier(boot_cpu_id) = 1; +} + +static inline unsigned long find_flush_base(unsigned long size) +{ + struct page *p = mem_map; + unsigned long found, base; + + size = PAGE_ALIGN(size); + found = size; + base = page_address(p); + while(found != 0) { + /* Failure. */ + if(p >= (mem_map + max_mapnr)) + return 0UL; + if(PageSkip(p)) { + p = p->next_hash; + base = page_address(p); + found = size; + } else { + found -= PAGE_SIZE; + p++; + } + } + return base; +} + +cycles_t cacheflush_time; + +__initfunc(static void smp_tune_scheduling (void)) +{ + unsigned long flush_base, flags, *p; + unsigned int ecache_size; + cycles_t tick1, tick2, raw; + + /* Approximate heuristic for SMP scheduling. It is an + * estimation of the time it takes to flush the L2 cache + * on the local processor. + * + * The ia32 chooses to use the L1 cache flush time instead, + * and I consider this complete nonsense. The Ultra can service + * a miss to the L1 with a hit to the L2 in 7 or 8 cycles, and + * L2 misses are what create extra bus traffic (ie. the "cost" + * of moving a process from one cpu to another). + */ + printk("SMP: Calibrating ecache flush... "); + ecache_size = prom_getintdefault(linux_cpus[0].prom_node, + "ecache-size", (512 *1024)); + flush_base = find_flush_base(ecache_size << 1); + + if(flush_base != 0UL) { + __save_and_cli(flags); + + /* Scan twice the size once just to get the TLB entries + * loaded and make sure the second scan measures pure misses. + */ + for(p = (unsigned long *)flush_base; + ((unsigned long)p) < (flush_base + (ecache_size<<1)); + p += (64 / sizeof(unsigned long))) + *((volatile unsigned long *)p); + + /* Now the real measurement. */ + __asm__ __volatile__(" + b,pt %%xcc, 1f + rd %%tick, %0 + + .align 64 +1: ldx [%2 + 0x000], %%g1 + ldx [%2 + 0x040], %%g2 + ldx [%2 + 0x080], %%g3 + ldx [%2 + 0x0c0], %%g5 + add %2, 0x100, %2 + cmp %2, %4 + bne,pt %%xcc, 1b + nop + + rd %%tick, %1" + : "=&r" (tick1), "=&r" (tick2), "=&r" (flush_base) + : "2" (flush_base), "r" (flush_base + ecache_size) + : "g1", "g2", "g3", "g5"); + + __restore_flags(flags); + + raw = (tick2 - tick1); + + /* Dampen it a little, considering two processes + * sharing the cache and fitting. + */ + cacheflush_time = (raw - (raw >> 2)); + } else + cacheflush_time = ((ecache_size << 2) + + (ecache_size << 1)); + + printk("Using heuristic of %d cycles.\n", + (int) cacheflush_time); } int __init setup_profiling_timer(unsigned int multiplier) diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/sparc64_ksyms.c linux/arch/sparc64/kernel/sparc64_ksyms.c --- v2.2.3/linux/arch/sparc64/kernel/sparc64_ksyms.c Thu Nov 19 09:56:27 1998 +++ linux/arch/sparc64/kernel/sparc64_ksyms.c Mon Mar 15 16:10:43 1999 @@ -1,8 +1,9 @@ -/* $Id: sparc64_ksyms.c,v 1.49 1998/10/28 08:11:28 jj Exp $ +/* $Id: sparc64_ksyms.c,v 1.57 1999/03/14 20:51:28 davem Exp $ * arch/sparc64/kernel/sparc64_ksyms.c: Sparc64 specific ksyms support. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) + * Copyright (C) 1999 Jakub Jelinek (jj@ultra.linux.cz) */ /* Tell string.h we don't want memcpy etc. as cpp defines */ @@ -52,8 +53,9 @@ short revents; }; -extern unsigned prom_cpu_nodes[NR_CPUS]; +extern unsigned prom_cpu_nodes[64]; extern void die_if_kernel(char *str, struct pt_regs *regs); +extern pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags); extern unsigned long sunos_mmap(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long); void _sigpause_common (unsigned int set, struct pt_regs *); @@ -102,6 +104,8 @@ #endif #endif +extern unsigned long phys_base; + /* One thing to note is that the way the symbols of the mul/div * support routines are named is a mess, they all start with * a '.' which makes it a bitch to export, here is the trick: @@ -155,6 +159,8 @@ EXPORT_SYMBOL(local_irq_count); EXPORT_SYMBOL(local_bh_count); #endif + +EXPORT_SYMBOL(ivector_table); EXPORT_SYMBOL(enable_irq); EXPORT_SYMBOL(disable_irq); @@ -171,6 +177,7 @@ EXPORT_SYMBOL(mmu_release_scsi_one); EXPORT_SYMBOL(mmu_release_scsi_sgl); #if CONFIG_SBUS +EXPORT_SYMBOL(mmu_set_sbus64); EXPORT_SYMBOL(SBus_chain); EXPORT_SYMBOL(dma_chain); #endif @@ -199,6 +206,9 @@ /* math-emu wants this */ EXPORT_SYMBOL(die_if_kernel); +/* Kernel thread creation. */ +EXPORT_SYMBOL(kernel_thread); + /* prom symbols */ EXPORT_SYMBOL(idprom); EXPORT_SYMBOL(prom_root_node); @@ -214,6 +224,7 @@ EXPORT_SYMBOL(saved_command_line); EXPORT_SYMBOL(prom_getname); EXPORT_SYMBOL(prom_feval); +EXPORT_SYMBOL(prom_getbool); EXPORT_SYMBOL(prom_getstring); EXPORT_SYMBOL(prom_apply_sbus_ranges); EXPORT_SYMBOL(prom_getint); @@ -257,7 +268,6 @@ EXPORT_SYMBOL(prom_cpu_nodes); EXPORT_SYMBOL(sys_ioctl); EXPORT_SYMBOL(sys32_ioctl); -EXPORT_SYMBOL(get_unmapped_area); EXPORT_SYMBOL(move_addr_to_kernel); EXPORT_SYMBOL(move_addr_to_user); #endif @@ -280,6 +290,10 @@ EXPORT_SYMBOL(__copy_from_user); EXPORT_SYMBOL(__strncpy_from_user); EXPORT_SYMBOL(__bzero_noasi); + +/* Various address conversion macros use this. */ +EXPORT_SYMBOL(phys_base); +EXPORT_SYMBOL(sparc64_valid_addr_bitmap); /* No version information on this, heavily used in inline asm, * and will always be 'void __ret_efault(void)'. diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/starfire.c linux/arch/sparc64/kernel/starfire.c --- v2.2.3/linux/arch/sparc64/kernel/starfire.c Wed Dec 31 16:00:00 1969 +++ linux/arch/sparc64/kernel/starfire.c Wed Mar 10 16:53:37 1999 @@ -0,0 +1,121 @@ +/* $Id: starfire.c,v 1.2 1998/12/09 18:53:11 davem Exp $ + * starfire.c: Starfire/E10000 support. + * + * Copyright (C) 1998 David S. Miller (davem@dm.cobaltmicro.com) + */ + +#include +#include + +#include +#include +#include + +/* A few places around the kernel check this to see if + * they need to call us to do things in a Starfire specific + * way. + */ +int this_is_starfire = 0; + +void starfire_check(void) +{ + int ssnode = prom_finddevice("/ssp-serial"); + + if(ssnode != 0 && ssnode != -1) { + int i; + + this_is_starfire = 1; + + /* Now must fixup cpu MIDs. OBP gave us a logical + * linear cpuid number, not the real upaid. + */ + for(i = 0; i < linux_num_cpus; i++) { + unsigned int mid = linux_cpus[i].mid; + + mid = (((mid & 0x3c) << 1) | + ((mid & 0x40) >> 4) | + (mid & 0x3)); + + linux_cpus[i].mid = mid; + } + } +} + +int starfire_hard_smp_processor_id(void) +{ + return *((unsigned int *) __va(0x1fff40000d0)); +} + +/* Each Starfire board has 32 registers which perform translation + * and delivery of traditional interrupt packets into the extended + * Starfire hardware format. Essentially UPAID's now have 2 more + * bits than in all previous Sun5 systems. + */ +struct starfire_irqinfo { + unsigned int *imap_slots[32]; + unsigned int *tregs[32]; + struct starfire_irqinfo *next; + int upaid, hwmid; +}; + +static struct starfire_irqinfo *sflist = NULL; + +/* Beam me up Scott(McNeil)y... */ +void *starfire_hookup(int upaid) +{ + struct starfire_irqinfo *p; + unsigned long treg_base, hwmid, i; + + p = kmalloc(sizeof(*p), GFP_KERNEL); + if(!p) { + prom_printf("starfire_hookup: No memory, this is insane.\n"); + prom_halt(); + } + treg_base = 0x100fc000000UL; + hwmid = ((upaid & 0x3c) << 1) | + ((upaid & 0x40) >> 4) | + (upaid & 0x3); + p->hwmid = hwmid; + treg_base += (hwmid << 33UL); + treg_base += 0x200UL; + for(i = 0; i < 32; i++) { + p->imap_slots[i] = NULL; + p->tregs[i] = __va(treg_base + (i * 0x10)); + } + p->upaid = upaid; + p->next = sflist; + sflist = p; + + return (void *) p; +} + +unsigned int starfire_translate(unsigned int *imap, + unsigned int upaid) +{ + struct starfire_irqinfo *p; + unsigned int bus_hwmid; + unsigned int i; + + bus_hwmid = (((unsigned long)imap) >> 33) & 0x7f; + for(p = sflist; p != NULL; p = p->next) + if(p->hwmid == bus_hwmid) + break; + if(p == NULL) { + prom_printf("XFIRE: Cannot find irqinfo for imap %016lx\n", + ((unsigned long)imap)); + prom_halt(); + } + for(i = 0; i < 32; i++) { + if(p->imap_slots[i] == imap || + p->imap_slots[i] == NULL) + break; + } + if(i == 32) { + printk("starfire_translate: Are you kidding me?\n"); + panic("Lucy in the sky...."); + } + p->imap_slots[i] = imap; + *(p->tregs[i]) = upaid; + + return i; +} diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/sys_sparc.c linux/arch/sparc64/kernel/sys_sparc.c --- v2.2.3/linux/arch/sparc64/kernel/sys_sparc.c Sun Nov 8 14:02:47 1998 +++ linux/arch/sparc64/kernel/sys_sparc.c Wed Mar 10 16:53:37 1999 @@ -1,4 +1,4 @@ -/* $Id: sys_sparc.c,v 1.25 1998/10/21 03:21:15 davem Exp $ +/* $Id: sys_sparc.c,v 1.26 1999/01/07 19:07:01 jj Exp $ * linux/arch/sparc64/kernel/sys_sparc.c * * This file contains various random system calls that @@ -27,6 +27,8 @@ #include #include +/* #define DEBUG_UNIMP_SYSCALL */ + /* XXX Make this per-binary type, this way we can detect the type of * XXX a binary. Every Sparc executable calls this very early on. */ @@ -200,11 +202,14 @@ c_sys_nis_syscall (struct pt_regs *regs) { static int count=0; + + /* Don't make the system unusable, if someone goes stuck */ + if (count++ > 5) return -ENOSYS; lock_kernel(); - if (++count <= 20) { /* Don't make the system unusable, if someone goes stuck */ - printk ("Unimplemented SPARC system call %ld\n",regs->u_regs[1]); - show_regs (regs); - } + printk ("Unimplemented SPARC system call %ld\n",regs->u_regs[1]); +#ifdef DEBUG_UNIMP_SYSCALL + show_regs (regs); +#endif unlock_kernel(); return -ENOSYS; } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/sys_sparc32.c linux/arch/sparc64/kernel/sys_sparc32.c --- v2.2.3/linux/arch/sparc64/kernel/sys_sparc32.c Thu Nov 19 09:56:27 1998 +++ linux/arch/sparc64/kernel/sys_sparc32.c Wed Mar 10 16:53:37 1999 @@ -1,4 +1,4 @@ -/* $Id: sys_sparc32.c,v 1.100 1998/11/08 11:14:00 davem Exp $ +/* $Id: sys_sparc32.c,v 1.107 1999/03/05 13:21:02 davem Exp $ * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls. * * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -50,6 +51,8 @@ #include #include +#include + /* Use this to get at 32-bit user passed pointers. */ /* Things to consider: the low-level assembly stub does srl x, 0, x for first four arguments, so if you have @@ -74,15 +77,6 @@ __ret; \ }) -static inline char * get_page(void) -{ - char * res; - res = (char *)__get_free_page(GFP_KERNEL); - return res; -} - -#define putname32 putname - /* In order to reduce some races, while at the same time doing additional * checking and hopefully speeding things up, we copy filenames to the * kernel data space before using them.. @@ -109,13 +103,13 @@ char *tmp, *result; result = ERR_PTR(-ENOMEM); - tmp = get_page(); + tmp = (char *)__get_free_page(GFP_KERNEL); if (tmp) { int retval = do_getname32(filename, tmp); result = tmp; if (retval < 0) { - putname32(tmp); + putname(tmp); result = ERR_PTR(retval); } } @@ -243,7 +237,10 @@ err = -EFAULT; if (get_user (pad, (u32 *)uptr)) goto out; - fourth.__pad = (void *)A(pad); + if(third == SETVAL) + fourth.val = (int)pad; + else + fourth.__pad = (void *)A(pad); if (IPCOP_MASK (third) & (IPCOP_MASK (IPC_INFO) | IPCOP_MASK (SEM_INFO) | IPCOP_MASK (GETVAL) | IPCOP_MASK (GETPID) | IPCOP_MASK (GETNCNT) | IPCOP_MASK (GETZCNT) | @@ -652,7 +649,7 @@ set_fs (KERNEL_DS); err = sys_quotactl(cmd, (const char *)spec, id, (caddr_t)&d); set_fs (old_fs); - putname32 (spec); + putname (spec); if (cmds == Q_GETQUOTA) { __kernel_time_t b = d.dqb_btime, i = d.dqb_itime; ((struct dqblk32 *)&d)->dqb_itime = i; @@ -696,7 +693,7 @@ set_fs (KERNEL_DS); ret = sys_statfs((const char *)pth, &s); set_fs (old_fs); - putname32 (pth); + putname (pth); if (put_statfs(buf, &s)) return -EFAULT; } @@ -744,7 +741,7 @@ set_fs (KERNEL_DS); ret = sys_utime(filenam, &t); set_fs (old_fs); - putname32 (filenam); + putname (filenam); } return ret; } @@ -796,8 +793,9 @@ } inode = file->f_dentry->d_inode; - retval = locks_verify_area((type == VERIFY_READ) ? - FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE, + /* VERIFY_WRITE actually means a read, as we write to user space */ + retval = locks_verify_area((type == VERIFY_WRITE + ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE), inode, file, file->f_pos, tot_len); if (retval) { if (iov != iovstack) @@ -1106,13 +1104,17 @@ __put_user(*fdset, ufdset); } +#define MAX_SELECT_SECONDS \ + ((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1) + asmlinkage int sys32_select(int n, u32 *inp, u32 *outp, u32 *exp, u32 tvp_x) { - fd_set_buffer *fds; + fd_set_bits fds; struct timeval32 *tvp = (struct timeval32 *)AA(tvp_x); + char *bits; unsigned long nn; long timeout; - int ret; + int ret, size; timeout = MAX_SCHEDULE_TIMEOUT; if (tvp) { @@ -1123,30 +1125,47 @@ || (ret = __get_user(usec, &tvp->tv_usec))) goto out_nofds; - timeout = (usec + 1000000/HZ - 1) / (1000000/HZ); - timeout += sec * HZ; + ret = -EINVAL; + if(sec < 0 || usec < 0) + goto out_nofds; + + if ((unsigned long) sec < MAX_SELECT_SECONDS) { + timeout = (usec + 1000000/HZ - 1) / (1000000/HZ); + timeout += sec * (unsigned long) HZ; + } } + ret = -EINVAL; + if (n < 0 || n > KFDS_NR) + goto out_nofds; + + /* + * We need 6 bitmaps (in/out/ex for both incoming and outgoing), + * since we used fdset we need to allocate memory in units of + * long-words. + */ ret = -ENOMEM; - fds = (fd_set_buffer *) __get_free_page(GFP_KERNEL); - if (!fds) + size = FDS_BYTES(n); + bits = kmalloc(6 * size, GFP_KERNEL); + if (!bits) goto out_nofds; - ret = -EINVAL; - if (n < 0) - goto out; - if (n > KFDS_NR) - n = KFDS_NR; + fds.in = (unsigned long *) bits; + fds.out = (unsigned long *) (bits + size); + fds.ex = (unsigned long *) (bits + 2*size); + fds.res_in = (unsigned long *) (bits + 3*size); + fds.res_out = (unsigned long *) (bits + 4*size); + fds.res_ex = (unsigned long *) (bits + 5*size); nn = (n + 8*sizeof(u32) - 1) / (8*sizeof(u32)); - if ((ret = get_fd_set32(nn, fds->in, inp)) || - (ret = get_fd_set32(nn, fds->out, outp)) || - (ret = get_fd_set32(nn, fds->ex, exp))) + if ((ret = get_fd_set32(nn, fds.in, inp)) || + (ret = get_fd_set32(nn, fds.out, outp)) || + (ret = get_fd_set32(nn, fds.ex, exp))) goto out; - zero_fd_set(n, fds->res_in); - zero_fd_set(n, fds->res_out); - zero_fd_set(n, fds->res_ex); + zero_fd_set(n, fds.res_in); + zero_fd_set(n, fds.res_out); + zero_fd_set(n, fds.res_ex); - ret = do_select(n, fds, &timeout); + ret = do_select(n, &fds, &timeout); if (tvp && !(current->personality & STICKY_TIMEOUTS)) { time_t sec = 0, usec = 0; @@ -1168,12 +1187,12 @@ ret = 0; } - set_fd_set32(nn, inp, fds->res_in); - set_fd_set32(nn, outp, fds->res_out); - set_fd_set32(nn, exp, fds->res_ex); + set_fd_set32(nn, inp, fds.res_in); + set_fd_set32(nn, outp, fds.res_out); + set_fd_set32(nn, exp, fds.res_ex); out: - free_page ((unsigned long)fds); + kfree(bits); out_nofds: return ret; } @@ -1213,7 +1232,7 @@ set_fs (KERNEL_DS); ret = sys_newstat(filenam, &s); set_fs (old_fs); - putname32 (filenam); + putname (filenam); if (putstat (statbuf, &s)) return -EFAULT; } @@ -1235,7 +1254,7 @@ set_fs (KERNEL_DS); ret = sys_newlstat(filenam, &s); set_fs (old_fs); - putname32 (filenam); + putname (filenam); if (putstat (statbuf, &s)) return -EFAULT; } @@ -2010,74 +2029,6 @@ return ret; } -struct timex32 { - unsigned int modes; - s32 offset; - s32 freq; - s32 maxerror; - s32 esterror; - int status; - s32 constant; - s32 precision; - s32 tolerance; - struct timeval32 time; - s32 tick; - s32 ppsfreq; - s32 jitter; - int shift; - s32 stabil; - s32 jitcnt; - s32 calcnt; - s32 errcnt; - s32 stbcnt; - int :32; int :32; int :32; int :32; - int :32; int :32; int :32; int :32; - int :32; int :32; int :32; int :32; -}; - -extern int do_adjtimex(struct timex *); - -asmlinkage int sys32_adjtimex(struct timex32 *txc_p) -{ - struct timex t; - int ret; - - ret = get_user (t.modes, &txc_p->modes); - ret |= __get_user (t.offset, &txc_p->offset); - ret |= __get_user (t.freq, &txc_p->freq); - ret |= __get_user (t.maxerror, &txc_p->maxerror); - ret |= __get_user (t.esterror, &txc_p->esterror); - ret |= __get_user (t.status, &txc_p->status); - ret |= __get_user (t.constant, &txc_p->constant); - ret |= __get_user (t.tick, &txc_p->tick); - ret |= __get_user (t.shift, &txc_p->shift); - if (ret || (ret = do_adjtimex(&t))) - return ret; - ret = __put_user (t.modes, &txc_p->modes); - ret |= __put_user (t.offset, &txc_p->offset); - ret |= __put_user (t.freq, &txc_p->freq); - ret |= __put_user (t.maxerror, &txc_p->maxerror); - ret |= __put_user (t.esterror, &txc_p->esterror); - ret |= __put_user (t.status, &txc_p->status); - ret |= __put_user (t.constant, &txc_p->constant); - ret |= __put_user (t.precision, &txc_p->precision); - ret |= __put_user (t.tolerance, &txc_p->tolerance); - ret |= __put_user (t.time.tv_sec, &txc_p->time.tv_sec); - ret |= __put_user (t.time.tv_usec, &txc_p->time.tv_usec); - ret |= __put_user (t.tick, &txc_p->tick); - ret |= __put_user (t.ppsfreq, &txc_p->ppsfreq); - ret |= __put_user (t.jitter, &txc_p->jitter); - ret |= __put_user (t.shift, &txc_p->shift); - ret |= __put_user (t.stabil, &txc_p->stabil); - ret |= __put_user (t.jitcnt, &txc_p->jitcnt); - ret |= __put_user (t.calcnt, &txc_p->calcnt); - ret |= __put_user (t.errcnt, &txc_p->errcnt); - ret |= __put_user (t.stbcnt, &txc_p->stbcnt); - if (!ret) - ret = time_state; - return ret; -} - /* XXX This really belongs in some header file... -DaveM */ #define MAX_SOCK_ADDR 128 /* 108 for Unix domain - 16 for IP, 16 for IPX, @@ -2131,9 +2082,44 @@ __kernel_size_t32 cmsg_len; int cmsg_level; int cmsg_type; - unsigned char cmsg_data[0]; }; +/* Bleech... */ +#define __CMSG32_NXTHDR(ctl, len, cmsg, cmsglen) __cmsg32_nxthdr((ctl),(len),(cmsg),(cmsglen)) +#define CMSG32_NXTHDR(mhdr, cmsg, cmsglen) cmsg32_nxthdr((mhdr), (cmsg), (cmsglen)) + +#define CMSG32_ALIGN(len) ( ((len)+sizeof(int)-1) & ~(sizeof(int)-1) ) + +#define CMSG32_DATA(cmsg) ((void *)((char *)(cmsg) + CMSG32_ALIGN(sizeof(struct cmsghdr32)))) +#define CMSG32_SPACE(len) (CMSG32_ALIGN(sizeof(struct cmsghdr32)) + CMSG32_ALIGN(len)) +#define CMSG32_LEN(len) (CMSG32_ALIGN(sizeof(struct cmsghdr32)) + (len)) + +#define __CMSG32_FIRSTHDR(ctl,len) ((len) >= sizeof(struct cmsghdr32) ? \ + (struct cmsghdr32 *)(ctl) : \ + (struct cmsghdr32 *)NULL) +#define CMSG32_FIRSTHDR(msg) __CMSG32_FIRSTHDR((msg)->msg_control, (msg)->msg_controllen) + +__inline__ struct cmsghdr32 *__cmsg32_nxthdr(void *__ctl, __kernel_size_t __size, + struct cmsghdr32 *__cmsg, int __cmsg_len) +{ + struct cmsghdr32 * __ptr; + + __ptr = (struct cmsghdr32 *)(((unsigned char *) __cmsg) + + CMSG32_ALIGN(__cmsg_len)); + if ((unsigned long)((char*)(__ptr+1) - (char *) __ctl) > __size) + return NULL; + + return __ptr; +} + +__inline__ struct cmsghdr32 *cmsg32_nxthdr (struct msghdr *__msg, + struct cmsghdr32 *__cmsg, + int __cmsg_len) +{ + return __cmsg32_nxthdr(__msg->msg_control, __msg->msg_controllen, + __cmsg, __cmsg_len); +} + static inline int iov_from_user32_to_kern(struct iovec *kiov, struct iovec32 *uiov32, int niov) @@ -2175,6 +2161,7 @@ kmsg->msg_control = (void *)A(tmp3); err = get_user(kmsg->msg_namelen, &umsg->msg_namelen); + err |= get_user(kmsg->msg_iovlen, &umsg->msg_iovlen); err |= get_user(kmsg->msg_controllen, &umsg->msg_controllen); err |= get_user(kmsg->msg_flags, &umsg->msg_flags); @@ -2217,6 +2204,165 @@ return tot_len; } +/* There is a lot of hair here because the alignment rules (and + * thus placement) of cmsg headers and length are different for + * 32-bit apps. -DaveM + */ +static int cmsghdr_from_user32_to_kern(struct msghdr *kmsg, + unsigned char *stackbuf, int stackbuf_size) +{ + struct cmsghdr32 *ucmsg; + struct cmsghdr *kcmsg, *kcmsg_base; + __kernel_size_t32 ucmlen; + __kernel_size_t kcmlen, tmp; + + kcmlen = 0; + kcmsg_base = kcmsg = (struct cmsghdr *)stackbuf; + ucmsg = CMSG32_FIRSTHDR(kmsg); + while(ucmsg != NULL) { + if(get_user(ucmlen, &ucmsg->cmsg_len)) + return -EFAULT; + + /* Catch bogons. */ + if(CMSG32_ALIGN(ucmlen) < + CMSG32_ALIGN(sizeof(struct cmsghdr32))) + return -EINVAL; + if((unsigned long)(((char *)ucmsg - (char *)kmsg->msg_control) + + ucmlen) > kmsg->msg_controllen) + return -EINVAL; + + tmp = ((ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))) + + CMSG_ALIGN(sizeof(struct cmsghdr))); + kcmlen += tmp; + ucmsg = CMSG32_NXTHDR(kmsg, ucmsg, ucmlen); + } + if(kcmlen == 0) + return -EINVAL; + + /* The kcmlen holds the 64-bit version of the control length. + * It may not be modified as we do not stick it into the kmsg + * until we have successfully copied over all of the data + * from the user. + */ + if(kcmlen > stackbuf_size) + kcmsg_base = kcmsg = kmalloc(kcmlen, GFP_KERNEL); + if(kcmsg == NULL) + return -ENOBUFS; + + /* Now copy them over neatly. */ + memset(kcmsg, 0, kcmlen); + ucmsg = CMSG32_FIRSTHDR(kmsg); + while(ucmsg != NULL) { + __get_user(ucmlen, &ucmsg->cmsg_len); + tmp = ((ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))) + + CMSG_ALIGN(sizeof(struct cmsghdr))); + kcmsg->cmsg_len = tmp; + __get_user(kcmsg->cmsg_level, &ucmsg->cmsg_level); + __get_user(kcmsg->cmsg_type, &ucmsg->cmsg_type); + + /* Copy over the data. */ + if(copy_from_user(CMSG_DATA(kcmsg), + CMSG32_DATA(ucmsg), + (ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))))) + goto out_free_efault; + + /* Advance. */ + kcmsg = (struct cmsghdr *)((char *)kcmsg + CMSG_ALIGN(tmp)); + ucmsg = CMSG32_NXTHDR(kmsg, ucmsg, ucmlen); + } + + /* Ok, looks like we made it. Hook it up and return success. */ + kmsg->msg_control = kcmsg_base; + kmsg->msg_controllen = kcmlen; + return 0; + +out_free_efault: + if(kcmsg_base != (struct cmsghdr *)stackbuf) + kfree(kcmsg_base); + return -EFAULT; +} + +static void put_cmsg32(struct msghdr *kmsg, int level, int type, + int len, void *data) +{ + struct cmsghdr32 *cm = (struct cmsghdr32 *) kmsg->msg_control; + struct cmsghdr32 cmhdr; + int cmlen = CMSG32_LEN(len); + + if(cm == NULL || kmsg->msg_controllen < sizeof(*cm)) { + kmsg->msg_flags |= MSG_CTRUNC; + return; + } + + if(kmsg->msg_controllen < cmlen) { + kmsg->msg_flags |= MSG_CTRUNC; + cmlen = kmsg->msg_controllen; + } + cmhdr.cmsg_level = level; + cmhdr.cmsg_type = type; + cmhdr.cmsg_len = cmlen; + + if(copy_to_user(cm, &cmhdr, sizeof cmhdr)) + return; + if(copy_to_user(CMSG32_DATA(cm), data, cmlen - sizeof(struct cmsghdr32))) + return; + cmlen = CMSG32_SPACE(len); + kmsg->msg_control += cmlen; + kmsg->msg_controllen -= cmlen; +} + +static void scm_detach_fds32(struct msghdr *kmsg, struct scm_cookie *scm) +{ + struct cmsghdr32 *cm = (struct cmsghdr32 *) kmsg->msg_control; + int fdmax = (kmsg->msg_controllen - sizeof(struct cmsghdr32)) / sizeof(int); + int fdnum = scm->fp->count; + struct file **fp = scm->fp->fp; + int *cmfptr; + int err = 0, i; + + if (fdnum < fdmax) + fdmax = fdnum; + + for (i = 0, cmfptr = (int *) CMSG32_DATA(cm); i < fdmax; i++, cmfptr++) { + int new_fd; + err = get_unused_fd(); + if (err < 0) + break; + new_fd = err; + err = put_user(new_fd, cmfptr); + if (err) { + put_unused_fd(new_fd); + break; + } + /* Bump the usage count and install the file. */ + fp[i]->f_count++; + current->files->fd[new_fd] = fp[i]; + } + + if (i > 0) { + int cmlen = CMSG32_LEN(i * sizeof(int)); + if (!err) + err = put_user(SOL_SOCKET, &cm->cmsg_level); + if (!err) + err = put_user(SCM_RIGHTS, &cm->cmsg_type); + if (!err) + err = put_user(cmlen, &cm->cmsg_len); + if (!err) { + cmlen = CMSG32_SPACE(i * sizeof(int)); + kmsg->msg_control += cmlen; + kmsg->msg_controllen -= cmlen; + } + } + if (i < fdnum) + kmsg->msg_flags |= MSG_CTRUNC; + + /* + * All of the files that fit in the message have had their + * usage counts incremented, so we just free the list. + */ + __scm_destroy(scm); +} + asmlinkage int sys32_sendmsg(int fd, struct msghdr32 *user_msg, unsigned user_flags) { struct socket *sock; @@ -2237,25 +2383,10 @@ total_len = err; if(kern_msg.msg_controllen) { - struct cmsghdr32 *ucmsg = (struct cmsghdr32 *)kern_msg.msg_control; - unsigned long *kcmsg; - __kernel_size_t32 cmlen; - - if(kern_msg.msg_controllen > sizeof(ctl) && - kern_msg.msg_controllen <= 256) { - err = -ENOBUFS; - ctl_buf = kmalloc(kern_msg.msg_controllen, GFP_KERNEL); - if(!ctl_buf) - goto out_freeiov; - } - __get_user(cmlen, &ucmsg->cmsg_len); - kcmsg = (unsigned long *) ctl_buf; - *kcmsg++ = (unsigned long)cmlen; - err = -EFAULT; - if(copy_from_user(kcmsg, &ucmsg->cmsg_level, - kern_msg.msg_controllen - sizeof(__kernel_size_t32))) - goto out_freectl; - kern_msg.msg_control = ctl_buf; + err = cmsghdr_from_user32_to_kern(&kern_msg, ctl, sizeof(ctl)); + if(err) + goto out_freeiov; + ctl_buf = kern_msg.msg_control; } kern_msg.msg_flags = user_flags; @@ -2269,7 +2400,6 @@ } unlock_kernel(); -out_freectl: /* N.B. Use kfree here, as kern_msg.msg_controllen might change? */ if(ctl_buf != ctl) kfree(ctl_buf); @@ -2310,26 +2440,43 @@ lock_kernel(); sock = sockfd_lookup(fd, &err); if (sock != NULL) { + struct scm_cookie scm; + if (sock->file->f_flags & O_NONBLOCK) user_flags |= MSG_DONTWAIT; - err = sock_recvmsg(sock, &kern_msg, total_len, user_flags); - if(err >= 0) + memset(&scm, 0, sizeof(scm)); + err = sock->ops->recvmsg(sock, &kern_msg, total_len, + user_flags, &scm); + if(err >= 0) { len = err; + if(!kern_msg.msg_control) { + if(sock->passcred || scm.fp) + kern_msg.msg_flags |= MSG_CTRUNC; + if(scm.fp) + __scm_destroy(&scm); + } else { + /* Wheee... */ + if(sock->passcred) + put_cmsg32(&kern_msg, + SOL_SOCKET, SCM_CREDENTIALS, + sizeof(scm.creds), &scm.creds); + if(scm.fp != NULL) + scm_detach_fds32(&kern_msg, &scm); + } + } sockfd_put(sock); } unlock_kernel(); if(uaddr != NULL && err >= 0) err = move_addr_to_user(addr, kern_msg.msg_namelen, uaddr, uaddr_len); - if(err >= 0) { - err = __put_user(kern_msg.msg_flags, &user_msg->msg_flags); - if(!err) { - /* XXX Convert cmsg back into userspace 32-bit format... */ - err = __put_user((unsigned long)kern_msg.msg_control - cmsg_ptr, - &user_msg->msg_controllen); - } + if(cmsg_ptr != 0 && err >= 0) { + u32 ucmsg_ptr = ((u32)(unsigned long)kern_msg.msg_control); + err = __put_user(ucmsg_ptr, &user_msg->msg_control); + err |= __put_user(kern_msg.msg_controllen, &user_msg->msg_controllen); } - + if(err >= 0) + err = __put_user(kern_msg.msg_flags, &user_msg->msg_flags); if(kern_msg.msg_iov != iov) kfree(kern_msg.msg_iov); out: @@ -2653,7 +2800,7 @@ error = do_execve32(filename, (u32 *)AA((u32)regs->u_regs[base + UREG_I1]), (u32 *)AA((u32)regs->u_regs[base + UREG_I2]), regs); - putname32(filename); + putname(filename); if(!error) { fprs_write(0); @@ -2943,8 +3090,10 @@ info.addr = (unsigned long)mod; info.size = mod->size; info.flags = mod->flags; - info.usecount = (mod_member_present(mod, can_unload) - && mod->can_unload ? -1 : mod->usecount); + info.usecount = + ((mod_member_present(mod, can_unload) + && mod->can_unload) + ? -1 : atomic_read(&mod->uc.usecount)); if (copy_to_user(buf, &info, sizeof(struct module_info32))) return -EFAULT; @@ -3452,7 +3601,7 @@ ret = sys_utimes(kfilename, &ktvs[0]); set_fs(old_fs); - putname32(kfilename); + putname(kfilename); } return ret; } @@ -3575,5 +3724,78 @@ if (!ret && offset && put_user(of, offset)) return -EFAULT; + return ret; +} + +/* Handle adjtimex compatability. */ + +struct timex32 { + u32 modes; + s32 offset, freq, maxerror, esterror; + s32 status, constant, precision, tolerance; + struct timeval32 time; + s32 tick; + s32 ppsfreq, jitter, shift, stabil; + s32 jitcnt, calcnt, errcnt, stbcnt; + s32 :32; s32 :32; s32 :32; s32 :32; + s32 :32; s32 :32; s32 :32; s32 :32; + s32 :32; s32 :32; s32 :32; s32 :32; +}; + +extern int do_adjtimex(struct timex *); + +asmlinkage int sys32_adjtimex(struct timex32 *utp) +{ + struct timex txc; + int ret; + + memset(&txc, 0, sizeof(struct timex)); + + if(get_user(txc.modes, &utp->modes) || + __get_user(txc.offset, &utp->offset) || + __get_user(txc.freq, &utp->freq) || + __get_user(txc.maxerror, &utp->maxerror) || + __get_user(txc.esterror, &utp->esterror) || + __get_user(txc.status, &utp->status) || + __get_user(txc.constant, &utp->constant) || + __get_user(txc.precision, &utp->precision) || + __get_user(txc.tolerance, &utp->tolerance) || + __get_user(txc.time.tv_sec, &utp->time.tv_sec) || + __get_user(txc.time.tv_usec, &utp->time.tv_usec) || + __get_user(txc.tick, &utp->tick) || + __get_user(txc.ppsfreq, &utp->ppsfreq) || + __get_user(txc.jitter, &utp->jitter) || + __get_user(txc.shift, &utp->shift) || + __get_user(txc.stabil, &utp->stabil) || + __get_user(txc.jitcnt, &utp->jitcnt) || + __get_user(txc.calcnt, &utp->calcnt) || + __get_user(txc.errcnt, &utp->errcnt) || + __get_user(txc.stbcnt, &utp->stbcnt)) + return -EFAULT; + + ret = do_adjtimex(&txc); + + if(put_user(txc.modes, &utp->modes) || + __put_user(txc.offset, &utp->offset) || + __put_user(txc.freq, &utp->freq) || + __put_user(txc.maxerror, &utp->maxerror) || + __put_user(txc.esterror, &utp->esterror) || + __put_user(txc.status, &utp->status) || + __put_user(txc.constant, &utp->constant) || + __put_user(txc.precision, &utp->precision) || + __put_user(txc.tolerance, &utp->tolerance) || + __put_user(txc.time.tv_sec, &utp->time.tv_sec) || + __put_user(txc.time.tv_usec, &utp->time.tv_usec) || + __put_user(txc.tick, &utp->tick) || + __put_user(txc.ppsfreq, &utp->ppsfreq) || + __put_user(txc.jitter, &utp->jitter) || + __put_user(txc.shift, &utp->shift) || + __put_user(txc.stabil, &utp->stabil) || + __put_user(txc.jitcnt, &utp->jitcnt) || + __put_user(txc.calcnt, &utp->calcnt) || + __put_user(txc.errcnt, &utp->errcnt) || + __put_user(txc.stbcnt, &utp->stbcnt)) + ret = -EFAULT; + return ret; } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/systbls.S linux/arch/sparc64/kernel/systbls.S --- v2.2.3/linux/arch/sparc64/kernel/systbls.S Sun Nov 8 14:02:48 1998 +++ linux/arch/sparc64/kernel/systbls.S Sun Mar 21 07:23:38 1999 @@ -1,4 +1,4 @@ -/* $Id: systbls.S,v 1.50 1998/10/07 01:27:27 davem Exp $ +/* $Id: systbls.S,v 1.52 1999/03/20 22:02:05 davem Exp $ * systbls.S: System call entry point tables for OS compatibility. * The native Linux system call table lives here also. * diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/time.c linux/arch/sparc64/kernel/time.c --- v2.2.3/linux/arch/sparc64/kernel/time.c Wed Jan 20 23:14:04 1999 +++ linux/arch/sparc64/kernel/time.c Mon Mar 15 16:10:43 1999 @@ -1,4 +1,4 @@ -/* $Id: time.c,v 1.16 1998/09/05 17:25:28 jj Exp $ +/* $Id: time.c,v 1.20 1999/03/15 22:13:40 davem Exp $ * time.c: UltraSparc timer and TOD clock support. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -31,6 +31,8 @@ #include #include +extern rwlock_t xtime_lock; + struct mostek48t02 *mstk48t02_regs = 0; static struct mostek48t08 *mstk48t08_regs = 0; static struct mostek48t59 *mstk48t59_regs = 0; @@ -69,6 +71,8 @@ { unsigned long ticks; + write_lock(&xtime_lock); + do { do_timer(regs); @@ -82,11 +86,15 @@ } while (ticks >= timer_tick_compare); timer_check_rtc(); + + write_unlock(&xtime_lock); } #ifdef __SMP__ void timer_tick_interrupt(struct pt_regs *regs) { + write_lock(&xtime_lock); + do_timer(regs); /* @@ -99,6 +107,8 @@ : "r" (timer_tick_offset)); timer_check_rtc(); + + write_unlock(&xtime_lock); } #endif @@ -256,13 +266,17 @@ node = prom_getchild(busnd); while(1) { - prom_getstring(node, "model", model, sizeof(model)); + if (!node) + model[0] = 0; + else + prom_getstring(node, "model", model, sizeof(model)); if(strcmp(model, "mk48t02") && strcmp(model, "mk48t08") && strcmp(model, "mk48t59")) { - node = prom_getsibling(node); + if (node) + node = prom_getsibling(node); #ifdef CONFIG_PCI - if ((node == 0) && ebus) { + while ((node == 0) && ebus) { ebus = ebus->next; if (ebus) { busnd = ebus->prom_node; @@ -397,6 +411,9 @@ return ticks / timer_ticks_per_usec; } +/* This need not obtain the xtime_lock as it is coded in + * an implicitly SMP safe way already. + */ void do_gettimeofday(struct timeval *tv) { /* Load doubles must be used on xtime so that what we get @@ -450,7 +467,7 @@ void do_settimeofday(struct timeval *tv) { - cli(); + write_lock_irq(&xtime_lock); tv->tv_usec -= do_gettimeoffset(); if(tv->tv_usec < 0) { @@ -461,10 +478,10 @@ xtime = *tv; time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; - time_state = TIME_ERROR; /* p. 24, (a) */ time_maxerror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT; - sti(); + + write_unlock_irq(&xtime_lock); } static int set_rtc_mmss(unsigned long nowtime) diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/trampoline.S linux/arch/sparc64/kernel/trampoline.S --- v2.2.3/linux/arch/sparc64/kernel/trampoline.S Sun Nov 8 14:02:48 1998 +++ linux/arch/sparc64/kernel/trampoline.S Wed Mar 10 16:53:37 1999 @@ -1,4 +1,4 @@ -/* $Id: trampoline.S,v 1.6 1998/10/11 06:58:23 davem Exp $ +/* $Id: trampoline.S,v 1.8 1998/12/09 21:01:15 davem Exp $ * trampoline.S: Jump start slave processors on sparc64. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -34,8 +34,8 @@ sllx %g4, 32, %g4 /* XXX Buggy PROM... */ - srl %o0, 0, %g6 - add %g6, %g4, %g6 + srl %o0, 0, %o0 + ldx [%o0], %g6 sethi %uhi(_PAGE_VALID | _PAGE_SZ4MB), %g5 sllx %g5, 32, %g5 @@ -197,10 +197,18 @@ #undef KERN_LOWBITS #undef VPTE_BASE + /* Setup interrupt globals, we are always SMP. */ wrpr %o1, (PSTATE_IG | PSTATE_IE), %pstate - sethi %hi(ivector_to_mask), %g5 - or %g5, %lo(ivector_to_mask), %g1 - mov 0x40, %g2 + + /* Get our UPA MID. */ + lduw [%o2 + AOFF_task_processor], %g1 + sethi %hi(cpu_data), %g5 + or %g5, %lo(cpu_data), %g5 + + /* In theory this is: &(cpu_data[this_upamid].irq_worklists[0]) */ + sllx %g1, 7, %g1 + add %g5, %g1, %g1 + add %g1, 64, %g1 wrpr %g0, 0, %wstate or %o1, PSTATE_IE, %o1 diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/traps.c linux/arch/sparc64/kernel/traps.c --- v2.2.3/linux/arch/sparc64/kernel/traps.c Sun Nov 8 14:02:48 1998 +++ linux/arch/sparc64/kernel/traps.c Wed Mar 10 16:53:37 1999 @@ -1,8 +1,8 @@ -/* $Id: traps.c,v 1.55 1998/10/11 06:58:22 davem Exp $ +/* $Id: traps.c,v 1.57 1999/03/02 15:42:18 jj Exp $ * arch/sparc64/kernel/traps.c * * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu) - * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997,1999 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ /* @@ -462,6 +462,9 @@ void die_if_kernel(char *str, struct pt_regs *regs) { + extern void __show_regs(struct pt_regs * regs); + extern void smp_report_regs(void); + /* Amuse the user. */ printk( " \\|/ ____ \\|/\n" @@ -471,7 +474,7 @@ printk("%s(%d): %s\n", current->comm, current->pid, str); __asm__ __volatile__("flushw"); - show_regs(regs); + __show_regs(regs); { struct reg_window *rw = (struct reg_window *) (regs->u_regs[UREG_FP] + STACK_BIAS); @@ -491,6 +494,10 @@ printk("Instruction DUMP:"); instruction_dump ((unsigned int *) regs->tpc); } +#ifdef __SMP__ + smp_report_regs(); +#endif + lock_kernel(); /* Or else! */ if(regs->tstate & TSTATE_PRIV) do_exit(SIGKILL); @@ -498,7 +505,7 @@ } extern int handle_popc(u32 insn, struct pt_regs *regs); -extern int handle_ldq_stq(u32 insn, struct pt_regs *regs); +extern int handle_ldf_stq(u32 insn, struct pt_regs *regs); void do_illegal_instruction(struct pt_regs *regs) { @@ -515,7 +522,7 @@ if (handle_popc(insn, regs)) return; } else if ((insn & 0xc1580000) == 0xc1100000) /* LDQ/STQ */ { - if (handle_ldq_stq(insn, regs)) + if (handle_ldf_stq(insn, regs)) return; } } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/unaligned.c linux/arch/sparc64/kernel/unaligned.c --- v2.2.3/linux/arch/sparc64/kernel/unaligned.c Sun Nov 8 14:02:48 1998 +++ linux/arch/sparc64/kernel/unaligned.c Wed Mar 10 16:53:37 1999 @@ -1,4 +1,4 @@ -/* $Id: unaligned.c,v 1.13 1998/10/07 22:43:13 davem Exp $ +/* $Id: unaligned.c,v 1.14 1999/03/02 15:42:16 jj Exp $ * unaligned.c: Unaligned load/store trap handling with special * cases for the kernel to do them more quickly. * @@ -470,7 +470,7 @@ extern void do_privact(struct pt_regs *regs); extern void data_access_exception(struct pt_regs *regs); -int handle_ldq_stq(u32 insn, struct pt_regs *regs) +int handle_ldf_stq(u32 insn, struct pt_regs *regs) { unsigned long addr = compute_effective_address(regs, insn, 0); int freg = ((insn >> 25) & 0x1e) | ((insn >> 20) & 0x20); @@ -522,8 +522,10 @@ return 1; } } else { - /* LDQ */ - u32 first, second, third, fourth; + /* LDF, LDDF, LDQF */ + u32 data[4] __attribute__ ((aligned(8))); + int size, i; + int err; if (asi < 0x80) { do_privact(regs); @@ -532,25 +534,35 @@ data_access_exception(regs); return 1; } - if (get_user (first, (u32 *)addr) || - __get_user (second, (u32 *)(addr + 4)) || - __get_user (third, (u32 *)(addr + 8)) || - __get_user (fourth, (u32 *)(addr + 12))) { - if (asi & 0x2) /* NF */ { - first = 0; second = 0; third = 0; fourth = 0; - } else { - data_access_exception(regs); - return 1; - } + switch (insn & 0x180000) { + case 0x000000: size = 1; break; + case 0x100000: size = 4; break; + default: size = 2; break; + } + for (i = 0; i < size; i++) + data[i] = 0; + + err = get_user (data[0], (u32 *)addr); + if (!err) { + for (i = 1; i < size; i++) + err |= __get_user (data[i], (u32 *)(addr + 4*i)); + } + if (err && !(asi & 0x2 /* NF */)) { + data_access_exception(regs); + return 1; } if (asi & 0x8) /* Little */ { - u32 tmp = le32_to_cpup(&first); - - first = le32_to_cpup(&fourth); - fourth = tmp; - tmp = le32_to_cpup(&second); - second = le32_to_cpup(&third); - third = tmp; + u64 tmp; + + switch (size) { + case 1: data[0] = le32_to_cpup(data + 0); break; + default:*(u64 *)(data + 0) = le64_to_cpup((u64 *)(data + 0)); + break; + case 4: tmp = le64_to_cpup((u64 *)(data + 0)); + *(u64 *)(data + 0) = le64_to_cpup((u64 *)(data + 2)); + *(u64 *)(data + 2) = tmp; + break; + } } if (!(current->tss.fpsaved[0] & FPRS_FEF)) { current->tss.fpsaved[0] = FPRS_FEF; @@ -562,14 +574,25 @@ else memset(f->regs+32, 0, 32*sizeof(u32)); } - f->regs[freg] = first; - f->regs[freg+1] = second; - f->regs[freg+2] = third; - f->regs[freg+3] = fourth; + memcpy(f->regs + freg, data, size * 4); current->tss.fpsaved[0] |= flag; } advance(regs); return 1; +} + +void handle_ld_nf(u32 insn, struct pt_regs *regs) +{ + int rd = ((insn >> 25) & 0x1f); + int from_kernel = (regs->tstate & TSTATE_PRIV) != 0; + unsigned long *reg; + + maybe_flush_windows(0, 0, rd, from_kernel); + reg = fetch_reg_addr(rd, regs); + if ((insn & 0x780000) == 0x180000) + reg[1] = 0; + reg[0] = 0; + advance(regs); } void handle_lddfmna(struct pt_regs *regs, unsigned long sfar, unsigned long sfsr) diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fabsq.c linux/arch/sparc64/math-emu/fabsq.c --- v2.2.3/linux/arch/sparc64/math-emu/fabsq.c Thu Apr 23 20:21:32 1998 +++ linux/arch/sparc64/math-emu/fabsq.c Wed Mar 10 16:53:37 1999 @@ -2,5 +2,5 @@ { rd[0] = rs2[0] & 0x7fffffffffffffffUL; rd[1] = rs2[1]; - return 1; + return 0; } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/faddd.c linux/arch/sparc64/math-emu/faddd.c --- v2.2.3/linux/arch/sparc64/math-emu/faddd.c Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/faddd.c Wed Mar 10 16:53:37 1999 @@ -8,6 +8,5 @@ __FP_UNPACK_D(A, rs1); __FP_UNPACK_D(B, rs2); FP_ADD_D(R, A, B); - __FP_PACK_D(rd, R); - return 1; + return __FP_PACK_D(rd, R); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/faddq.c linux/arch/sparc64/math-emu/faddq.c --- v2.2.3/linux/arch/sparc64/math-emu/faddq.c Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/faddq.c Wed Mar 10 16:53:37 1999 @@ -8,6 +8,5 @@ __FP_UNPACK_Q(A, rs1); __FP_UNPACK_Q(B, rs2); FP_ADD_Q(R, A, B); - __FP_PACK_Q(rd, R); - return 1; + return __FP_PACK_Q(rd, R); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fadds.c linux/arch/sparc64/math-emu/fadds.c --- v2.2.3/linux/arch/sparc64/math-emu/fadds.c Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/fadds.c Wed Mar 10 16:53:37 1999 @@ -8,6 +8,5 @@ __FP_UNPACK_S(A, rs1); __FP_UNPACK_S(B, rs2); FP_ADD_S(R, A, B); - __FP_PACK_S(rd, R); - return 1; + return __FP_PACK_S(rd, R); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fcmpeq.c linux/arch/sparc64/math-emu/fcmpeq.c --- v2.2.3/linux/arch/sparc64/math-emu/fcmpeq.c Thu Apr 23 20:21:32 1998 +++ linux/arch/sparc64/math-emu/fcmpeq.c Wed Mar 10 16:53:37 1999 @@ -21,5 +21,5 @@ case 3: fsr &= ~0x3000000000UL; fsr |= (ret << 36); break; } *(unsigned long *)rd = fsr; - return 1; + return 0; } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fcmpq.c linux/arch/sparc64/math-emu/fcmpq.c --- v2.2.3/linux/arch/sparc64/math-emu/fcmpq.c Thu Apr 23 20:21:32 1998 +++ linux/arch/sparc64/math-emu/fcmpq.c Wed Mar 10 16:53:37 1999 @@ -21,5 +21,5 @@ case 3: fsr &= ~0x3000000000UL; fsr |= (ret << 36); break; } *(unsigned long *)rd = fsr; - return 1; + return 0; } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fdivd.c linux/arch/sparc64/math-emu/fdivd.c --- v2.2.3/linux/arch/sparc64/math-emu/fdivd.c Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/fdivd.c Wed Mar 10 16:53:37 1999 @@ -4,10 +4,16 @@ int FDIVD(void *rd, void *rs2, void *rs1) { FP_DECL_D(A); FP_DECL_D(B); FP_DECL_D(R); + int ret = 0; __FP_UNPACK_D(A, rs1); __FP_UNPACK_D(B, rs2); + if(B_c == FP_CLS_ZERO && + A_c != FP_CLS_ZERO) { + ret |= EFLAG_DIVZERO; + if(__FPU_TRAP_P(EFLAG_DIVZERO)) + return ret; + } FP_DIV_D(R, A, B); - __FP_PACK_D(rd, R); - return 1; + return (ret | __FP_PACK_D(rd, R)); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fdivq.c linux/arch/sparc64/math-emu/fdivq.c --- v2.2.3/linux/arch/sparc64/math-emu/fdivq.c Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/fdivq.c Wed Mar 10 16:53:37 1999 @@ -4,10 +4,16 @@ int FDIVQ(void *rd, void *rs2, void *rs1) { FP_DECL_Q(A); FP_DECL_Q(B); FP_DECL_Q(R); + int ret; __FP_UNPACK_Q(A, rs1); __FP_UNPACK_Q(B, rs2); + if(B_c == FP_CLS_ZERO && + A_c != FP_CLS_ZERO) { + ret |= EFLAG_DIVZERO; + if(__FPU_TRAP_P(EFLAG_DIVZERO)) + return ret; + } FP_DIV_Q(R, A, B); - __FP_PACK_Q(rd, R); - return 1; + return (ret | __FP_PACK_Q(rd, R)); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fdivs.c linux/arch/sparc64/math-emu/fdivs.c --- v2.2.3/linux/arch/sparc64/math-emu/fdivs.c Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/fdivs.c Wed Mar 10 16:53:37 1999 @@ -4,10 +4,17 @@ int FDIVS(void *rd, void *rs2, void *rs1) { FP_DECL_S(A); FP_DECL_S(B); FP_DECL_S(R); + int ret = 0; __FP_UNPACK_S(A, rs1); __FP_UNPACK_S(B, rs2); + if(B_c == FP_CLS_ZERO && + A_c != FP_CLS_ZERO) { + ret |= EFLAG_DIVZERO; + if(__FPU_TRAP_P(EFLAG_DIVZERO)) + return ret; + } FP_DIV_S(R, A, B); - __FP_PACK_S(rd, R); - return 1; + return (ret | __FP_PACK_S(rd, R)); } + diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fdmulq.c linux/arch/sparc64/math-emu/fdmulq.c --- v2.2.3/linux/arch/sparc64/math-emu/fdmulq.c Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/fdmulq.c Wed Mar 10 16:53:37 1999 @@ -11,6 +11,5 @@ __FP_UNPACK_D(IN, rs2); FP_CONV(Q,D,2,1,B,IN); FP_MUL_Q(R, A, B); - __FP_PACK_Q(rd, R); - return 1; + return __FP_PACK_Q(rd, R); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fdtoi.c linux/arch/sparc64/math-emu/fdtoi.c --- v2.2.3/linux/arch/sparc64/math-emu/fdtoi.c Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/fdtoi.c Wed Mar 10 16:53:37 1999 @@ -9,5 +9,5 @@ __FP_UNPACK_D(A, rs2); FP_TO_INT_D(r, A, 32, 1); *rd = r; - return 1; + return 0; } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fdtoq.c linux/arch/sparc64/math-emu/fdtoq.c --- v2.2.3/linux/arch/sparc64/math-emu/fdtoq.c Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/fdtoq.c Wed Mar 10 16:53:37 1999 @@ -8,6 +8,5 @@ __FP_UNPACK_D(A, rs2); FP_CONV(Q,D,2,1,R,A); - __FP_PACK_Q(rd, R); - return 1; + return __FP_PACK_Q(rd, R); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fdtos.c linux/arch/sparc64/math-emu/fdtos.c --- v2.2.3/linux/arch/sparc64/math-emu/fdtos.c Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/fdtos.c Wed Mar 10 16:53:37 1999 @@ -8,6 +8,5 @@ __FP_UNPACK_D(A, rs2); FP_CONV(S,D,1,1,R,A); - __FP_PACK_S(rd, R); - return 1; + return __FP_PACK_S(rd, R); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fdtox.c linux/arch/sparc64/math-emu/fdtox.c --- v2.2.3/linux/arch/sparc64/math-emu/fdtox.c Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/fdtox.c Wed Mar 10 16:53:37 1999 @@ -9,5 +9,5 @@ __FP_UNPACK_D(A, rs2); FP_TO_INT_D(r, A, 64, 1); *rd = r; - return 1; + return 0; } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fitoq.c linux/arch/sparc64/math-emu/fitoq.c --- v2.2.3/linux/arch/sparc64/math-emu/fitoq.c Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/fitoq.c Wed Mar 10 16:53:37 1999 @@ -7,6 +7,5 @@ int a = *(int *)rs2; FP_FROM_INT_Q(R, a, 32, int); - __FP_PACK_Q(rd, R); - return 1; + return __FP_PACK_Q(rd, R); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fmuld.c linux/arch/sparc64/math-emu/fmuld.c --- v2.2.3/linux/arch/sparc64/math-emu/fmuld.c Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/fmuld.c Wed Mar 10 16:53:37 1999 @@ -8,6 +8,5 @@ __FP_UNPACK_D(A, rs1); __FP_UNPACK_D(B, rs2); FP_MUL_D(R, A, B); - __FP_PACK_D(rd, R); - return 1; + return __FP_PACK_D(rd, R); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fmulq.c linux/arch/sparc64/math-emu/fmulq.c --- v2.2.3/linux/arch/sparc64/math-emu/fmulq.c Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/fmulq.c Wed Mar 10 16:53:37 1999 @@ -8,6 +8,5 @@ __FP_UNPACK_Q(A, rs1); __FP_UNPACK_Q(B, rs2); FP_MUL_Q(R, A, B); - __FP_PACK_Q(rd, R); - return 1; + return __FP_PACK_Q(rd, R); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fmuls.c linux/arch/sparc64/math-emu/fmuls.c --- v2.2.3/linux/arch/sparc64/math-emu/fmuls.c Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/fmuls.c Wed Mar 10 16:53:37 1999 @@ -8,6 +8,5 @@ __FP_UNPACK_S(A, rs1); __FP_UNPACK_S(B, rs2); FP_MUL_S(R, A, B); - __FP_PACK_S(rd, R); - return 1; + return __FP_PACK_S(rd, R); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fnegq.c linux/arch/sparc64/math-emu/fnegq.c --- v2.2.3/linux/arch/sparc64/math-emu/fnegq.c Thu Apr 23 20:21:32 1998 +++ linux/arch/sparc64/math-emu/fnegq.c Wed Mar 10 16:53:37 1999 @@ -2,6 +2,6 @@ { rd[0] = rs2[0] ^ 0x8000000000000000UL; rd[1] = rs2[1]; - return 1; + return 0; } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fqtod.c linux/arch/sparc64/math-emu/fqtod.c --- v2.2.3/linux/arch/sparc64/math-emu/fqtod.c Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/fqtod.c Wed Mar 10 16:53:37 1999 @@ -8,6 +8,5 @@ __FP_UNPACK_Q(A, rs2); FP_CONV(D,Q,1,2,R,A); - __FP_PACK_D(rd, R); - return 1; + return __FP_PACK_D(rd, R); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fqtoi.c linux/arch/sparc64/math-emu/fqtoi.c --- v2.2.3/linux/arch/sparc64/math-emu/fqtoi.c Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/fqtoi.c Wed Mar 10 16:53:37 1999 @@ -9,5 +9,5 @@ __FP_UNPACK_Q(A, rs2); FP_TO_INT_Q(r, A, 32, 1); *rd = r; - return 1; + return 0; } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fqtos.c linux/arch/sparc64/math-emu/fqtos.c --- v2.2.3/linux/arch/sparc64/math-emu/fqtos.c Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/fqtos.c Wed Mar 10 16:53:37 1999 @@ -8,6 +8,5 @@ __FP_UNPACK_Q(A, rs2); FP_CONV(S,Q,1,2,R,A); - __FP_PACK_S(rd, R); - return 1; + return __FP_PACK_S(rd, R); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fqtox.c linux/arch/sparc64/math-emu/fqtox.c --- v2.2.3/linux/arch/sparc64/math-emu/fqtox.c Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/fqtox.c Wed Mar 10 16:53:37 1999 @@ -9,5 +9,5 @@ __FP_UNPACK_Q(A, rs2); FP_TO_INT_Q(r, A, 64, 1); *rd = r; - return 1; + return 0; } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fsmuld.c linux/arch/sparc64/math-emu/fsmuld.c --- v2.2.3/linux/arch/sparc64/math-emu/fsmuld.c Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/fsmuld.c Wed Mar 10 16:53:37 1999 @@ -11,6 +11,5 @@ __FP_UNPACK_S(IN, rs2); FP_CONV(D,S,1,1,B,IN); FP_MUL_D(R, A, B); - __FP_PACK_D(rd, R); - return 1; + return __FP_PACK_D(rd, R); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fsqrtd.c linux/arch/sparc64/math-emu/fsqrtd.c --- v2.2.3/linux/arch/sparc64/math-emu/fsqrtd.c Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/fsqrtd.c Wed Mar 10 16:53:37 1999 @@ -7,6 +7,5 @@ __FP_UNPACK_D(A, rs2); FP_SQRT_D(R, A); - __FP_PACK_D(rd, R); - return 1; + return __FP_PACK_D(rd, R); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fsqrtq.c linux/arch/sparc64/math-emu/fsqrtq.c --- v2.2.3/linux/arch/sparc64/math-emu/fsqrtq.c Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/fsqrtq.c Wed Mar 10 16:53:37 1999 @@ -7,6 +7,5 @@ __FP_UNPACK_Q(A, rs2); FP_SQRT_Q(R, A); - __FP_PACK_Q(rd, R); - return 1; + return __FP_PACK_Q(rd, R); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fsqrts.c linux/arch/sparc64/math-emu/fsqrts.c --- v2.2.3/linux/arch/sparc64/math-emu/fsqrts.c Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/fsqrts.c Wed Mar 10 16:53:37 1999 @@ -7,6 +7,5 @@ __FP_UNPACK_S(A, rs2); FP_SQRT_S(R, A); - __FP_PACK_S(rd, R); - return 1; + return __FP_PACK_S(rd, R); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fstod.c linux/arch/sparc64/math-emu/fstod.c --- v2.2.3/linux/arch/sparc64/math-emu/fstod.c Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/fstod.c Wed Mar 10 16:53:37 1999 @@ -8,6 +8,5 @@ __FP_UNPACK_S(A, rs2); FP_CONV(D,S,1,1,R,A); - __FP_PACK_D(rd, R); - return 1; + return __FP_PACK_D(rd, R); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fstoi.c linux/arch/sparc64/math-emu/fstoi.c --- v2.2.3/linux/arch/sparc64/math-emu/fstoi.c Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/fstoi.c Wed Mar 10 16:53:37 1999 @@ -9,5 +9,5 @@ __FP_UNPACK_S(A, rs2); FP_TO_INT_S(r, A, 32, 1); *rd = r; - return 1; + return 0; } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fstoq.c linux/arch/sparc64/math-emu/fstoq.c --- v2.2.3/linux/arch/sparc64/math-emu/fstoq.c Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/fstoq.c Wed Mar 10 16:53:37 1999 @@ -8,6 +8,5 @@ __FP_UNPACK_S(A, rs2); FP_CONV(Q,S,2,1,R,A); - __FP_PACK_Q(rd, R); - return 1; + return __FP_PACK_Q(rd, R); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fstox.c linux/arch/sparc64/math-emu/fstox.c --- v2.2.3/linux/arch/sparc64/math-emu/fstox.c Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/fstox.c Wed Mar 10 16:53:37 1999 @@ -9,5 +9,5 @@ __FP_UNPACK_S(A, rs2); FP_TO_INT_S(r, A, 64, 1); *rd = r; - return 1; + return 0; } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fsubd.c linux/arch/sparc64/math-emu/fsubd.c --- v2.2.3/linux/arch/sparc64/math-emu/fsubd.c Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/fsubd.c Wed Mar 10 16:53:37 1999 @@ -10,6 +10,5 @@ if (B_c != FP_CLS_NAN) B_s ^= 1; FP_ADD_D(R, A, B); - __FP_PACK_D(rd, R); - return 1; + return __FP_PACK_D(rd, R); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fsubq.c linux/arch/sparc64/math-emu/fsubq.c --- v2.2.3/linux/arch/sparc64/math-emu/fsubq.c Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/fsubq.c Wed Mar 10 16:53:37 1999 @@ -10,6 +10,5 @@ if (B_c != FP_CLS_NAN) B_s ^= 1; FP_ADD_Q(R, A, B); - __FP_PACK_Q(rd, R); - return 1; + return __FP_PACK_Q(rd, R); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fsubs.c linux/arch/sparc64/math-emu/fsubs.c --- v2.2.3/linux/arch/sparc64/math-emu/fsubs.c Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/fsubs.c Wed Mar 10 16:53:37 1999 @@ -10,6 +10,5 @@ if (B_c != FP_CLS_NAN) B_s ^= 1; FP_ADD_S(R, A, B); - __FP_PACK_S(rd, R); - return 1; + return __FP_PACK_S(rd, R); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fxtoq.c linux/arch/sparc64/math-emu/fxtoq.c --- v2.2.3/linux/arch/sparc64/math-emu/fxtoq.c Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/fxtoq.c Wed Mar 10 16:53:37 1999 @@ -7,6 +7,5 @@ long a = *(long *)rs2; FP_FROM_INT_Q(R, a, 64, long); - __FP_PACK_Q(rd, R); - return 1; + return __FP_PACK_Q(rd, R); } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/math.c linux/arch/sparc64/math-emu/math.c --- v2.2.3/linux/arch/sparc64/math-emu/math.c Thu Aug 6 14:06:31 1998 +++ linux/arch/sparc64/math-emu/math.c Wed Mar 10 16:53:37 1999 @@ -1,7 +1,8 @@ -/* $Id: math.c,v 1.5 1998/06/12 14:54:27 jj Exp $ +/* $Id: math.c,v 1.7 1999/02/10 14:16:26 davem Exp $ * arch/sparc64/math-emu/math.c * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1999 David S. Miller (davem@redhat.com) * * Emulation routines originate from soft-fp package, which is part * of glibc and has appropriate copyrights in it. @@ -14,6 +15,8 @@ #include #include +#include "soft-fp.h" + #define FLOATFUNC(x) extern int x(void *,void *,void *); FLOATFUNC(FMOVQ) @@ -54,6 +57,91 @@ FLOATFUNC(FSTOI) FLOATFUNC(FDTOI) +#define FSR_TEM_SHIFT 23UL +#define FSR_TEM_MASK (0x1fUL << FSR_TEM_SHIFT) +#define FSR_AEXC_SHIFT 5UL +#define FSR_AEXC_MASK (0x1fUL << FSR_AEXC_SHIFT) +#define FSR_CEXC_SHIFT 0UL +#define FSR_CEXC_MASK (0x1fUL << FSR_CEXC_SHIFT) + +/* All routines returning an exception to raise should detect + * such exceptions _before_ rounding to be consistant with + * the behavior of the hardware in the implemented cases + * (and thus with the recommendations in the V9 architecture + * manual). + * + * We return 0 if a SIGFPE should be sent, 1 otherwise. + */ +static int record_exception(struct pt_regs *regs, int eflag) +{ + u64 fsr = current->tss.xfsr[0]; + int would_trap; + + /* Determine if this exception would have generated a trap. */ + would_trap = (fsr & ((long)eflag << FSR_TEM_SHIFT)) != 0UL; + + /* If trapping, we only want to signal one bit. */ + if(would_trap != 0) { + eflag &= ((fsr & FSR_TEM_MASK) >> FSR_TEM_SHIFT); + if((eflag & (eflag - 1)) != 0) { + if(eflag & EFLAG_INVALID) + eflag = EFLAG_INVALID; + else if(eflag & EFLAG_DIVZERO) + eflag = EFLAG_DIVZERO; + else if(eflag & EFLAG_INEXACT) + eflag = EFLAG_INEXACT; + } + } + + /* Set CEXC, here are the rules: + * + * 1) In general all FPU ops will set one and only one + * bit in the CEXC field, this is always the case + * when the IEEE exception trap is enabled in TEM. + * + * 2) As a special case, if an overflow or underflow + * is being signalled, AND the trap is not enabled + * in TEM, then the inexact field shall also be set. + */ + fsr &= ~(FSR_CEXC_MASK); + if(would_trap || + (eflag & (EFLAG_OVERFLOW | EFLAG_UNDERFLOW)) == 0) { + fsr |= ((long)eflag << FSR_CEXC_SHIFT); + } else { + fsr |= (((long)eflag << FSR_CEXC_SHIFT) | + (EFLAG_INEXACT << FSR_CEXC_SHIFT)); + } + + /* Set the AEXC field, rules are: + * + * 1) If a trap would not be generated, the + * CEXC just generated is OR'd into the + * existing value of AEXC. + * + * 2) When a trap is generated, AEXC is cleared. + */ + if(would_trap == 0) + fsr |= ((long)eflag << FSR_AEXC_SHIFT); + else + fsr &= ~(FSR_AEXC_MASK); + + /* If trapping, indicate fault trap type IEEE. */ + if(would_trap != 0) + fsr |= (1UL << 14); + + current->tss.xfsr[0] = fsr; + + /* If we will not trap, advance the program counter over + * the instruction being handled. + */ + if(would_trap == 0) { + regs->tpc = regs->tnpc; + regs->tnpc += 4; + } + + return (would_trap ? 0 : 1); +} + int do_mathemu(struct pt_regs *regs, struct fpustate *f) { unsigned long pc = regs->tpc; @@ -175,7 +263,12 @@ current->tss.fpsaved[0] |= flags; break; } - func(rd, rs2, rs1); + flags = func(rd, rs2, rs1); + if(flags != 0) + return record_exception(regs, flags); + + /* Success and no exceptions detected. */ + current->tss.xfsr[0] &= ~(FSR_CEXC_MASK); regs->tpc = regs->tnpc; regs->tnpc += 4; return 1; diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/op-2.h linux/arch/sparc64/math-emu/op-2.h --- v2.2.3/linux/arch/sparc64/math-emu/op-2.h Thu Apr 23 20:21:32 1998 +++ linux/arch/sparc64/math-emu/op-2.h Wed Mar 10 16:53:37 1999 @@ -190,14 +190,14 @@ \ __FP_FRAC_ADD_4(_FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2), \ _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0), \ + 0, _b_f1, _b_f0, 0, \ _FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2), \ - _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0), \ - 0, _b_f1, _b_f0, 0); \ + _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0)); \ __FP_FRAC_ADD_4(_FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2), \ _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0), \ + 0, _c_f1, _c_f0, 0, \ _FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2), \ - _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0), \ - 0, _c_f1, _c_f0, 0); \ + _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0)); \ \ /* Normalize since we know where the msb of the multiplicands \ were (bit B), we know that the msb of the of the product is \ diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/op-common.h linux/arch/sparc64/math-emu/op-common.h --- v2.2.3/linux/arch/sparc64/math-emu/op-common.h Thu Aug 6 14:06:31 1998 +++ linux/arch/sparc64/math-emu/op-common.h Wed Mar 10 16:53:37 1999 @@ -53,14 +53,14 @@ */ #define _FP_PACK_CANONICAL(fs, wc, X) \ -do { \ +({int __ret = 0; \ switch (X##_c) \ { \ case FP_CLS_NORMAL: \ X##_e += _FP_EXPBIAS_##fs; \ if (X##_e > 0) \ { \ - _FP_ROUND(wc, X); \ + __ret |= _FP_ROUND(wc, X); \ if (_FP_FRAC_OVERP_##wc(fs, X)) \ { \ _FP_FRAC_SRL_##wc(X, (_FP_WORKBITS+1)); \ @@ -73,6 +73,7 @@ /* overflow to infinity */ \ X##_e = _FP_EXPMAX_##fs; \ _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc); \ + __ret |= EFLAG_OVERFLOW; \ } \ } \ else \ @@ -82,7 +83,7 @@ if (X##_e <= _FP_WFRACBITS_##fs) \ { \ _FP_FRAC_SRS_##wc(X, X##_e, _FP_WFRACBITS_##fs); \ - _FP_ROUND(wc, X); \ + __ret |= _FP_ROUND(wc, X); \ _FP_FRAC_SLL_##wc(X, 1); \ if (_FP_FRAC_OVERP_##wc(fs, X)) \ { \ @@ -93,6 +94,7 @@ { \ X##_e = 0; \ _FP_FRAC_SRL_##wc(X, _FP_WORKBITS+1); \ + __ret |= EFLAG_UNDERFLOW; \ } \ } \ else \ @@ -100,6 +102,7 @@ /* underflow to zero */ \ X##_e = 0; \ _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc); \ + __ret |= EFLAG_UNDERFLOW; \ } \ } \ break; \ @@ -125,7 +128,8 @@ _FP_FRAC_HIGH_##wc(X) |= _FP_QNANBIT_##fs; \ break; \ } \ -} while (0) + __ret; \ +}) /* @@ -424,11 +428,19 @@ } \ else \ { \ - /* Force -0 -> +0 */ \ - if (!X##_e && _FP_FRAC_ZEROP_##wc(X)) X##_s = 0; \ - if (!Y##_e && _FP_FRAC_ZEROP_##wc(Y)) X##_s = 0; \ + int __is_zero_x; \ + int __is_zero_y; \ + \ + __is_zero_x = (!X##_e && _FP_FRAC_ZEROP_##wc(X)) ? 1 : 0; \ + __is_zero_y = (!Y##_e && _FP_FRAC_ZEROP_##wc(Y)) ? 1 : 0; \ \ - if (X##_s != Y##_s) \ + if (__is_zero_x && __is_zero_y) \ + ret = 0; \ + else if (__is_zero_x) \ + ret = Y##_s ? 1 : -1; \ + else if (__is_zero_y) \ + ret = X##_s ? -1 : 1; \ + else if (X##_s != Y##_s) \ ret = X##_s ? -1 : 1; \ else if (X##_e > Y##_e) \ ret = X##_s ? -1 : 1; \ diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/sfp-machine.h linux/arch/sparc64/math-emu/sfp-machine.h --- v2.2.3/linux/arch/sparc64/math-emu/sfp-machine.h Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/sfp-machine.h Tue Mar 16 21:52:06 1999 @@ -52,16 +52,6 @@ X##_s = _flo->bits.sign; \ } while (0) -#define __FP_PACK_RAW_1(fs, val, X) \ - do { \ - union _FP_UNION_##fs *_flo = \ - (union _FP_UNION_##fs *)val; \ - \ - _flo->bits.frac = X##_f; \ - _flo->bits.exp = X##_e; \ - _flo->bits.sign = X##_s; \ - } while (0) - #define __FP_UNPACK_RAW_2(fs, X, val) \ do { \ union _FP_UNION_##fs *_flo = \ @@ -73,52 +63,79 @@ X##_s = _flo->bits.sign; \ } while (0) -#define __FP_PACK_RAW_2(fs, val, X) \ - do { \ - union _FP_UNION_##fs *_flo = \ - (union _FP_UNION_##fs *)val; \ - \ - _flo->bits.frac0 = X##_f0; \ - _flo->bits.frac1 = X##_f1; \ - _flo->bits.exp = X##_e; \ - _flo->bits.sign = X##_s; \ - } while (0) - #define __FP_UNPACK_S(X,val) \ do { \ __FP_UNPACK_RAW_1(S,X,val); \ _FP_UNPACK_CANONICAL(S,1,X); \ } while (0) -#define __FP_PACK_S(val,X) \ - do { \ - _FP_PACK_CANONICAL(S,1,X); \ - __FP_PACK_RAW_1(S,val,X); \ - } while (0) - #define __FP_UNPACK_D(X,val) \ do { \ __FP_UNPACK_RAW_1(D,X,val); \ _FP_UNPACK_CANONICAL(D,1,X); \ } while (0) -#define __FP_PACK_D(val,X) \ - do { \ - _FP_PACK_CANONICAL(D,1,X); \ - __FP_PACK_RAW_1(D,val,X); \ - } while (0) - #define __FP_UNPACK_Q(X,val) \ do { \ __FP_UNPACK_RAW_2(Q,X,val); \ _FP_UNPACK_CANONICAL(Q,2,X); \ } while (0) -#define __FP_PACK_Q(val,X) \ - do { \ - _FP_PACK_CANONICAL(Q,2,X); \ - __FP_PACK_RAW_2(Q,val,X); \ +#define __FP_PACK_RAW_1(fs, val, X) \ + do { \ + union _FP_UNION_##fs *_flo = \ + (union _FP_UNION_##fs *)val; \ + \ + _flo->bits.frac = X##_f; \ + _flo->bits.exp = X##_e; \ + _flo->bits.sign = X##_s; \ } while (0) + +#define __FP_PACK_RAW_2(fs, val, X) \ + do { \ + union _FP_UNION_##fs *_flo = \ + (union _FP_UNION_##fs *)val; \ + \ + _flo->bits.frac0 = X##_f0; \ + _flo->bits.frac1 = X##_f1; \ + _flo->bits.exp = X##_e; \ + _flo->bits.sign = X##_s; \ + } while (0) + +#include +#include + +/* We only actually write to the destination register + * if exceptions signalled (if any) will not trap. + */ +#define __FPU_TEM \ + (((current->tss.xfsr[0])>>23)&0x1f) +#define __FPU_TRAP_P(bits) \ + ((__FPU_TEM & (bits)) != 0) + +#define __FP_PACK_S(val,X) \ +({ int __exc = _FP_PACK_CANONICAL(S,1,X); \ + if(!__exc || !__FPU_TRAP_P(__exc)) \ + __FP_PACK_RAW_1(S,val,X); \ + __exc; \ +}) + +#define __FP_PACK_D(val,X) \ +({ int __exc = _FP_PACK_CANONICAL(D,1,X); \ + if(!__exc || !__FPU_TRAP_P(__exc)) \ + __FP_PACK_RAW_1(D,val,X); \ + __exc; \ +}) + +#define __FP_PACK_Q(val,X) \ +({ int __exc = _FP_PACK_CANONICAL(Q,2,X); \ + if(!__exc || !__FPU_TRAP_P(__exc)) \ + __FP_PACK_RAW_2(Q,val,X); \ + __exc; \ +}) + +/* Obtain the current rounding mode. */ +#define FP_ROUNDMODE ((current->tss.xfsr[0] >> 30) & 0x3) #include #include @@ -153,28 +170,24 @@ #define umul_ppmm(wh, wl, u, v) \ do { \ - long tmp1 = 0, tmp2 = 0, tmp3 = 0; \ __asm__ ("mulx %2,%3,%1 - srlx %2,32,%4 - srl %3,0,%5 - mulx %4,%5,%6 - srlx %3,32,%4 - srl %2,0,%5 - mulx %4,%5,%5 - srlx %2,32,%4 - add %5,%6,%6 - srlx %3,32,%5 - mulx %4,%5,%4 - srlx %6,32,%5 - add %4,%5,%0" \ + srlx %2,32,%%g1 + srl %3,0,%%g2 + mulx %%g1,%%g2,%%g3 + srlx %3,32,%%g1 + srl %2,0,%%g2 + mulx %%g1,%%g2,%%g2 + srlx %2,32,%%g1 + add %%g2,%%g3,%%g3 + srlx %3,32,%%g2 + mulx %%g1,%%g2,%%g1 + srlx %%g3,32,%%g2 + add %%g1,%%g2,%0" \ : "=r" ((UDItype)(wh)), \ "=&r" ((UDItype)(wl)) \ : "r" ((UDItype)(u)), \ - "r" ((UDItype)(v)), \ - "r" ((UDItype)(tmp1)), \ - "r" ((UDItype)(tmp2)), \ - "r" ((UDItype)(tmp3)) \ - : "cc"); \ + "r" ((UDItype)(v)) \ + : "g1", "g2", "g3", "cc"); \ } while (0) #define udiv_qrnnd(q, r, n1, n0, d) \ @@ -223,3 +236,10 @@ #else #define __BYTE_ORDER __LITTLE_ENDIAN #endif + +/* Exception flags. */ +#define EFLAG_INVALID (1 << 4) +#define EFLAG_OVERFLOW (1 << 3) +#define EFLAG_UNDERFLOW (1 << 2) +#define EFLAG_DIVZERO (1 << 1) +#define EFLAG_INEXACT (1 << 0) diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/soft-fp.h linux/arch/sparc64/math-emu/soft-fp.h --- v2.2.3/linux/arch/sparc64/math-emu/soft-fp.h Mon Jan 12 15:15:44 1998 +++ linux/arch/sparc64/math-emu/soft-fp.h Wed Mar 10 16:53:37 1999 @@ -14,45 +14,56 @@ # define FP_RND_ZERO 1 # define FP_RND_PINF 2 # define FP_RND_MINF 3 +#ifndef FP_ROUNDMODE # define FP_ROUNDMODE FP_RND_NEAREST #endif +#endif #define _FP_ROUND_NEAREST(wc, X) \ - do { \ +({ int __ret = EFLAG_INEXACT; \ if ((_FP_FRAC_LOW_##wc(X) & 15) != _FP_WORK_ROUND) \ _FP_FRAC_ADDI_##wc(X, _FP_WORK_ROUND); \ - } while(0) + else __ret = 0; \ + __ret; \ +}) -#define _FP_ROUND_ZERO(wc, X) +#define _FP_ROUND_ZERO(wc, X) 0 /* XXX */ #define _FP_ROUND_PINF(wc, X) \ - do { \ +({ int __ret = EFLAG_INEXACT; \ if (!X##_s && (_FP_FRAC_LOW_##wc(X) & 7)) \ _FP_FRAC_ADDI_##wc(X, _FP_WORK_LSB); \ - } while (0) + else __ret = 0; \ + __ret; \ +}) #define _FP_ROUND_MINF(wc, X) \ - do { \ +({ int __ret = EFLAG_INEXACT; \ if (X##_s && (_FP_FRAC_LOW_##wc(X) & 7)) \ _FP_FRAC_ADDI_##wc(X, _FP_WORK_LSB); \ - } while (0) + else __ret = 0; \ + __ret; \ +}) #define _FP_ROUND(wc, X) \ +({ int __ret = 0; \ switch (FP_ROUNDMODE) \ { \ case FP_RND_NEAREST: \ - _FP_ROUND_NEAREST(wc,X); \ + __ret |= _FP_ROUND_NEAREST(wc,X); \ break; \ case FP_RND_ZERO: \ - _FP_ROUND_ZERO(wc,X); \ + __ret |= _FP_ROUND_ZERO(wc,X); \ break; \ case FP_RND_PINF: \ - _FP_ROUND_PINF(wc,X); \ + __ret |= _FP_ROUND_PINF(wc,X); \ break; \ case FP_RND_MINF: \ - _FP_ROUND_MINF(wc,X); \ + __ret |= _FP_ROUND_MINF(wc,X); \ break; \ - } + }; \ + __ret; \ +}) #define FP_CLS_NORMAL 0 #define FP_CLS_ZERO 1 diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/mm/fault.c linux/arch/sparc64/mm/fault.c --- v2.2.3/linux/arch/sparc64/mm/fault.c Thu Nov 19 09:56:27 1998 +++ linux/arch/sparc64/mm/fault.c Tue Mar 16 21:52:06 1999 @@ -1,8 +1,8 @@ -/* $Id: fault.c,v 1.26 1998/11/08 11:14:03 davem Exp $ +/* $Id: fault.c,v 1.34 1999/03/16 12:12:28 jj Exp $ * arch/sparc64/mm/fault.c: Page fault handlers for the 64-bit Sparc. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) - * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz) */ #include @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include @@ -26,7 +28,7 @@ extern struct sparc_phys_banks sp_banks[SPARC_PHYS_BANKS]; /* Nice, simple, prom library does all the sweating for us. ;) */ -unsigned long prom_probe_memory (void) +unsigned long __init prom_probe_memory (void) { register struct linux_mlist_p1275 *mlist; register unsigned long bytes, base_paddr, tally; @@ -35,7 +37,7 @@ i = 0; mlist = *prom_meminfo()->p1275_available; bytes = tally = mlist->num_bytes; - base_paddr = (unsigned int) mlist->start_adr; + base_paddr = mlist->start_adr; sp_banks[0].base_addr = base_paddr; sp_banks[0].num_bytes = bytes; @@ -55,12 +57,12 @@ break; } - sp_banks[i].base_addr = (unsigned long) mlist->start_adr; + sp_banks[i].base_addr = mlist->start_adr; sp_banks[i].num_bytes = mlist->num_bytes; } i++; - sp_banks[i].base_addr = 0xdeadbeef; + sp_banks[i].base_addr = 0xdeadbeefbeefdeadUL; sp_banks[i].num_bytes = 0; /* Now mask all bank sizes on a page boundary, it is all we can @@ -72,26 +74,12 @@ return tally; } -/* Traverse the memory lists in the prom to see how much physical we - * have. - */ -unsigned long -probe_memory(void) -{ - unsigned long total; - - total = prom_probe_memory(); - - /* Oh man, much nicer, keep the dirt in promlib. */ - return total; -} - void unhandled_fault(unsigned long address, struct task_struct *tsk, struct pt_regs *regs) { if((unsigned long) address < PAGE_SIZE) { printk(KERN_ALERT "Unable to handle kernel NULL " - "pointer dereference"); + "pointer dereference\n"); } else { printk(KERN_ALERT "Unable to handle kernel paging request " "at virtual address %016lx\n", (unsigned long)address); @@ -100,22 +88,74 @@ (unsigned long) tsk->mm->context); printk(KERN_ALERT "tsk->mm->pgd = %016lx\n", (unsigned long) tsk->mm->pgd); - lock_kernel(); die_if_kernel("Oops", regs); - unlock_kernel(); } /* #define DEBUG_EXCEPTIONS */ /* #define DEBUG_LOCKUPS */ +/* #define INSN_VPTE_LOOKUP */ + +static inline u32 get_user_insn(unsigned long tpc) +{ + u32 insn; +#ifndef INSN_VPTE_LOOKUP + pgd_t *pgdp = pgd_offset(current->mm, tpc); + pmd_t *pmdp; + pte_t *ptep; + + if(pgd_none(*pgdp)) + return 0; + pmdp = pmd_offset(pgdp, tpc); + if(pmd_none(*pmdp)) + return 0; + ptep = pte_offset(pmdp, tpc); + if(!pte_present(*ptep)) + return 0; + insn = *(unsigned int *)(pte_page(*ptep) + (tpc & ~PAGE_MASK)); +#else + register unsigned long pte asm("l1"); + + /* So that we don't pollute TLB, we read the instruction + * using PHYS bypass. For that, we of course need + * to know its page table entry. Do this by simulating + * dtlb_miss handler. -jj */ + pte = ((((long)tpc) >> (PAGE_SHIFT-3)) & ~7); + asm volatile (" + rdpr %%pstate, %%l0 + wrpr %%l0, %2, %%pstate + wrpr %%g0, 1, %%tl + mov %%l1, %%g6 + ldxa [%%g3 + %%l1] %3, %%g5 + mov %%g5, %%l1 + wrpr %%g0, 0, %%tl + wrpr %%l0, 0, %%pstate + " : "=r" (pte) : "0" (pte), "i" (PSTATE_MG|PSTATE_IE), "i" (ASI_S) : "l0"); + + if ((long)pte >= 0) return 0; + + pte = (pte & _PAGE_PADDR) + (tpc & ~PAGE_MASK); + asm ("lduwa [%1] %2, %0" : "=r" (insn) : "r" (pte), "i" (ASI_PHYS_USE_EC)); +#endif + + return insn; +} + asmlinkage void do_sparc64_fault(struct pt_regs *regs, unsigned long address, int write) { struct mm_struct *mm = current->mm; struct vm_area_struct *vma; + unsigned int insn = 0; #ifdef DEBUG_LOCKUPS static unsigned long lastaddr, lastpc; static int lastwrite, lockcnt; #endif + /* + * If we're in an interrupt or have no user + * context, we must not take the fault.. + */ + if (in_interrupt() || mm == &init_mm) + goto do_kernel_fault; down(&mm->mmap_sem); #ifdef DEBUG_LOCKUPS @@ -135,6 +175,21 @@ vma = find_vma(mm, address); if(!vma) goto bad_area; +#ifndef INSN_VPTE_LOOKUP + write &= 0xf; +#else + if (write & 0x10) { + write = 0; + if((vma->vm_flags & VM_WRITE)) { + if (regs->tstate & TSTATE_PRIV) + insn = *(unsigned int *)regs->tpc; + else + insn = get_user_insn(regs->tpc); + if ((insn & 0xc0200000) == 0xc0200000 && (insn & 0x1780000) != 0x1680000) + write = 1; + } + } +#endif if(vma->vm_start <= address) goto good_area; if(!(vma->vm_flags & VM_GROWSDOWN)) @@ -168,16 +223,44 @@ do_kernel_fault: { - unsigned long g2 = regs->u_regs[UREG_G2]; + unsigned long g2; + unsigned char asi = ASI_P; + + if (!insn) { + if (regs->tstate & TSTATE_PRIV) + insn = *(unsigned int *)regs->tpc; + else + insn = get_user_insn(regs->tpc); + } + if (write != 1 && (insn & 0xc0800000) == 0xc0800000) { + if (insn & 0x2000) + asi = (regs->tstate >> 24); + else + asi = (insn >> 5); + if ((asi & 0xf2) == 0x82) { + /* This was a non-faulting load. Just clear the + destination register(s) and continue with the next + instruction. -jj */ + if (insn & 0x1000000) { + extern int handle_ldf_stq(u32, struct pt_regs *); + + handle_ldf_stq(insn, regs); + } else { + extern int handle_ld_nf(u32, struct pt_regs *); + + handle_ld_nf(insn, regs); + } + return; + } + } + + g2 = regs->u_regs[UREG_G2]; /* Is this in ex_table? */ if (regs->tstate & TSTATE_PRIV) { - unsigned char asi = ASI_P; - unsigned int insn; unsigned long fixup; - - insn = *(unsigned int *)regs->tpc; - if ((insn & 0xc0800000) == 0xc0800000) { + + if (asi == ASI_P && (insn & 0xc0800000) == 0xc0800000) { if (insn & 0x2000) asi = (regs->tstate >> 24); else diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/mm/generic.c linux/arch/sparc64/mm/generic.c --- v2.2.3/linux/arch/sparc64/mm/generic.c Thu Nov 19 09:56:27 1998 +++ linux/arch/sparc64/mm/generic.c Mon Mar 15 16:10:43 1999 @@ -1,4 +1,4 @@ -/* $Id: generic.c,v 1.3 1998/10/27 23:28:07 davem Exp $ +/* $Id: generic.c,v 1.8 1999/03/12 06:51:50 davem Exp $ * generic.c: Generic Sparc mm routines that are not dependent upon * MMU type but are Sparc specific. * @@ -56,6 +56,10 @@ * * They use a pgprot that sets PAGE_IO and does not check the * mem_map table as this is independent of normal memory. + * + * As a special hack if the lowest bit of offset is set the + * side-effect bit will be turned off. This is used as a + * performance improvement on FFB/AFB. -DaveM */ static inline void io_remap_pte_range(pte_t * pte, unsigned long address, unsigned long size, unsigned long offset, pgprot_t prot, int space) @@ -71,23 +75,32 @@ pte_t entry; unsigned long curend = address + PAGE_SIZE; - entry = mk_pte_io(offset, prot, space); - offset += PAGE_SIZE; + entry = mk_pte_io((offset & ~(0x1UL)), prot, space); if (!(address & 0xffff)) { - if (!(address & 0x3fffff) && !(offset & 0x3fffff) && end >= address + 0x400000) { - entry = mk_pte_io(offset, __pgprot(pgprot_val (prot) | _PAGE_SZ4MB), space); + if (!(address & 0x3fffff) && !(offset & 0x3ffffe) && end >= address + 0x400000) { + entry = mk_pte_io((offset & ~(0x1UL)), + __pgprot(pgprot_val (prot) | _PAGE_SZ4MB), + space); curend = address + 0x400000; - offset += 0x400000 - PAGE_SIZE; - } else if (!(address & 0x7ffff) && !(offset & 0x7ffff) && end >= address + 0x80000) { - entry = mk_pte_io(offset, __pgprot(pgprot_val (prot) | _PAGE_SZ512K), space); + offset += 0x400000; + } else if (!(address & 0x7ffff) && !(offset & 0x7fffe) && end >= address + 0x80000) { + entry = mk_pte_io((offset & ~(0x1UL)), + __pgprot(pgprot_val (prot) | _PAGE_SZ512K), + space); curend = address + 0x80000; - offset += 0x80000 - PAGE_SIZE; - } else if (!(offset & 0xffff) && end >= address + 0x10000) { - entry = mk_pte_io(offset, __pgprot(pgprot_val (prot) | _PAGE_SZ64K), space); + offset += 0x80000; + } else if (!(offset & 0xfffe) && end >= address + 0x10000) { + entry = mk_pte_io((offset & ~(0x1UL)), + __pgprot(pgprot_val (prot) | _PAGE_SZ64K), + space); curend = address + 0x10000; - offset += 0x10000 - PAGE_SIZE; + offset += 0x10000; } - } + } else + offset += PAGE_SIZE; + + if (offset & 0x1UL) + pte_val(entry) &= ~(_PAGE_E); do { oldpage = *pte; pte_clear(pte); diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/mm/init.c linux/arch/sparc64/mm/init.c --- v2.2.3/linux/arch/sparc64/mm/init.c Sun Nov 8 14:02:49 1998 +++ linux/arch/sparc64/mm/init.c Wed Mar 10 16:53:37 1999 @@ -1,8 +1,8 @@ -/* $Id: init.c,v 1.103 1998/10/20 03:09:12 jj Exp $ +/* $Id: init.c,v 1.124 1999/02/08 07:01:42 ecd Exp $ * arch/sparc64/mm/init.c * - * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu) - * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1996-1999 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1997-1999 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ #include @@ -23,6 +23,7 @@ #include #include #include +#include /* Turn this off if you suspect some place in some physical memory hole might get into page tables (something would be broken very much). */ @@ -34,6 +35,8 @@ struct sparc_phys_banks sp_banks[SPARC_PHYS_BANKS]; +unsigned long *sparc64_valid_addr_bitmap; + /* Ugly, but necessary... -DaveM */ unsigned long phys_base; @@ -69,7 +72,7 @@ page->next_hash = NULL; page->pprev_hash = NULL; pgd_cache_size -= 2; - free_page(PAGE_OFFSET + (page->map_nr << PAGE_SHIFT)); + __free_page(page); freed++; if (page2) page = page2->next_hash; @@ -160,27 +163,31 @@ static unsigned long dvmaiobase = 0; static unsigned long dvmaiosz __initdata = 0; -/* #define E3000_DEBUG */ - __initfunc(void dvmaio_init(void)) { - int i; + long i; if (!dvmaiobase) { for (i = 0; sp_banks[i].num_bytes != 0; i++) if (sp_banks[i].base_addr + sp_banks[i].num_bytes > dvmaiobase) dvmaiobase = sp_banks[i].base_addr + sp_banks[i].num_bytes; + + /* We map directly phys_base to phys_base+(4GB-DVMAIO_SIZE). */ + dvmaiobase -= phys_base; + dvmaiobase = (dvmaiobase + DVMAIO_SIZE + 0x400000 - 1) & ~(0x400000 - 1); for (i = 0; i < 6; i++) - if (dvmaiobase <= ((1024 * 64 * 1024) << i)) + if (dvmaiobase <= ((1024L * 64 * 1024) << i)) break; - dvmaiobase = ((1024 * 64 * 1024) << i) - DVMAIO_SIZE; + dvmaiobase = ((1024L * 64 * 1024) << i) - DVMAIO_SIZE; dvmaiosz = i; } } __initfunc(void iommu_init(int iommu_node, struct linux_sbus *sbus)) { + extern int this_is_starfire; + extern void *starfire_hookup(int); struct iommu_struct *iommu; struct sysio_regs *sregs; struct linux_prom64_registers rprop; @@ -191,10 +198,6 @@ int err, i, j; dvmaio_init(); -#ifdef E3000_DEBUG - prom_printf("\niommu_init: [%x:%p] ", - iommu_node, sbus); -#endif err = prom_getproperty(iommu_node, "reg", (char *)&rprop, sizeof(rprop)); if(err == -1) { @@ -203,19 +206,16 @@ } sregs = (struct sysio_regs *) __va(rprop.phys_addr); -#ifdef E3000_DEBUG - prom_printf("sregs[%p]\n"); -#endif if(!sregs) { prom_printf("iommu_init: Fatal error, sysio regs not mapped\n"); prom_halt(); } iommu = kmalloc(sizeof(struct iommu_struct), GFP_ATOMIC); - -#ifdef E3000_DEBUG - prom_printf("iommu_init: iommu[%p] ", iommu); -#endif + if (!iommu) { + prom_printf("iommu_init: Fatal error, kmalloc(iommu) failed\n"); + prom_halt(); + } spin_lock_init(&iommu->iommu_lock); iommu->sysio_regs = sregs; @@ -224,14 +224,19 @@ control = sregs->iommu_control; impl = (control & IOMMU_CTRL_IMPL) >> 60; vers = (control & IOMMU_CTRL_VERS) >> 56; -#ifdef E3000_DEBUG - prom_printf("sreg_control[%08x]\n", control); - prom_printf("IOMMU: IMPL[%x] VERS[%x] SYSIO mapped at %016lx\n", - (unsigned int) impl, (unsigned int)vers, (unsigned long) sregs); -#endif printk("IOMMU(SBUS): IMPL[%x] VERS[%x] SYSIO mapped at %016lx\n", (unsigned int) impl, (unsigned int)vers, (unsigned long) sregs); + /* Streaming buffer is unreliable on VERS 0 of SYSIO, + * although such parts were never shipped in production + * Sun hardware, I check just to be robust. --DaveM + */ + vers = ((sregs->control & SYSIO_CONTROL_VER) >> 56); + if (vers == 0) + iommu->strbuf_enabled = 0; + else + iommu->strbuf_enabled = 1; + control &= ~(IOMMU_CTRL_TSBSZ); control |= ((IOMMU_TSBSZ_2K * dvmaiosz) | IOMMU_CTRL_TBWSZ | IOMMU_CTRL_ENAB); @@ -281,9 +286,14 @@ /* Setup aliased mappings... */ for(i = 0; i < (dvmaiobase >> 16); i++) { - *iopte = (IOPTE_VALID | IOPTE_64K | IOPTE_STBUF | - IOPTE_CACHE | IOPTE_WRITE); - *iopte |= (i << 16); + unsigned long val = ((((unsigned long)i) << 16UL) + phys_base); + + val |= IOPTE_VALID | IOPTE_64K | IOPTE_WRITE; + if (iommu->strbuf_enabled) + val |= IOPTE_STBUF; + else + val |= IOPTE_CACHE; + *iopte = val; iopte++; } @@ -291,38 +301,34 @@ for( ; i < ((dvmaiobase + DVMAIO_SIZE) >> 16); i++) *iopte++ = 0; -#ifdef E3000_DEBUG - prom_printf("IOMMU: pte's mapped, enabling IOMMU... "); -#endif sregs->iommu_tsbbase = __pa(tsbbase); sregs->iommu_control = control; -#ifdef E3000_DEBUG - prom_printf("done\n"); -#endif /* Get the streaming buffer going. */ control = sregs->sbuf_control; impl = (control & SYSIO_SBUFCTRL_IMPL) >> 60; vers = (control & SYSIO_SBUFCTRL_REV) >> 56; -#ifdef E3000_DEBUG - prom_printf("IOMMU: enabling streaming buffer, control[%08x]... ", - control); -#endif - printk("IOMMU: Streaming Buffer IMPL[%x] REV[%x] ", + printk("IOMMU: Streaming Buffer IMPL[%x] REV[%x] ... ", (unsigned int)impl, (unsigned int)vers); - iommu->sbuf_flushflag_va = kmalloc(sizeof(unsigned long), GFP_DMA); - printk("FlushFLAG[%016lx] ... ", (iommu->sbuf_flushflag_pa = __pa(iommu->sbuf_flushflag_va))); - *(iommu->sbuf_flushflag_va) = 0; - - sregs->sbuf_control = (control | SYSIO_SBUFCTRL_SB_EN); + iommu->flushflag = 0; -#ifdef E3000_DEBUG - prom_printf("done, returning\n"); -#endif - printk("ENABLED\n"); + if (iommu->strbuf_enabled != 0) { + sregs->sbuf_control = (control | SYSIO_SBUFCTRL_SB_EN); + printk("ENABLED\n"); + } else { + sregs->sbuf_control = (control & ~(SYSIO_SBUFCTRL_SB_EN)); + printk("DISABLED\n"); + } /* Finally enable DVMA arbitration for all devices, just in case. */ sregs->sbus_control |= SYSIO_SBCNTRL_AEN; + + /* If necessary, hook us up for starfire IRQ translations. */ + sbus->upaid = prom_getintdefault(sbus->prom_node, "upa-portid", -1); + if(this_is_starfire) + sbus->starfire_cookie = starfire_hookup(sbus->upaid); + else + sbus->starfire_cookie = NULL; } void mmu_map_dma_area(unsigned long addr, int len, __u32 *dvma_addr, @@ -397,19 +403,35 @@ __u32 mmu_get_scsi_one(char *vaddr, unsigned long len, struct linux_sbus *sbus) { - __u32 sbus_addr = (__u32) __pa(vaddr); + struct iommu_struct *iommu = sbus->iommu; + struct sysio_regs *sregs = iommu->sysio_regs; + unsigned long start = (unsigned long) vaddr; + unsigned long end = PAGE_ALIGN(start + len); + unsigned long flags, tmp; + volatile u64 *sbctrl = (volatile u64 *) &sregs->sbus_control; -#ifndef DEBUG_IOMMU - return sbus_addr; -#else - if((sbus_addr < dvmaiobase) && - ((sbus_addr + len) < dvmaiobase)) - return sbus_addr; - - /* "can't happen"... GFP_DMA assures this. */ - panic("Very high scsi_one mappings should never happen."); - return (__u32)0; -#endif + start &= PAGE_MASK; + if (end > MAX_DMA_ADDRESS) { + printk("mmu_get_scsi_one: Bogus DMA buffer address [%016lx:%d]\n", + (unsigned long) vaddr, (int)len); + panic("DMA address too large, tell DaveM"); + } + + if (iommu->strbuf_enabled) { + spin_lock_irqsave(&iommu->iommu_lock, flags); + iommu->flushflag = 0; + while(start < end) { + sregs->sbuf_pflush = start; + start += PAGE_SIZE; + } + sregs->sbuf_fsync = __pa(&(iommu->flushflag)); + tmp = *sbctrl; + while(iommu->flushflag == 0) + membar("#LoadLoad"); + spin_unlock_irqrestore(&iommu->iommu_lock, flags); + } + + return sbus_dvma_addr(vaddr); } void mmu_release_scsi_one(u32 vaddr, unsigned long len, struct linux_sbus *sbus) @@ -418,43 +440,89 @@ struct sysio_regs *sregs = iommu->sysio_regs; unsigned long start = (unsigned long) vaddr; unsigned long end = PAGE_ALIGN(start + len); - unsigned long flags; - unsigned int *sync_word; + unsigned long flags, tmp; + volatile u64 *sbctrl = (volatile u64 *) &sregs->sbus_control; start &= PAGE_MASK; - spin_lock_irqsave(&iommu->iommu_lock, flags); + if (iommu->strbuf_enabled) { + spin_lock_irqsave(&iommu->iommu_lock, flags); - while(start < end) { - sregs->sbuf_pflush = start; - start += PAGE_SIZE; - } - sync_word = iommu->sbuf_flushflag_va; - sregs->sbuf_fsync = iommu->sbuf_flushflag_pa; - membar("#StoreLoad | #MemIssue"); - while((*sync_word & 0x1) == 0) - membar("#LoadLoad"); - *sync_word = 0; + /* 1) Clear the flush flag word */ + iommu->flushflag = 0; - spin_unlock_irqrestore(&iommu->iommu_lock, flags); + /* 2) Tell the streaming buffer which entries + * we want flushed. + */ + while(start < end) { + sregs->sbuf_pflush = start; + start += PAGE_SIZE; + } + + /* 3) Initiate flush sequence. */ + sregs->sbuf_fsync = __pa(&(iommu->flushflag)); + + /* 4) Guarentee completion of all previous writes + * by reading SYSIO's SBUS control register. + */ + tmp = *sbctrl; + + /* 5) Wait for flush flag to get set. */ + while(iommu->flushflag == 0) + membar("#LoadLoad"); + + spin_unlock_irqrestore(&iommu->iommu_lock, flags); + } } void mmu_get_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus) { - while(sz >= 0) { - __u32 page = (__u32) __pa(((unsigned long) sg[sz].addr)); -#ifndef DEBUG_IOMMU - sg[sz].dvma_addr = page; -#else - if((page < dvmaiobase) && - (page + sg[sz].len) < dvmaiobase) { - sg[sz].dvma_addr = page; - } else { - /* "can't happen"... GFP_DMA assures this. */ - panic("scsi_sgl high mappings should never happen."); + struct iommu_struct *iommu = sbus->iommu; + struct sysio_regs *sregs = iommu->sysio_regs; + unsigned long flags, tmp; + volatile u64 *sbctrl = (volatile u64 *) &sregs->sbus_control; + + if (iommu->strbuf_enabled) { + spin_lock_irqsave(&iommu->iommu_lock, flags); + iommu->flushflag = 0; + + while(sz >= 0) { + unsigned long start = (unsigned long)sg[sz].addr; + unsigned long end = PAGE_ALIGN(start + sg[sz].len); + + if (end > MAX_DMA_ADDRESS) { + printk("mmu_get_scsi_sgl: Bogus DMA buffer address " + "[%016lx:%d]\n", start, (int) sg[sz].len); + panic("DMA address too large, tell DaveM"); + } + + sg[sz--].dvma_addr = sbus_dvma_addr(start); + start &= PAGE_MASK; + while(start < end) { + sregs->sbuf_pflush = start; + start += PAGE_SIZE; + } + } + + sregs->sbuf_fsync = __pa(&(iommu->flushflag)); + tmp = *sbctrl; + while(iommu->flushflag == 0) + membar("#LoadLoad"); + spin_unlock_irqrestore(&iommu->iommu_lock, flags); + } else { + /* Just verify the addresses and fill in the + * dvma_addr fields in this case. + */ + while(sz >= 0) { + unsigned long start = (unsigned long)sg[sz].addr; + unsigned long end = PAGE_ALIGN(start + sg[sz].len); + if (end > MAX_DMA_ADDRESS) { + printk("mmu_get_scsi_sgl: Bogus DMA buffer address " + "[%016lx:%d]\n", start, (int) sg[sz].len); + panic("DMA address too large, tell DaveM"); + } + sg[sz--].dvma_addr = sbus_dvma_addr(start); } -#endif - sz--; } } @@ -462,30 +530,98 @@ { struct iommu_struct *iommu = sbus->iommu; struct sysio_regs *sregs = iommu->sysio_regs; - unsigned long flags; - unsigned int *sync_word; + volatile u64 *sbctrl = (volatile u64 *) &sregs->sbus_control; + unsigned long flags, tmp; - spin_lock_irqsave(&iommu->iommu_lock, flags); + if (iommu->strbuf_enabled) { + spin_lock_irqsave(&iommu->iommu_lock, flags); - while(sz >= 0) { - unsigned long start = sg[sz].dvma_addr; - unsigned long end = PAGE_ALIGN(start + sg[sz].len); + /* 1) Clear the flush flag word */ + iommu->flushflag = 0; - start &= PAGE_MASK; - while(start < end) { - sregs->sbuf_pflush = start; - start += PAGE_SIZE; + /* 2) Tell the streaming buffer which entries + * we want flushed. + */ + while(sz >= 0) { + unsigned long start = sg[sz].dvma_addr; + unsigned long end = PAGE_ALIGN(start + sg[sz].len); + + start &= PAGE_MASK; + while(start < end) { + sregs->sbuf_pflush = start; + start += PAGE_SIZE; + } + sz--; } - sz--; + + /* 3) Initiate flush sequence. */ + sregs->sbuf_fsync = __pa(&(iommu->flushflag)); + + /* 4) Guarentee completion of previous writes + * by reading SYSIO's SBUS control register. + */ + tmp = *sbctrl; + + /* 5) Wait for flush flag to get set. */ + while(iommu->flushflag == 0) + membar("#LoadLoad"); + + spin_unlock_irqrestore(&iommu->iommu_lock, flags); } - sync_word = iommu->sbuf_flushflag_va; - sregs->sbuf_fsync = iommu->sbuf_flushflag_pa; - membar("#StoreLoad | #MemIssue"); - while((*sync_word & 0x1) == 0) - membar("#LoadLoad"); - *sync_word = 0; +} + +void mmu_set_sbus64(struct linux_sbus_device *sdev, int bursts) +{ + struct linux_sbus *sbus = sdev->my_bus; + struct sysio_regs *sregs = sbus->iommu->sysio_regs; + int slot = sdev->slot; + u64 *cfg, tmp; + + switch(slot) { + case 0: + cfg = &sregs->sbus_s0cfg; + break; + case 1: + cfg = &sregs->sbus_s1cfg; + break; + case 2: + cfg = &sregs->sbus_s2cfg; + break; + case 3: + cfg = &sregs->sbus_s3cfg; + break; + + case 13: + cfg = &sregs->sbus_s4cfg; + break; + case 14: + cfg = &sregs->sbus_s5cfg; + break; + case 15: + cfg = &sregs->sbus_s6cfg; + break; + + default: + return; + }; + + /* ETM already enabled? If so, we're done. */ + tmp = *cfg; + if ((tmp & SYSIO_SBSCFG_ETM) != 0) + return; - spin_unlock_irqrestore(&iommu->iommu_lock, flags); + /* Set burst bits. */ + if (bursts & DMA_BURST8) + tmp |= SYSIO_SBSCFG_BA8; + if (bursts & DMA_BURST16) + tmp |= SYSIO_SBSCFG_BA16; + if (bursts & DMA_BURST32) + tmp |= SYSIO_SBSCFG_BA32; + if (bursts & DMA_BURST64) + tmp |= SYSIO_SBSCFG_BA64; + + /* Finally turn on ETM and set register. */ + *cfg = (tmp | SYSIO_SBSCFG_ETM); } int mmu_info(char *buf) @@ -562,16 +698,9 @@ */ static void __flush_nucleus_vptes(void) { - unsigned long pstate; unsigned long prom_reserved_base = 0xfffffffc00000000UL; int i; - __asm__ __volatile__("flushw\n\t" - "rdpr %%pstate, %0\n\t" - "wrpr %0, %1, %%pstate" - : "=r" (pstate) - : "i" (PSTATE_IE)); - /* Only DTLB must be checked for VPTE entries. */ for(i = 0; i < 63; i++) { unsigned long tag = spitfire_get_dtlb_tag(i); @@ -586,8 +715,6 @@ membar("#Sync"); } } - __asm__ __volatile__("wrpr %0, 0, %%pstate" - : : "r" (pstate)); } static int prom_ditlb_set = 0; @@ -600,10 +727,19 @@ void prom_world(int enter) { + unsigned long pstate; int i; if (!prom_ditlb_set) return; + + /* Make sure the following runs atomically. */ + __asm__ __volatile__("flushw\n\t" + "rdpr %%pstate, %0\n\t" + "wrpr %0, %1, %%pstate" + : "=r" (pstate) + : "i" (PSTATE_IE)); + if (enter) { /* Kick out nucleus VPTEs. */ __flush_nucleus_vptes(); @@ -648,6 +784,8 @@ } } } + __asm__ __volatile__("wrpr %0, 0, %%pstate" + : : "r" (pstate)); } void inherit_locked_prom_mappings(int save_p) @@ -680,7 +818,7 @@ unsigned long data; data = spitfire_get_dtlb_data(i); - if(data & _PAGE_L) { + if((data & (_PAGE_L|_PAGE_VALID)) == (_PAGE_L|_PAGE_VALID)) { unsigned long tag = spitfire_get_dtlb_tag(i); if(save_p) { @@ -703,7 +841,7 @@ unsigned long data; data = spitfire_get_itlb_data(i); - if(data & _PAGE_L) { + if((data & (_PAGE_L|_PAGE_VALID)) == (_PAGE_L|_PAGE_VALID)) { unsigned long tag = spitfire_get_itlb_tag(i); if(save_p) { @@ -867,15 +1005,11 @@ struct pgtable_cache_struct pgt_quicklists; #endif -/* XXX Add __GFP_HIGH to these calls to "fool" page allocator - * XXX so we don't go to swap so quickly... then do the same - * XXX for get_user_page as well -DaveM - */ pmd_t *get_pmd_slow(pgd_t *pgd, unsigned long offset) { pmd_t *pmd; - pmd = (pmd_t *) __get_free_page(GFP_DMA|GFP_KERNEL); + pmd = (pmd_t *) __get_free_page(GFP_KERNEL); if(pmd) { memset(pmd, 0, PAGE_SIZE); pgd_set(pgd, pmd); @@ -888,7 +1022,7 @@ { pte_t *pte; - pte = (pte_t *) __get_free_page(GFP_DMA|GFP_KERNEL); + pte = (pte_t *) __get_free_page(GFP_KERNEL); if(pte) { memset(pte, 0, PAGE_SIZE); pmd_set(pmd, pte); @@ -997,11 +1131,11 @@ __initfunc(unsigned long paging_init(unsigned long start_mem, unsigned long end_mem)) { - extern void setup_tba(void); extern pmd_t swapper_pmd_dir[1024]; - extern unsigned long irq_init(unsigned long start_mem, unsigned long end_mem); - extern unsigned int sparc64_vpte_patchme[1]; + extern unsigned int sparc64_vpte_patchme1[1]; + extern unsigned int sparc64_vpte_patchme2[1]; unsigned long alias_base = phys_base + PAGE_OFFSET; + unsigned long second_alias_page = 0; unsigned long pt; unsigned long flags; unsigned long shift = alias_base - ((unsigned long)&empty_zero_page); @@ -1026,6 +1160,21 @@ : "r" (TLB_TAG_ACCESS), "r" (alias_base), "r" (pt), "i" (ASI_DMMU), "i" (ASI_DTLB_DATA_ACCESS), "r" (61 << 3) : "memory"); + if (start_mem >= KERNBASE + 0x340000) { + second_alias_page = alias_base + 0x400000; + __asm__ __volatile__(" + stxa %1, [%0] %3 + stxa %2, [%5] %4 + membar #Sync + flush %%g6 + nop + nop + nop" + : /* No outputs */ + : "r" (TLB_TAG_ACCESS), "r" (second_alias_page), "r" (pt + 0x400000), + "i" (ASI_DMMU), "i" (ASI_DTLB_DATA_ACCESS), "r" (60 << 3) + : "memory"); + } __restore_flags(flags); /* Now set kernel pgd to upper alias so physical page computations @@ -1038,10 +1187,10 @@ /* Now can init the kernel/bad page tables. */ pgd_set(&swapper_pg_dir[0], swapper_pmd_dir + (shift / sizeof(pgd_t))); - sparc64_vpte_patchme[0] |= (init_mm.pgd[0] >> 10); + sparc64_vpte_patchme1[0] |= (init_mm.pgd[0] >> 10); + sparc64_vpte_patchme2[0] |= (init_mm.pgd[0] & 0x3ff); + flushi((long)&sparc64_vpte_patchme1[0]); - start_mem = irq_init(start_mem, end_mem); - /* We use mempool to create page tables, therefore adjust it up * such that __pa() macros etc. work. */ @@ -1051,9 +1200,20 @@ allocate_ptable_skeleton(DVMA_VADDR, DVMA_VADDR + 0x4000000); inherit_prom_mappings(); - - /* Ok, we can use our TLB miss and window trap handlers safely. */ - setup_tba(); + /* Ok, we can use our TLB miss and window trap handlers safely. + * We need to do a quick peek here to see if we are on StarFire + * or not, so setup_tba can setup the IRQ globals correctly (it + * needs to get the hard smp processor id correctly). + */ + { + extern void setup_tba(int); + int is_starfire = prom_finddevice("/ssp-serial"); + if(is_starfire != 0 && is_starfire != -1) + is_starfire = 1; + else + is_starfire = 0; + setup_tba(is_starfire); + } /* Really paranoid. */ flushi((long)&empty_zero_page); @@ -1064,6 +1224,8 @@ */ /* We only created DTLB mapping of this stuff. */ spitfire_flush_dtlb_nucleus_page(alias_base); + if (second_alias_page) + spitfire_flush_dtlb_nucleus_page(second_alias_page); membar("#Sync"); /* Paranoid */ @@ -1079,10 +1241,6 @@ return device_scan (PAGE_ALIGN (start_mem)); } -/* XXX Add also PG_Hole flag, set it in the page structs here, - * XXX remove FREE_UNUSED_MEM_MAP code, and the nfsd file handle - * problems will all be gone. -DaveM - */ __initfunc(static void taint_real_pages(unsigned long start_mem, unsigned long end_mem)) { unsigned long tmp = 0, paddr, endaddr; @@ -1096,14 +1254,24 @@ if (!sp_banks[tmp].num_bytes) { mem_map[paddr>>PAGE_SHIFT].flags |= (1<>PAGE_SHIFT].next_hash = mem_map + (phys_base >> PAGE_SHIFT); + mem_map[(paddr>>PAGE_SHIFT)+1UL].flags |= (1<>PAGE_SHIFT)+1UL].next_hash = mem_map + (phys_base >> PAGE_SHIFT); return; } if (sp_banks[tmp].base_addr > paddr) { - /* Making a one or two pages PG_skip holes is not necessary */ - if (sp_banks[tmp].base_addr - paddr > 2 * PAGE_SIZE) { + /* Making a one or two pages PG_skip holes + * is not necessary. We add one more because + * we must set the PG_skip flag on the first + * two mem_map[] entries for the hole. Go and + * see the mm/filemap.c:shrink_mmap() loop for + * details. -DaveM + */ + if (sp_banks[tmp].base_addr - paddr > 3 * PAGE_SIZE) { mem_map[paddr>>PAGE_SHIFT].flags |= (1<>PAGE_SHIFT].next_hash = mem_map + (sp_banks[tmp].base_addr >> PAGE_SHIFT); + mem_map[(paddr>>PAGE_SHIFT)+1UL].flags |= (1<>PAGE_SHIFT)+1UL].next_hash = mem_map + (sp_banks[tmp].base_addr >> PAGE_SHIFT); } paddr = sp_banks[tmp].base_addr; } @@ -1111,7 +1279,8 @@ endaddr = sp_banks[tmp].base_addr + sp_banks[tmp].num_bytes; while (paddr < endaddr) { mem_map[paddr>>PAGE_SHIFT].flags &= ~(1<= dvmaiobase) + set_bit(paddr >> 22, sparc64_valid_addr_bitmap); + if (paddr >= (MAX_DMA_ADDRESS - PAGE_OFFSET)) mem_map[paddr>>PAGE_SHIFT].flags &= ~(1<> ((22 - PAGE_SHIFT) + 6); + i += 1; + memset(sparc64_valid_addr_bitmap, 0, i << 3); + start_mem += i << 3; start_mem = PAGE_ALIGN(start_mem); num_physpages = 0; @@ -1138,6 +1313,8 @@ if (phys_base) { mem_map[0].flags |= (1<> PAGE_SHIFT); + mem_map[1].flags |= (1<> PAGE_SHIFT); } addr = PAGE_OFFSET + phys_base; @@ -1148,6 +1325,7 @@ else #endif mem_map[MAP_NR(addr)].flags |= (1<> 22, sparc64_valid_addr_bitmap); addr += PAGE_SIZE; } @@ -1159,6 +1337,9 @@ if (PageSkip(page)) { unsigned long low, high; + /* See taint_real_pages() for why this is done. -DaveM */ + page++; + low = PAGE_ALIGN((unsigned long)(page+1)); if (page->next_hash < page) high = ((unsigned long)end) & PAGE_MASK; @@ -1255,7 +1436,7 @@ val->totalram = 0; val->sharedram = 0; - val->freeram = nr_free_pages << PAGE_SHIFT; + val->freeram = ((unsigned long)nr_free_pages) << PAGE_SHIFT; val->bufferram = buffermem; for (page = mem_map, end = mem_map + max_mapnr; page < end; page++) { diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/prom/memory.c linux/arch/sparc64/prom/memory.c --- v2.2.3/linux/arch/sparc64/prom/memory.c Mon Mar 17 14:54:24 1997 +++ linux/arch/sparc64/prom/memory.c Wed Mar 10 16:53:37 1999 @@ -1,4 +1,4 @@ -/* $Id: memory.c,v 1.3 1997/03/04 16:27:10 jj Exp $ +/* $Id: memory.c,v 1.4 1998/11/25 10:04:06 jj Exp $ * memory.c: Prom routine for acquiring various bits of information * about RAM on the machine, both virtual and physical. * @@ -41,8 +41,8 @@ prom_sortmemlist(struct linux_mlist_p1275 *thislist)) { int swapi = 0; - int i, mitr, tmpsize; - unsigned long tmpaddr; + int i, mitr; + unsigned long tmpaddr, tmpsize; unsigned long lowest; for(i=0; thislist[i].theres_more != 0; i++) { @@ -79,7 +79,7 @@ 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_reg_memlist[iter].reg_size; prom_phys_avail[iter].theres_more = &prom_phys_avail[iter+1]; } @@ -93,7 +93,7 @@ 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_reg_memlist[iter].reg_size; prom_phys_total[iter].theres_more = &prom_phys_total[iter+1]; } @@ -112,7 +112,7 @@ 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_reg_memlist[iter].reg_size; prom_prom_taken[iter].theres_more = &prom_phys_total[iter+1]; } @@ -130,7 +130,7 @@ prom_prom_taken[iter].start_adr; } prom_prom_taken[iter-1].num_bytes = - ((unsigned long)-1) - (unsigned long) prom_prom_taken[iter-1].start_adr; + -1UL - prom_prom_taken[iter-1].start_adr; /* Sort the other two lists. */ prom_sortmemlist(prom_phys_total); diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/prom/misc.c linux/arch/sparc64/prom/misc.c --- v2.2.3/linux/arch/sparc64/prom/misc.c Sun Nov 8 14:02:50 1998 +++ linux/arch/sparc64/prom/misc.c Wed Mar 10 16:53:37 1999 @@ -1,4 +1,4 @@ -/* $Id: misc.c,v 1.13 1998/10/13 14:03:49 davem Exp $ +/* $Id: misc.c,v 1.14 1998/12/18 10:01:59 davem Exp $ * misc.c: Miscellaneous prom functions that don't belong * anywhere else. * @@ -122,9 +122,123 @@ p1275_cmd("SUNW,set-trap-table", P1275_INOUT(1, 0), tba); } +/* This is only used internally below. */ +static int prom_get_mmu_ihandle(void) +{ + int node; + int ret; + + node = prom_finddevice("/chosen"); + ret = prom_getint(node, "mmu"); + if(ret == -1 || ret == 0) { + prom_printf("PROMLIB: Fatal error, cannot get mmu ihandle.\n"); + prom_halt(); + } + return ret; +} + +/* Load explicit I/D TLB entries. */ +long prom_itlb_load(unsigned long index, + unsigned long tte_data, + unsigned long vaddr) +{ + return p1275_cmd("call-method", + (P1275_ARG(0, P1275_ARG_IN_BUF) | P1275_INOUT(5, 1)), + "SUNW,itlb-load", + prom_get_mmu_ihandle(), + /* And then our actual args are pushed backwards. */ + vaddr, + tte_data, + index); +} + +long prom_dtlb_load(unsigned long index, + unsigned long tte_data, + unsigned long vaddr) +{ + return p1275_cmd("call-method", + (P1275_ARG(0, P1275_ARG_IN_BUF) | P1275_INOUT(5, 1)), + "SUNW,dtlb-load", + prom_get_mmu_ihandle(), + /* And then our actual args are pushed backwards. */ + vaddr, + tte_data, + index); +} + +/* Set aside physical memory which is not touched or modified + * across soft resets. + */ +unsigned long prom_retain(char *name, + unsigned long pa_low, unsigned long pa_high, + long size, long align) +{ + /* XXX I don't think we return multiple values correctly. + * XXX OBP supposedly returns pa_low/pa_high here, how does + * XXX it work? + */ + + /* If align is zero, the pa_low/pa_high args are passed, + * else they are not. + */ + if(align == 0) + return p1275_cmd("SUNW,retain", + (P1275_ARG(0, P1275_ARG_IN_BUF) | P1275_INOUT(5, 2)), + name, pa_low, pa_high, size, align); + else + return p1275_cmd("SUNW,retain", + (P1275_ARG(0, P1275_ARG_IN_BUF) | P1275_INOUT(3, 2)), + name, size, align); +} + +/* Get "Unumber" string for the SIMM at the given + * memory address. Usually this will be of the form + * "Uxxxx" where xxxx is a decimal number which is + * etched into the motherboard next to the SIMM slot + * in question. + */ +int prom_getunumber(unsigned long phys_lo, unsigned long phys_hi, + char *buf, int buflen) +{ + return p1275_cmd("SUNW,get-unumber", + (P1275_ARG(2, P1275_ARG_OUT_BUF) | P1275_INOUT(4, 1)), + phys_lo, phys_hi, buf, buflen); +} + +/* Power management extensions. */ +void prom_sleepself(void) +{ + p1275_cmd("SUNW,sleep-self", P1275_INOUT(0, 0)); +} + +int prom_sleepsystem(void) +{ + return p1275_cmd("SUNW,sleep-system", P1275_INOUT(0, 1)); +} + +int prom_wakeupsystem(void) +{ + return p1275_cmd("SUNW,wakeup-system", P1275_INOUT(0, 1)); +} + #ifdef __SMP__ void prom_startcpu(int cpunode, unsigned long pc, unsigned long o0) { p1275_cmd("SUNW,start-cpu", P1275_INOUT(3, 0), cpunode, pc, o0); +} + +void prom_stopself(void) +{ + p1275_cmd("SUNW,stop-self", P1275_INOUT(0, 0)); +} + +void prom_idleself(void) +{ + p1275_cmd("SUNW,idle-self", P1275_INOUT(0, 0)); +} + +void prom_resumecpu(int cpunode) +{ + p1275_cmd("SUNW,resume-cpu", P1275_INOUT(1, 0), cpunode); } #endif diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/solaris/fs.c linux/arch/sparc64/solaris/fs.c --- v2.2.3/linux/arch/sparc64/solaris/fs.c Thu Nov 19 09:56:27 1998 +++ linux/arch/sparc64/solaris/fs.c Wed Mar 10 16:53:37 1999 @@ -1,4 +1,4 @@ -/* $Id: fs.c,v 1.11 1998/10/28 08:12:04 jj Exp $ +/* $Id: fs.c,v 1.12 1999/01/02 16:46:06 davem Exp $ * fs.c: fs related syscall emulation for Solaris * * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -21,7 +22,6 @@ #include "conv.h" extern char * getname32(u32 filename); -#define putname32 putname #define R4_DEV(DEV) ((DEV & 0xff) | ((DEV & 0xff00) << 10)) #define R4_MAJOR(DEV) (((DEV) >> 18) & 0x3fff) @@ -138,7 +138,7 @@ set_fs (KERNEL_DS); ret = sys_newstat(filenam, &s); set_fs (old_fs); - putname32 (filenam); + putname (filenam); if (putstat ((struct sol_stat *)A(statbuf), &s)) return -EFAULT; } @@ -166,7 +166,7 @@ set_fs (KERNEL_DS); ret = sys_newstat(filenam, &s); set_fs (old_fs); - putname32 (filenam); + putname (filenam); if (putstat64 ((struct sol_stat64 *)A(statbuf), &s)) return -EFAULT; } @@ -188,7 +188,7 @@ set_fs (KERNEL_DS); ret = sys_newlstat(filenam, &s); set_fs (old_fs); - putname32 (filenam); + putname (filenam); if (putstat ((struct sol_stat *)A(statbuf), &s)) return -EFAULT; } @@ -215,7 +215,7 @@ set_fs (KERNEL_DS); ret = sys_newlstat(filenam, &s); set_fs (old_fs); - putname32 (filenam); + putname (filenam); if (putstat64 ((struct sol_stat64 *)A(statbuf), &s)) return -EFAULT; } diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/solaris/systbl.S linux/arch/sparc64/solaris/systbl.S --- v2.2.3/linux/arch/sparc64/solaris/systbl.S Thu Nov 19 09:56:28 1998 +++ linux/arch/sparc64/solaris/systbl.S Wed Mar 10 16:53:37 1999 @@ -1,4 +1,4 @@ -/* $Id: systbl.S,v 1.7 1998/10/28 08:11:49 jj Exp $ +/* $Id: systbl.S,v 1.8 1999/02/11 18:34:02 davem Exp $ * systbl.S: System call entry point table for Solaris compatibility. * * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -72,7 +72,7 @@ .word CHAIN(dup) /* dup d 41 */ .word CHAIN(pipe) /* pipe 42 */ .word CHAIN(times) /* times p 43 */ - .word CHAIN(profil) /* prof xxxx 44 */ + .word 44 /*CHAIN(profil)*/ /* prof xxxx 44 */ .word solaris_unimplemented /* lock/plock 45 */ .word CHAIN(setgid) /* setgid d 46 */ .word solaris_getgid /* getgid 47 */ diff -u --recursive --new-file v2.2.3/linux/drivers/ap1000/apfddi.c linux/drivers/ap1000/apfddi.c --- v2.2.3/linux/drivers/ap1000/apfddi.c Fri May 8 23:14:47 1998 +++ linux/drivers/ap1000/apfddi.c Wed Mar 10 16:51:35 1999 @@ -30,7 +30,6 @@ #include #include -#include #include #include #include diff -u --recursive --new-file v2.2.3/linux/drivers/ap1000/ringbuf.c linux/drivers/ap1000/ringbuf.c --- v2.2.3/linux/drivers/ap1000/ringbuf.c Wed Aug 26 11:37:34 1998 +++ linux/drivers/ap1000/ringbuf.c Wed Mar 10 16:51:35 1999 @@ -22,7 +22,6 @@ #include #include #include -#include #include #include diff -u --recursive --new-file v2.2.3/linux/drivers/block/cmd646.c linux/drivers/block/cmd646.c --- v2.2.3/linux/drivers/block/cmd646.c Thu Aug 6 14:06:31 1998 +++ linux/drivers/block/cmd646.c Mon Mar 15 16:11:29 1999 @@ -1,4 +1,4 @@ -/* $Id: cmd646.c,v 1.10 1998/08/03 15:28:42 davem Exp $ +/* $Id: cmd646.c,v 1.11 1998/12/13 08:36:54 davem Exp $ * cmd646.c: Enable interrupts at initialization time on Ultra/PCI machines. * Note, this driver is not used at all on other systems because * there the "BIOS" has done all of the following already. @@ -219,8 +219,11 @@ hwif->chipset = ide_cmd646; - /* Set a good latency timer value. */ - (void) pci_write_config_byte(dev, PCI_LATENCY_TIMER, 240); + /* Set a good latency timer and cache line size value. */ + (void) pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64); +#ifdef __sparc_v9__ + (void) pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 0x10); +#endif /* Setup interrupts. */ (void) pci_read_config_byte(dev, 0x71, &mrdmode); diff -u --recursive --new-file v2.2.3/linux/drivers/block/genhd.c linux/drivers/block/genhd.c --- v2.2.3/linux/drivers/block/genhd.c Wed Mar 10 15:29:45 1999 +++ linux/drivers/block/genhd.c Sun Mar 21 07:11:36 1999 @@ -867,19 +867,20 @@ int nr_sects; int blk; int part, res; + int old_blocksize; + int blocksize; - /* - * Don't bother touching M/O 2K media. - */ - - if (get_ptable_blocksize(dev) != 1024) - return 0; - - set_blocksize(dev,512); + old_blocksize = get_ptable_blocksize(dev); + if (hardsect_size[MAJOR(dev)] != NULL) + blocksize = hardsect_size[MAJOR(dev)][MINOR(dev)]; + else + blocksize = 512; + + set_blocksize(dev,blocksize); res = 0; for (blk = 0; blk < RDB_ALLOCATION_LIMIT; blk++) { - if(!(bh = bread(dev,blk,512))) { + if(!(bh = bread(dev,blk,blocksize))) { printk("Dev %s: unable to read RDB block %d\n", kdevname(dev),blk); goto rdb_done; @@ -887,16 +888,25 @@ if (*(u32 *)bh->b_data == htonl(IDNAME_RIGIDDISK)) { rdb = (struct RigidDiskBlock *)bh->b_data; if (checksum_block((u32 *)bh->b_data,htonl(rdb->rdb_SummedLongs) & 0x7F)) { - printk("Dev %s: RDB in block %d has bad checksum\n", - kdevname(dev),blk); - brelse(bh); - continue; + /* Try again with 0xdc..0xdf zeroed, Windows might have + * trashed it. + */ + *(u32 *)(&bh->b_data[0xdc]) = 0; + if (checksum_block((u32 *)bh->b_data, + htonl(rdb->rdb_SummedLongs) & 0x7F)) { + brelse(bh); + printk("Dev %s: RDB in block %d has bad checksum\n", + kdevname(dev),blk); + continue; + } + printk("Warning: Trashed word at 0xd0 in block %d " + "ignored in checksum calculation\n",kdevname(dev),blk); } printk(" RDSK"); blk = htonl(rdb->rdb_PartitionList); brelse(bh); for (part = 1; blk > 0 && part <= 16; part++) { - if (!(bh = bread(dev,blk, 512))) { + if (!(bh = bread(dev,blk,blocksize))) { printk("Dev %s: unable to read partition block %d\n", kdevname(dev),blk); goto rdb_done; @@ -929,11 +939,7 @@ } rdb_done: - /* - * FIXME: should restore the original size. Then we could clean - * up the M/O skip. Amiga people ? - */ - set_blocksize(dev,BLOCK_SIZE); + set_blocksize(dev,old_blocksize); return res; } #endif /* CONFIG_AMIGA_PARTITION */ diff -u --recursive --new-file v2.2.3/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c --- v2.2.3/linux/drivers/block/ide-cd.c Wed Jan 20 23:14:04 1999 +++ linux/drivers/block/ide-cd.c Wed Mar 10 17:01:57 1999 @@ -33,6 +33,7 @@ * boot * -Integrate DVD-ROM support in driver. Thanks to Merete Gotsæd-Petersen * of Pioneer Denmark for providing me with a drive for testing. + * -Implement Features and Profiles. * * * ---------------------------------- @@ -230,9 +231,23 @@ * 4.52 Jan 19, 1999 -- Jens Axboe * - Detect DVD-ROM/RAM drives * + * 4.53 Feb 22, 1999 - Include other model Samsung and one Goldstar + * drive in transfer size limit. + * - Fix the I/O error when doing eject without a medium + * loaded on some drives. + * - CDROMREADMODE2 is now implemented through + * CDROMREADRAW, since many drives don't support + * MODE2 (even though ATAPI 2.6 says they must). + * - Added ignore parameter to ide-cd (as a module), eg + * insmod ide-cd ignore='hda hdb' + * Useful when using ide-cd in conjunction with + * ide-scsi. TODO: non-modular way of doing the + * same. + * + * *************************************************************************/ -#define IDECD_VERSION "4.52" +#define IDECD_VERSION "4.53" #include #include @@ -252,7 +267,6 @@ #include "ide.h" #include "ide-cd.h" - /**************************************************************************** * Generic packet command support and error handling routines. */ @@ -1527,6 +1541,10 @@ CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1; stat = 0; } + + /* no medium, that's alright. */ + if (stat != 0 && reqbuf->sense_key == NOT_READY && reqbuf->asc == 0x3a) + stat = 0; if (stat == 0) CDROM_STATE_FLAGS (drive)->door_locked = lockflag; @@ -1806,7 +1824,6 @@ return cdrom_queue_packet_command (drive, &pc); } - /* ATAPI cdrom drives are free to select the speed you request or any slower rate :-( Requesting too fast a speed will _not_ produce an error. */ static int @@ -2092,22 +2109,17 @@ if (cmd == CDROMREADMODE1) { blocksize = CD_FRAMESIZE; format = 2; - } else if (cmd == CDROMREADMODE2) { - blocksize = CD_FRAMESIZE_RAW0; - format = 3; - } else { + } else { /* for RAW and MODE2. */ blocksize = CD_FRAMESIZE_RAW; format = 0; } - stat = verify_area (VERIFY_WRITE, (char *)arg, blocksize); - if (stat) return stat; + + copy_from_user_ret(&msf, (void *)arg, sizeof (msf), -EFAULT); - copy_from_user (&msf, (void *)arg, sizeof (msf)); + lba = msf_to_lba(msf.cdmsf_min0, + msf.cdmsf_sec0, + msf.cdmsf_frame0); - lba = msf_to_lba (msf.cdmsf_min0, - msf.cdmsf_sec0, - msf.cdmsf_frame0); - /* Make sure the TOC is up to date. */ stat = cdrom_read_toc (drive, NULL); if (stat) return stat; @@ -2117,14 +2129,21 @@ if (lba < 0 || lba >= toc->capacity) return -EINVAL; - buf = (char *) kmalloc (CD_FRAMESIZE_RAW, GFP_KERNEL); + buf = (char *) kmalloc (blocksize, GFP_KERNEL); if (buf == NULL) return -ENOMEM; stat = cdrom_read_block (drive, format, lba, 1, buf, blocksize, NULL); - if (stat == 0) - copy_to_user ((char *)arg, buf, blocksize); + + if (stat == 0) { + if (cmd == CDROMREADMODE2) { + /* For Mode2, skip the Sync, Header, and Subheader */ + copy_to_user_ret((char *)arg, buf+16, CD_FRAMESIZE_RAW0, -EFAULT); + } else { + copy_to_user_ret((char *)arg, buf, blocksize, -EFAULT); + } + } kfree (buf); return stat; @@ -2486,14 +2505,12 @@ static int ide_cdrom_reset (struct cdrom_device_info *cdi) { - ide_drive_t *drive = (ide_drive_t*) cdi->handle; struct request req; ide_init_drive_cmd (&req); req.cmd = RESET_DRIVE_COMMAND; return ide_do_drive_cmd (drive, &req, ide_wait); - } @@ -2501,9 +2518,10 @@ int ide_cdrom_tray_move (struct cdrom_device_info *cdi, int position) { ide_drive_t *drive = (ide_drive_t*) cdi->handle; + struct atapi_request_sense rq; if (position) { - int stat = cdrom_lockdoor (drive, 0, NULL); + int stat = cdrom_lockdoor (drive, 0, &rq); if (stat) return stat; } @@ -2980,12 +2998,17 @@ CDROM_CONFIG_FLAGS (drive)->no_eject = 1; CDROM_CONFIG_FLAGS (drive)->supp_disc_present = 0; - /* limit transfer size per interrupt. currently only one Samsung - drive needs this. */ + /* limit transfer size per interrupt. */ CDROM_CONFIG_FLAGS (drive)->limit_nframes = 0; - if (drive->id != NULL) - if (strcmp (drive->id->model, "SAMSUNG CD-ROM SCR-2432") == 0) + if (drive->id != NULL) { + if (!strcmp(drive->id->model, "SAMSUNG CD-ROM SCR-2430")) + CDROM_CONFIG_FLAGS (drive)->limit_nframes = 1; + else if (!strcmp(drive->id->model, "SAMSUNG CD-ROM SCR-2432")) CDROM_CONFIG_FLAGS (drive)->limit_nframes = 1; + else if (!strcmp (drive->id->model, "GCD-R580B")) + CDROM_CONFIG_FLAGS (drive)->limit_nframes = 1; + /* 124/SECTORS_PER_FRAME; ? */ + } #if ! STANDARD_ATAPI /* by default Sanyo 3 CD changer support is turned off and @@ -3155,7 +3178,13 @@ NULL }; +/* options */ +char *ignore = NULL; + #ifdef MODULE +MODULE_PARM(ignore, "s"); +MODULE_DESCRIPTION("ATAPI CD-ROM Driver"); + int init_module (void) { return ide_cdrom_init(); @@ -3183,6 +3212,12 @@ MOD_INC_USE_COUNT; while ((drive = ide_scan_devices (ide_cdrom, ide_cdrom_driver.name, NULL, failed++)) != NULL) { + /* skip drives that we were told to ignore */ + if (ignore != NULL) + if (strstr(ignore, drive->name)) { + printk("ide-cd: ignoring drive %s\n", drive->name); + continue; + } info = (struct cdrom_info *) kmalloc (sizeof (struct cdrom_info), GFP_KERNEL); if (info == NULL) { printk ("%s: Can't allocate a cdrom structure\n", drive->name); diff -u --recursive --new-file v2.2.3/linux/drivers/block/ide-cd.h linux/drivers/block/ide-cd.h --- v2.2.3/linux/drivers/block/ide-cd.h Wed Jan 20 23:14:04 1999 +++ linux/drivers/block/ide-cd.h Sun Mar 21 14:18:22 1999 @@ -85,6 +85,9 @@ #define MECHANISM_STATUS 0xbd #define READ_CD 0xbe +/* DVD Opcodes */ +#define DVD_GET_PERFORMANCE 0xac + /* Page codes for mode sense/set */ @@ -562,7 +565,7 @@ /* Sector buffer. If a read request wants only the first part of a cdrom block, we cache the rest of the block here, - in the expectation that that data is going to be wanted soon. + in the expectation that the data is going to be wanted soon. SECTOR_BUFFERED is the number of the first buffered sector, and NSECTORS_BUFFERED is the number of sectors in the buffer. Before the buffer is allocated, we should have @@ -656,6 +659,7 @@ { PLAY_CD, "Play CD" }, { MECHANISM_STATUS, "Mechanism Status" }, { READ_CD, "Read CD" }, + { DVD_GET_PERFORMANCE, "Get Performance" }, }; @@ -776,7 +780,8 @@ { 0x6400, "Illegal mode for this track or incompatible medium" }, - { 0xb900, "Play operation oborted (sic)" }, + /* Following error is misspelled in ATAPI 2.6 */ + { 0xb900, "Play operation oborted [sic]" }, { 0xbf00, "Loss of streaming" }, }; diff -u --recursive --new-file v2.2.3/linux/drivers/block/ide-disk.c linux/drivers/block/ide-disk.c --- v2.2.3/linux/drivers/block/ide-disk.c Tue Feb 23 15:21:33 1999 +++ linux/drivers/block/ide-disk.c Wed Mar 10 17:49:43 1999 @@ -670,10 +670,15 @@ if (id == NULL) return; - /* check for removable disks (eg. SYQUEST), ignore 'WD' drives */ - if (id->config & (1<<7)) { /* removable disk ? */ + /* + * CompactFlash cards and their brethern look just like hard drives + * to us, but they are removable and don't have a doorlock mechanism. + */ + if (drive->removable && !drive_is_flashcard(drive)) { + /* + * Removable disks (eg. SYQUEST); ignore 'WD' drives + */ if (id->model[0] != 'W' || id->model[1] != 'D') { - drive->removable = 1; drive->doorlocking = 1; } } @@ -804,12 +809,6 @@ MOD_INC_USE_COUNT; while ((drive = ide_scan_devices (ide_disk, idedisk_driver.name, NULL, failed++)) != NULL) { - - /* SunDisk drives: ignore "second" drive; can mess up non-Sun systems! FIXME */ - struct hd_driveid *id = drive->id; - if (id && id->model[0] == 'S' && id->model[1] == 'u' && drive->select.b.unit) - continue; - if (ide_register_subdriver (drive, &idedisk_driver, IDE_SUBDRIVER_VERSION)) { printk (KERN_ERR "ide-disk: %s: Failed to register the driver with ide.c\n", drive->name); continue; diff -u --recursive --new-file v2.2.3/linux/drivers/block/ide-pmac.c linux/drivers/block/ide-pmac.c --- v2.2.3/linux/drivers/block/ide-pmac.c Thu Nov 19 09:56:28 1998 +++ linux/drivers/block/ide-pmac.c Wed Mar 10 21:48:46 1999 @@ -20,13 +20,19 @@ #include #include #include +#include #include #include #include #include #include #include +#ifdef CONFIG_PMAC_PBOOK +#include +#include +#endif #include "ide.h" +#include "ide_modes.h" ide_ioreg_t pmac_ide_regbase[MAX_HWIFS]; int pmac_ide_irq[MAX_HWIFS]; @@ -41,6 +47,13 @@ static int pmac_ide_build_dmatable(ide_drive_t *drive, int wr); #endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */ +#ifdef CONFIG_PMAC_PBOOK +static int idepmac_notify(struct notifier_block *, unsigned long, void *); +struct notifier_block idepmac_sleep_notifier = { + idepmac_notify +}; +#endif /* CONFIG_PMAC_PBOOK */ + /* * N.B. this can't be an initfunc, because the media-bay task can * call ide_[un]register at any time. @@ -74,6 +87,23 @@ } } +void pmac_ide_tuneproc(ide_drive_t *drive, byte pio) +{ + ide_pio_data_t d; + + if (_machine != _MACH_Pmac) + return; + pio = ide_get_best_pio_mode(drive, pio, 4, &d); + switch (pio) { + case 4: + out_le32((unsigned *)(IDE_DATA_REG + 0x200), 0x211025); + break; + default: + out_le32((unsigned *)(IDE_DATA_REG + 0x200), 0x2f8526); + break; + } +} + __initfunc(void pmac_ide_probe(void)) { @@ -145,9 +175,10 @@ pmac_ide_init_hwif_ports(hwif->io_ports, base, &hwif->irq); hwif->chipset = ide_generic; hwif->noprobe = !hwif->io_ports[IDE_DATA_OFFSET]; + hwif->tuneproc = pmac_ide_tuneproc; #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC - if (np->n_addrs >= 2 && np->n_intrs >= 2) { + if (np->n_addrs >= 2) { /* has a DBDMA controller channel */ pmac_ide_setup_dma(np, hwif); } @@ -156,6 +187,10 @@ ++i; } pmac_ide_count = i; + +#ifdef CONFIG_PMAC_PBOOK + notifier_chain_register(&sleep_notifier_list, &idepmac_sleep_notifier); +#endif /* CONFIG_PMAC_PBOOK */ } #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC @@ -311,5 +346,32 @@ } return 0; } - #endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */ + +#ifdef CONFIG_PMAC_PBOOK +static int idepmac_notify(struct notifier_block *this, + unsigned long code, void *p) +{ + int i, timeout; + + switch (code) { + case PBOOK_SLEEP: + /* do anything here?? */ + break; + case PBOOK_WAKE: + /* wait for the controller(s) to become ready */ + timeout = 5000; + for (i = 0; i < pmac_ide_count; ++i) { + unsigned long base = pmac_ide_regbase[i]; + if (check_media_bay_by_base(base, MB_CD) == -EINVAL) + continue; + while ((inb(base + 0x70) & BUSY_STAT) && timeout) { + mdelay(1); + --timeout; + } + } + break; + } + return NOTIFY_DONE; +} +#endif /* CONFIG_PMAC_PBOOK */ diff -u --recursive --new-file v2.2.3/linux/drivers/block/ide-probe.c linux/drivers/block/ide-probe.c --- v2.2.3/linux/drivers/block/ide-probe.c Tue Feb 23 15:21:33 1999 +++ linux/drivers/block/ide-probe.c Wed Mar 10 17:49:43 1999 @@ -1,5 +1,5 @@ /* - * linux/drivers/block/ide-probe.c Version 1.03 Dec 5, 1997 + * linux/drivers/block/ide-probe.c Version 1.04 March 10, 1999 * * Copyright (C) 1994-1998 Linus Torvalds & authors (see below) */ @@ -17,6 +17,7 @@ * Version 1.02 increase WAIT_PIDENTIFY to avoid CD-ROM locking at boot * by Andrea Arcangeli * Version 1.03 fix for (hwif->chipset == ide_4drives) + * Version 1.04 fixed buggy treatments of known flash memory cards */ #undef REALLY_SLOW_IO /* most systems can safely undef this */ @@ -42,6 +43,33 @@ #include "ide.h" +/* + * CompactFlash cards and their brethern pretend to be removable hard disks, except: + * (1) they never have a slave unit, and + * (2) they don't have doorlock mechanisms. + * This test catches them, and is invoked elsewhere when setting appropriate config bits. + * + * FIXME: This treatment is probably applicable for *all* PCMCIA (PC CARD) devices, + * so in linux 2.3.x we should change this to just treat all PCMCIA drives this way, + * and get rid of the model-name tests below (too big of an interface change for 2.2.x). + * At that time, we might also consider parameterizing the timeouts and retries, + * since these are MUCH faster than mechanical drives. -M.Lord + */ +int drive_is_flashcard (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + + if (drive->removable && id != NULL) { + if (!strncmp(id->model, "KODAK ATA_FLASH", 15) /* Kodak */ + || !strncmp(id->model, "Hitachi CV", 10) /* Hitachi */ + || !strncmp(id->model, "SunDisk SDCFB", 13)) /* SunDisk */ + { + return 1; /* yes, it is a flash memory card */ + } + } + return 0; /* no, it is not a flash memory card */ +} + static inline void do_identify (ide_drive_t *drive, byte cmd) { int bswap = 1; @@ -84,16 +112,6 @@ drive->present = 1; /* - * Prevent long system lockup probing later for non-existant - * slave drive if the hwif is actually a Kodak CompactFlash card. - */ - if (!strcmp(id->model, "KODAK ATA_FLASH")) { - ide_drive_t *mate = &HWIF(drive)->drives[1^drive->select.b.unit]; - mate->present = 0; - mate->noprobe = 1; - } - - /* * Check for an ATAPI device */ if (cmd == WIN_PIDENTIFY) { @@ -137,6 +155,20 @@ return; } + /* + * Not an ATAPI device: looks like a "regular" hard disk + */ + if (id->config & (1<<7)) + drive->removable = 1; + /* + * Prevent long system lockup probing later for non-existant + * slave drive if the hwif is actually a flash memory card of some variety: + */ + if (drive_is_flashcard(drive)) { + ide_drive_t *mate = &HWIF(drive)->drives[1^drive->select.b.unit]; + mate->present = 0; + mate->noprobe = 1; + } drive->media = ide_disk; printk("ATA DISK drive\n"); return; diff -u --recursive --new-file v2.2.3/linux/drivers/block/ide.c linux/drivers/block/ide.c --- v2.2.3/linux/drivers/block/ide.c Wed Mar 10 15:29:45 1999 +++ linux/drivers/block/ide.c Wed Mar 10 17:49:43 1999 @@ -2963,6 +2963,7 @@ /* * Probe module */ +EXPORT_SYMBOL(drive_is_flashcard); EXPORT_SYMBOL(ide_timer_expiry); EXPORT_SYMBOL(ide_intr); EXPORT_SYMBOL(ide_geninit); diff -u --recursive --new-file v2.2.3/linux/drivers/block/ide.h linux/drivers/block/ide.h --- v2.2.3/linux/drivers/block/ide.h Tue Feb 23 15:21:33 1999 +++ linux/drivers/block/ide.h Sun Mar 21 14:18:31 1999 @@ -674,6 +674,13 @@ */ struct request **ide_get_queue (kdev_t dev); +/* + * CompactFlash cards and their brethern pretend to be removable hard disks, + * but they never have a slave unit, and they don't have doorlock mechanisms. + * This test catches them, and is invoked elsewhere when setting appropriate config bits. + */ +int drive_is_flashcard (ide_drive_t *drive); + int ide_spin_wait_hwgroup(ide_drive_t *drive, unsigned long *flags); void ide_timer_expiry (unsigned long data); void ide_intr (int irq, void *dev_id, struct pt_regs *regs); diff -u --recursive --new-file v2.2.3/linux/drivers/block/ide_modes.h linux/drivers/block/ide_modes.h --- v2.2.3/linux/drivers/block/ide_modes.h Fri Jul 31 17:06:37 1998 +++ linux/drivers/block/ide_modes.h Sun Mar 21 14:18:21 1999 @@ -15,7 +15,7 @@ * breaking the fragile cmd640.c support. */ -#if defined(CONFIG_BLK_DEV_CMD640) || defined(CONFIG_IDE_CHIPSETS) || defined(CONFIG_BLK_DEV_OPTI621) +#if defined(CONFIG_BLK_DEV_CMD640) || defined(CONFIG_IDE_CHIPSETS) || defined(CONFIG_BLK_DEV_OPTI621) || defined(CONFIG_BLK_DEV_IDE_PMAC) /* * Standard (generic) timings for PIO modes, from ATA2 specification. diff -u --recursive --new-file v2.2.3/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c --- v2.2.3/linux/drivers/block/ll_rw_blk.c Mon Dec 28 15:00:52 1998 +++ linux/drivers/block/ll_rw_blk.c Thu Mar 11 23:20:14 1999 @@ -392,8 +392,10 @@ lock_buffer(bh); - if (blk_size[major]) - if (blk_size[major][MINOR(bh->b_rdev)] < (sector + count)>>1) { + if (blk_size[major]) { + unsigned long maxsector = (blk_size[major][MINOR(bh->b_rdev)] << 1) + 1; + + if (maxsector < count || maxsector - count < sector) { bh->b_state &= (1 << BH_Lock); /* This may well happen - the kernel calls bread() without checking the size of the device, e.g., @@ -406,6 +408,7 @@ blk_size[major][MINOR(bh->b_rdev)]); goto end_io; } + } rw_ahead = 0; /* normal case; gets changed below for READA/WRITEA */ switch (rw) { diff -u --recursive --new-file v2.2.3/linux/drivers/block/nbd.c linux/drivers/block/nbd.c --- v2.2.3/linux/drivers/block/nbd.c Wed Mar 10 15:29:45 1999 +++ linux/drivers/block/nbd.c Tue Mar 9 16:58:05 1999 @@ -418,7 +418,7 @@ return 0; #endif case BLKGETSIZE: - return put_user(nbd_bytesizes[dev] << 9, (long *) arg); + return put_user(nbd_bytesizes[dev] >> 9, (long *) arg); } return -EINVAL; } diff -u --recursive --new-file v2.2.3/linux/drivers/block/ns87415.c linux/drivers/block/ns87415.c --- v2.2.3/linux/drivers/block/ns87415.c Thu Nov 19 09:56:28 1998 +++ linux/drivers/block/ns87415.c Mon Mar 15 16:11:29 1999 @@ -95,6 +95,12 @@ byte stat; #endif + /* Set a good latency timer and cache line size value. */ + (void) pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64); +#ifdef __sparc_v9__ + (void) pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 0x10); +#endif + /* * We cannot probe for IRQ: both ports share common IRQ on INTA. * Also, leave IRQ masked during drive probing, to prevent infinite diff -u --recursive --new-file v2.2.3/linux/drivers/block/sl82c105.c linux/drivers/block/sl82c105.c --- v2.2.3/linux/drivers/block/sl82c105.c Thu Nov 19 09:56:28 1998 +++ linux/drivers/block/sl82c105.c Wed Mar 10 21:48:46 1999 @@ -1,3 +1,4 @@ +#include #include #include #include @@ -19,7 +20,6 @@ struct pci_dev *dev = hwif->pci_dev; unsigned short t16; unsigned int t32; - pci_read_config_word(dev, PCI_COMMAND, &t16); printk("SL82C105 command word: %x\n",t16); t16 |= PCI_COMMAND_IO; @@ -28,7 +28,9 @@ pci_read_config_dword(dev, 0x44, &t32); printk("IDE timing: %08x, resetting to PIO0 timing\n",t32); pci_write_config_dword(dev, 0x44, 0x03e4); +#ifndef CONFIG_MBX pci_read_config_dword(dev, 0x40, &t32); printk("IDE control/status register: %08x\n",t32); pci_write_config_dword(dev, 0x40, 0x10ff08a1); +#endif /* CONFIG_MBX */ } diff -u --recursive --new-file v2.2.3/linux/drivers/block/swim3.c linux/drivers/block/swim3.c --- v2.2.3/linux/drivers/block/swim3.c Thu Nov 19 09:56:28 1998 +++ linux/drivers/block/swim3.c Wed Mar 10 21:48:46 1999 @@ -10,6 +10,12 @@ * 2 of the License, or (at your option) any later version. */ +/* + * TODO: + * handle 2 drives + * handle GCR disks + */ + #include #include #include @@ -52,18 +58,18 @@ */ struct swim3 { REG(data); - REG(usecs); /* counts down at 1MHz */ + REG(timer); /* counts down at 1MHz */ REG(error); REG(mode); REG(select); /* controls CA0, CA1, CA2 and LSTRB signals */ - REG(reg5); + REG(setup); REG(control); /* writing bits clears them */ REG(status); /* writing bits sets them in control */ REG(intr); REG(nseek); /* # tracks to seek */ REG(ctrack); /* current track number */ REG(csect); /* current sector number */ - REG(ssize); /* sector size code?? */ + REG(gap3); /* size of gap 3 in track format */ REG(sector); /* sector # to read or write */ REG(nsect); /* # sectors to read or write */ REG(intr_enable); @@ -78,21 +84,46 @@ /* Bits in control register */ #define DO_SEEK 0x80 +#define FORMAT 0x40 #define SELECT 0x20 #define WRITE_SECTORS 0x10 -#define SCAN_TRACK 0x08 +#define DO_ACTION 0x08 +#define DRIVE2_ENABLE 0x04 #define DRIVE_ENABLE 0x02 #define INTR_ENABLE 0x01 /* Bits in status register */ +#define FIFO_1BYTE 0x80 +#define FIFO_2BYTE 0x40 +#define ERROR 0x20 #define DATA 0x08 +#define RDDATA 0x04 +#define INTR_PENDING 0x02 +#define MARK_BYTE 0x01 /* Bits in intr and intr_enable registers */ -#define ERROR 0x20 +#define ERROR_INTR 0x20 #define DATA_CHANGED 0x10 #define TRANSFER_DONE 0x08 #define SEEN_SECTOR 0x04 #define SEEK_DONE 0x02 +#define TIMER_DONE 0x01 + +/* Bits in error register */ +#define ERR_DATA_CRC 0x80 +#define ERR_ADDR_CRC 0x40 +#define ERR_OVERRUN 0x04 +#define ERR_UNDERRUN 0x01 + +/* Bits in setup register */ +#define S_SW_RESET 0x80 +#define S_GCR_WRITE 0x40 +#define S_IBM_DRIVE 0x20 +#define S_TEST_MODE 0x10 +#define S_FCLK_DIV2 0x08 +#define S_GCR 0x04 +#define S_COPY_PROT 0x02 +#define S_INV_WDATA 0x01 /* Select values for swim3_action */ #define SEEK_POSITIVE 0 @@ -100,14 +131,18 @@ #define STEP 1 #define MOTOR_ON 2 #define MOTOR_OFF 6 +#define INDEX 3 #define EJECT 7 +#define SETMFM 9 +#define SETGCR 13 /* Select values for swim3_select and swim3_readbit */ #define STEP_DIR 0 #define STEPPING 1 #define MOTOR_ON 2 -#define RELAX 3 +#define RELAX 3 /* also eject in progress */ #define READ_DATA_0 4 +#define TWOMEG_DRIVE 5 #define SINGLE_SIDED 6 #define DRIVE_PRESENT 7 #define DISK_IN 8 @@ -115,7 +150,25 @@ #define TRACK_ZERO 10 #define TACHO 11 #define READ_DATA_1 12 +#define MFM_MODE 13 #define SEEK_COMPLETE 14 +#define ONEMEG_MEDIA 15 + +/* Definitions of values used in writing and formatting */ +#define DATA_ESCAPE 0x99 +#define GCR_SYNC_EXC 0x3f +#define GCR_SYNC_CONV 0x80 +#define GCR_FIRST_MARK 0xd5 +#define GCR_SECOND_MARK 0xaa +#define GCR_ADDR_MARK "\xd5\xaa\x00" +#define GCR_DATA_MARK "\xd5\xaa\x0b" +#define GCR_SLIP_BYTE "\x27\xaa" +#define GCR_SELF_SYNC "\x3f\xbf\x1e\x34\x3c\x3f" + +#define DATA_99 "\x99\x99" +#define MFM_ADDR_MARK "\x99\xa1\x99\xa1\x99\xa1\x99\xfe" +#define MFM_INDEX_MARK "\x99\xc2\x99\xc2\x99\xc2\x99\xfc" +#define MFM_GAP_LEN 12 struct floppy_state { enum swim_state state; @@ -153,7 +206,7 @@ 0x4e4e, 0x4e4e, 0x4e4e, 0x4e4e, 0x4e4e, /* gap field */ 0, 0, 0, 0, 0, 0, /* sync field */ 0x99a1, 0x99a1, 0x99a1, 0x99fb, /* data address mark */ - 0x990f /* init CRC generator */ + 0x990f /* no escape for 512 bytes */ }; static unsigned short write_postamble[] = { @@ -217,11 +270,11 @@ volatile struct swim3 *sw = fs->swim3; swim3_select(fs, action); - udelay(10); - sw->select |= LSTRB; eieio(); - udelay(20); - sw->select &= ~LSTRB; eieio(); - udelay(10); + udelay(1); + out_8(&sw->select, sw->select | LSTRB); + udelay(2); + out_8(&sw->select, sw->select & ~LSTRB); + udelay(1); out_8(&sw->select, RELAX); } @@ -328,9 +381,9 @@ swim3_select(fs, READ_DATA_0); xx = sw->intr; /* clear SEEN_SECTOR bit */ - out_8(&sw->control_bis, SCAN_TRACK); + out_8(&sw->control_bis, DO_ACTION); /* enable intr when track found */ - out_8(&sw->intr_enable, ERROR | SEEN_SECTOR); + out_8(&sw->intr_enable, ERROR_INTR | SEEN_SECTOR); set_timeout(fs, HZ, scan_timeout); /* enable timeout */ } @@ -349,7 +402,7 @@ swim3_select(fs, STEP); out_8(&sw->control_bis, DO_SEEK); /* enable intr when seek finished */ - out_8(&sw->intr_enable, ERROR | SEEK_DONE); + out_8(&sw->intr_enable, ERROR_INTR | SEEK_DONE); set_timeout(fs, HZ/2, seek_timeout); /* enable timeout */ } @@ -384,7 +437,7 @@ swim3_select(fs, fs->head? READ_DATA_1: READ_DATA_0); out_8(&sw->sector, fs->req_sector); out_8(&sw->nsect, n); - out_8(&sw->ssize, 0); + out_8(&sw->gap3, 0); st_le32(&dr->cmdptr, virt_to_bus(cp)); if (CURRENT->cmd == WRITE) { /* Set up 3 dma commands: write preamble, data, postamble */ @@ -400,9 +453,9 @@ out_le16(&cp->command, DBDMA_STOP); out_le32(&dr->control, (RUN << 16) | RUN); out_8(&sw->control_bis, - (CURRENT->cmd == WRITE? WRITE_SECTORS: 0) | SCAN_TRACK); + (CURRENT->cmd == WRITE? WRITE_SECTORS: 0) | DO_ACTION); /* enable intr when transfer complete */ - out_8(&sw->intr_enable, ERROR | TRANSFER_DONE); + out_8(&sw->intr_enable, ERROR_INTR | TRANSFER_DONE); set_timeout(fs, 2*HZ, xfer_timeout); /* enable timeout */ } @@ -445,7 +498,7 @@ /* wait for SEEK_COMPLETE to become true */ swim3_select(fs, SEEK_COMPLETE); udelay(10); - out_8(&sw->intr_enable, ERROR | DATA_CHANGED); + out_8(&sw->intr_enable, ERROR_INTR | DATA_CHANGED); in_8(&sw->intr); /* clear DATA_CHANGED */ if (in_8(&sw->status) & DATA) { /* seek_complete is not yet true */ @@ -487,7 +540,7 @@ volatile struct swim3 *sw = fs->swim3; fs->timeout_pending = 0; - out_8(&sw->control_bic, SCAN_TRACK); + out_8(&sw->control_bic, DO_ACTION); out_8(&sw->select, RELAX); out_8(&sw->intr_enable, 0); fs->cur_cyl = -1; @@ -537,7 +590,7 @@ fs->timeout_pending = 0; st_le32(&dr->control, RUN << 16); out_8(&sw->intr_enable, 0); - out_8(&sw->control_bic, WRITE_SECTORS | SCAN_TRACK); + out_8(&sw->control_bic, WRITE_SECTORS | DO_ACTION); out_8(&sw->select, RELAX); if (CURRENT->cmd == WRITE) ++cp; @@ -568,13 +621,13 @@ #if 0 printk("swim3 intr state=%d intr=%x err=%x\n", fs->state, intr, err); #endif - if ((intr & ERROR) && fs->state != do_transfer) + if ((intr & ERROR_INTR) && fs->state != do_transfer) printk(KERN_ERR "swim3_interrupt, state=%d, cmd=%x, intr=%x, err=%x\n", fs->state, CURRENT->cmd, intr, err); switch (fs->state) { case locating: if (intr & SEEN_SECTOR) { - out_8(&sw->control_bic, SCAN_TRACK); + out_8(&sw->control_bic, DO_ACTION); out_8(&sw->select, RELAX); out_8(&sw->intr_enable, 0); del_timer(&fs->timeout); @@ -622,13 +675,13 @@ act(fs); break; case do_transfer: - if ((intr & (ERROR | TRANSFER_DONE)) == 0) + if ((intr & (ERROR_INTR | TRANSFER_DONE)) == 0) break; dr = fs->dma; cp = fs->dma_cmd; st_le32(&dr->control, RUN << 16); out_8(&sw->intr_enable, 0); - out_8(&sw->control_bic, WRITE_SECTORS | SCAN_TRACK); + out_8(&sw->control_bic, WRITE_SECTORS | DO_ACTION); out_8(&sw->select, RELAX); del_timer(&fs->timeout); fs->timeout_pending = 0; @@ -636,7 +689,7 @@ ++cp; stat = ld_le16(&cp->xfer_status); resid = ld_le16(&cp->res_count); - if (intr & ERROR) { + if (intr & ERROR_INTR) { n = fs->scount - 1 - resid / 512; if (n > 0) { CURRENT->sector += n; @@ -800,6 +853,8 @@ if (devnum >= floppy_count) return -ENODEV; + if (filp == 0) + return -EIO; fs = &floppy_states[devnum]; sw = fs->swim3; @@ -809,7 +864,7 @@ return -ENXIO; out_8(&sw->mode, 0x95); out_8(&sw->control_bic, 0xff); - out_8(&sw->reg5, 0x28); + out_8(&sw->setup, S_IBM_DRIVE | S_FCLK_DIV2); udelay(10); out_8(&sw->intr_enable, 0); out_8(&sw->control_bis, DRIVE_ENABLE | INTR_ENABLE); @@ -834,14 +889,14 @@ } else if (fs->ref_count == -1 || filp->f_flags & O_EXCL) return -EBUSY; - if (err == 0 && filp && (filp->f_flags & O_NDELAY) == 0 + if (err == 0 && (filp->f_flags & O_NDELAY) == 0 && (filp->f_mode & 3)) { check_disk_change(inode->i_rdev); if (fs->ejected) err = -ENXIO; } - if (err == 0 && filp && (filp->f_flags & (O_WRONLY | O_RDWR))) { + if (err == 0 && (filp->f_mode & 2)) { if (fs->write_prot < 0) fs->write_prot = swim3_readbit(fs, WRITE_PROT); if (fs->write_prot) @@ -977,10 +1032,14 @@ if (devnum >= floppy_count) return -ENODEV; - + check_disk_change(inode->i_rdev); fs = &floppy_states[devnum]; if (fs->ejected) return -ENXIO; + if (fs->write_prot < 0) + fs->write_prot = swim3_readbit(fs, WRITE_PROT); + if (fs->write_prot) + return -EROFS; return block_write(filp, buf, count, ppos); } diff -u --recursive --new-file v2.2.3/linux/drivers/cdrom/cdrom.c linux/drivers/cdrom/cdrom.c --- v2.2.3/linux/drivers/cdrom/cdrom.c Tue Jan 19 11:32:51 1999 +++ linux/drivers/cdrom/cdrom.c Sun Mar 21 18:37:56 1999 @@ -24,8 +24,9 @@ -- Change the CDROMREADMODE1, CDROMREADMODE2, CDROMREADAUDIO, and CDROMREADRAW ioctls so they go through the Uniform CD-ROM driver. - - + + -- Sync options and capability flags. + Revision History @@ -97,17 +98,33 @@ cdi->options in various ioctl. -- Added version to proc entry. - 2.52 Jan 16, 1998 - Jens Axboe + 2.52 Jan 16, 1999 - Jens Axboe -- Fixed an error in open_for_data where we would sometimes not return the correct error value. Thanks Huba Gaspar . -- Fixed module usage count - usage was based on /proc/sys/dev instead of /proc/sys/dev/cdrom. This could lead to an oops when other - modules had entries in dev. + modules had entries in dev. Feb 02 - real bug was in sysctl.c where + dev would be removed even though it was used. cdrom.c just illuminated + that bug. + + 2.53 Feb 22, 1999 - Jens Axboe + -- Fixup of several ioctl calls, in particular CDROM_SET_OPTIONS has + been "rewritten" because capabilities and options aren't in sync. They + should be... + -- Added CDROM_LOCKDOOR ioctl. Locks the door and keeps it that way. + -- Added CDROM_RESET ioctl. + -- Added CDROM_DEBUG ioctl. Enable debug messages on-the-fly. + -- Added CDROM_GET_CAPABILITY ioctl. This relieves userspace programs + from parsing /proc/sys/dev/cdrom/info. + + 2.54 Mar 15, 1999 - Jens Axboe + -- Check capability mask from low level driver when counting tracks as + per suggestion from Corey J. Scotts . -------------------------------------------------------------------------*/ -#define REVISION "Revision: 2.52" -#define VERSION "Id: cdrom.c 2.52 1999/01/16" +#define REVISION "Revision: 2.54" +#define VERSION "Id: cdrom.c 2.54 1999/03/15" /* I use an error-log mask to give fine grain control over the type of messages dumped to the system logs. The available masks include: */ @@ -144,6 +161,8 @@ /* used to tell the module to turn on full debugging messages */ static int debug = 0; +/* used to keep tray locked at all times */ +static int keeplocked = 0; /* default compatibility mode */ static int autoclose=1; static int autoeject=0; @@ -164,13 +183,11 @@ #endif /* These are used to simplify getting data in from and back to user land */ -#define IOCTL_IN(arg, type, in) { \ - if ( copy_from_user(&in, (type *) arg, sizeof in) ) \ - return -EFAULT; } - -#define IOCTL_OUT(arg, type, out) { \ - if ( copy_to_user((type *) arg, &out, sizeof out) ) \ - return -EFAULT; } +#define IOCTL_IN(arg, type, in) \ + copy_from_user_ret(&in, (type *) arg, sizeof in, -EFAULT) + +#define IOCTL_OUT(arg, type, out) \ + copy_to_user_ret((type *) arg, &out, sizeof out, -EFAULT) #define FM_WRITE 0x2 /* file mode write bit */ @@ -328,7 +345,7 @@ if (fp->f_mode & FM_WRITE) return -EROFS; purpose = purpose || !(cdi->options & CDO_USE_FFLAGS); - if (cdi->use_count || purpose) + if (purpose) ret = cdi->ops->open(cdi, purpose); else ret = open_for_data(cdi); @@ -517,10 +534,10 @@ if (cdi->use_count == 0) cdinfo(CD_CLOSE, "Use count for \"/dev/%s\" now zero\n", cdi->name); if (cdi->use_count == 0 && /* last process that closes dev*/ - cdo->capability & CDC_LOCK) { + cdo->capability & CDC_LOCK && !keeplocked) { cdinfo(CD_CLOSE, "Unlocking door!\n"); cdo->lock_door(cdi, 0); - } + } opened_for_data = !(cdi->options & CDO_USE_FFLAGS) || !(fp && fp->f_flags & O_NONBLOCK); cdo->release(cdi); @@ -588,14 +605,17 @@ tracks->xa=0; tracks->error=0; cdinfo(CD_COUNT_TRACKS, "entering cdrom_count_tracks\n"); - if (!(cdi->ops->capability & CDC_PLAY_AUDIO)) { + if (!(cdi->ops->capability & ~cdi->mask & CDC_PLAY_AUDIO)) { tracks->error=CDS_NO_INFO; return; } /* Grab the TOC header so we can see how many tracks there are */ - ret=cdi->ops->audio_ioctl(cdi, CDROMREADTOCHDR, &header); + ret = cdi->ops->audio_ioctl(cdi, CDROMREADTOCHDR, &header); if (ret) { - tracks->error=(ret == -ENOMEDIUM) ? CDS_NO_DISC : CDS_NO_INFO; + if (ret == -ENOMEDIUM) + tracks->error = CDS_NO_DISC; + else + tracks->error = CDS_NO_INFO; return; } /* check what type of tracks are on this disc */ @@ -716,18 +736,18 @@ cdinfo(CD_DO_IOCTL, "entering CDROMEJECT\n"); if (!(cdo->capability & ~cdi->mask & CDC_OPEN_TRAY)) return -ENOSYS; - if (cdi->use_count != 1) + if (cdi->use_count != 1 || keeplocked) return -EBUSY; - if (cdo->capability & ~cdi->mask & CDC_LOCK) { + if (cdo->capability & ~cdi->mask & CDC_LOCK) if ((ret=cdo->lock_door(cdi, 0))) return ret; - } + return cdo->tray_move(cdi, 1); } case CDROMCLOSETRAY: cdinfo(CD_DO_IOCTL, "entering CDROMCLOSETRAY\n"); - if (!(cdo->capability & ~cdi->mask & CDC_OPEN_TRAY)) + if (!(cdo->capability & ~cdi->mask & CDC_CLOSE_TRAY)) return -ENOSYS; return cdo->tray_move(cdi, 0); @@ -735,6 +755,8 @@ cdinfo(CD_DO_IOCTL, "entering CDROMEJECT_SW\n"); if (!(cdo->capability & ~cdi->mask & CDC_OPEN_TRAY)) return -ENOSYS; + if (keeplocked) + return -EBUSY; cdi->options &= ~(CDO_AUTO_CLOSE | CDO_AUTO_EJECT); if (arg) cdi->options |= CDO_AUTO_CLOSE | CDO_AUTO_EJECT; @@ -755,8 +777,23 @@ case CDROM_SET_OPTIONS: cdinfo(CD_DO_IOCTL, "entering CDROM_SET_OPTIONS\n"); - if (cdo->capability & arg & ~cdi->mask) - return -ENOSYS; + /* options need to be in sync with capability. too late for + that, so we have to check each one separately... */ + switch (arg) { + case CDO_USE_FFLAGS: + case CDO_CHECK_TYPE: + break; + case CDO_LOCK: + if (!(cdo->capability & ~cdi->mask & CDC_LOCK)) + return -ENOSYS; + break; + case 0: + return cdi->options; + /* default is basically CDO_[AUTO_CLOSE|AUTO_EJECT] */ + default: + if (!(cdo->capability & ~cdi->mask & arg)) + return -ENOSYS; + } cdi->options |= (int) arg; return cdi->options; @@ -783,6 +820,36 @@ return cdo->select_disc(cdi, arg); } + case CDROMRESET: { + cdinfo(CD_DO_IOCTL, "entering CDROM_RESET\n"); + if (!(cdo->capability & ~cdi->mask & CDC_RESET)) + return -ENOSYS; + return cdo->reset(cdi); + } + + case CDROM_LOCKDOOR: { + cdinfo(CD_DO_IOCTL, "%socking door.\n",arg?"L":"Unl"); + if (!(cdo->capability & ~cdi->mask & CDC_LOCK)) { + return -EDRIVE_CANT_DO_THIS; + } else { + keeplocked = arg ? 1 : 0; + return cdo->lock_door(cdi, arg); + } + } + + case CDROM_DEBUG: { + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + cdinfo(CD_DO_IOCTL, "%sabling debug.\n",arg?"En":"Dis"); + debug = arg ? 1 : 0; + return debug; + } + + case CDROM_GET_CAPABILITY: { + cdinfo(CD_DO_IOCTL, "entering CDROM_GET_CAPABILITY\n"); + return cdo->capability; + } + /* The following function is implemented, although very few audio * discs give Universal Product Code information, which should just be * the Medium Catalog Number on the box. Note, that the way the code @@ -1123,7 +1190,7 @@ return; cdrom_sysctl_header = register_sysctl_table(cdrom_root_table, 1); - cdrom_root_table->de->fill_inode = &cdrom_procfs_modcount; + cdrom_root_table->child->de->fill_inode = &cdrom_procfs_modcount; initialized = 1; } diff -u --recursive --new-file v2.2.3/linux/drivers/cdrom/cm206.c linux/drivers/cdrom/cm206.c --- v2.2.3/linux/drivers/cdrom/cm206.c Thu Sep 17 17:53:35 1998 +++ linux/drivers/cdrom/cm206.c Wed Mar 10 17:01:57 1999 @@ -186,7 +186,6 @@ #include #include #include -#include /* #include */ diff -u --recursive --new-file v2.2.3/linux/drivers/char/Makefile linux/drivers/char/Makefile --- v2.2.3/linux/drivers/char/Makefile Wed Mar 10 15:29:45 1999 +++ linux/drivers/char/Makefile Mon Mar 15 16:11:29 1999 @@ -44,12 +44,17 @@ ifdef CONFIG_VT L_OBJS += keyboard.o endif -ifneq ($(ARCH),m68k) -L_OBJS += pc_keyb.o defkeymap.o + ifneq ($(ARCH),m68k) + L_OBJS += pc_keyb.o defkeymap.o + endif +else +ifdef CONFIG_PCI +L_OBJS += defkeymap.o keyboard.o endif -ifdef CONFIG_MAGIC_SYSRQ -L_OBJS += sysrq.o endif + +ifdef CONFIG_MAGIC_SYSRQ +LX_OBJS += sysrq.o endif ifeq ($(CONFIG_ATARI_DSP56K),y) diff -u --recursive --new-file v2.2.3/linux/drivers/char/README.epca linux/drivers/char/README.epca --- v2.2.3/linux/drivers/char/README.epca Fri Jan 8 22:36:05 1999 +++ linux/drivers/char/README.epca Tue Mar 16 14:21:35 1999 @@ -504,3 +504,14 @@ Files affected : epca.c Release version : 1.1.1 (BETA) ----------------------------------------------------------------------- +Programmer : Daniel Taylor +Date : March 11, 1999 +Description (Verbose) : Updated driver: + 1. Simultaneous data and modem change events were + resulting in the modem change events not being + recognized. Fixed. + 2. Modified pc_info device name to work better + with devfs. +Files affected : epca.c +Release version : 1.3.0-K +----------------------------------------------------------------------- diff -u --recursive --new-file v2.2.3/linux/drivers/char/bw-qcam.c linux/drivers/char/bw-qcam.c --- v2.2.3/linux/drivers/char/bw-qcam.c Sun Nov 8 14:02:53 1998 +++ linux/drivers/char/bw-qcam.c Wed Mar 10 17:03:52 1999 @@ -5,6 +5,28 @@ * * Video4Linux conversion work by Alan Cox. * Parport compatibility by Phil Blundell. + * Busy loop avoidance by Mark Cooke. + * + * Module parameters: + * + * maxpoll=<1 - 5000> + * + * When polling the QuickCam for a response, busy-wait for a + * maximum of this many loops. The default of 250 gives little + * impact on interactive response. + * + * NOTE: If this parameter is set too high, the processor + * will busy wait until this loop times out, and then + * slowly poll for a further 5 seconds before failing + * the transaction. You have been warned. + * + * yieldlines=<1 - 250> + * + * When acquiring a frame from the camera, the data gathering + * loop will yield back to the scheduler after completing + * this many lines. The default of 4 provides a trade-off + * between increased frame acquisition time and impact on + * interactive response. */ /* qcam-lib.c -- Library for programming with the Connectix QuickCam. @@ -58,6 +80,14 @@ #include "bw-qcam.h" +#if LINUX_VERSION_CODE >= 0x020117 +MODULE_PARM(maxpoll,"i"); +MODULE_PARM(yieldlines,"i"); +#endif + +static unsigned int maxpoll=250; /* Maximum busy-loop count for qcam I/O */ +static unsigned int yieldlines=4; /* Yield after this many during capture */ + extern __inline__ int read_lpstatus(struct qcam_device *q) { return parport_read_status(q->pport); @@ -154,6 +184,7 @@ q->top = 1; q->left = 14; q->mode = -1; + q->status = QC_PARAM_CHANGE; return q; } @@ -209,14 +240,17 @@ { /* 1000 is enough spins on the I/O for all normal cases, at that point we start to poll slowly - until the camera wakes up */ + until the camera wakes up. However, we are + busy blocked until the camera responds, so + setting it lower is much better for interactive + response. */ - if(runs++>1000) + if(runs++>maxpoll) { current->state=TASK_INTERRUPTIBLE; - schedule_timeout(HZ/10); + schedule_timeout(HZ/200); } - if(runs>1050) + if(runs>(maxpoll+1000)) /* 5 seconds */ return -1; } } @@ -226,14 +260,17 @@ { /* 1000 is enough spins on the I/O for all normal cases, at that point we start to poll slowly - until the camera wakes up */ + until the camera wakes up. However, we are + busy blocked until the camera responds, so + setting it lower is much better for interactive + response. */ - if(runs++>1000) + if(runs++>maxpoll) { current->state=TASK_INTERRUPTIBLE; - schedule_timeout(HZ/10); + schedule_timeout(HZ/200); } - if(runs++>1050) /* 5 seconds */ + if(runs++>(maxpoll+1000)) /* 5 seconds */ return -1; } } @@ -256,14 +293,17 @@ status = read_lpdata(q); /* 1000 is enough spins on the I/O for all normal cases, at that point we start to poll slowly - until the camera wakes up */ + until the camera wakes up. However, we are + busy blocked until the camera responds, so + setting it lower is much better for interactive + response. */ - if(runs++>1000) + if(runs++>maxpoll) { current->state=TASK_INTERRUPTIBLE; - schedule_timeout(HZ/10); + schedule_timeout(HZ/200); } - if(runs++>1050) /* 5 seconds */ + if(runs++>(maxpoll+1000)) /* 5 seconds */ return 0; } while ((status & 1) != val); @@ -287,7 +327,7 @@ lastreg = reg = read_lpstatus(q) & 0xf0; - for (i = 0; i < 300; i++) + for (i = 0; i < 500; i++) { reg = read_lpstatus(q) & 0xf0; if (reg != lastreg) @@ -296,9 +336,20 @@ mdelay(2); } - /* Be liberal in what you accept... */ - if (count > 30 && count < 200) +#if 0 + /* Force camera detection during testing. Sometimes the camera + won't be flashing these bits. Possibly unloading the module + in the middle of a grab? Or some timeout condition? + I've seen this parameter as low as 19 on my 450Mhz box - mpc */ + printk("Debugging: QCam detection counter <30-200 counts as detected>: %d\n", count); + return 1; +#endif + + /* Be (even more) liberal in what you accept... */ + +/* if (count > 30 && count < 200) */ + if (count > 20 && count < 300) return 1; /* found */ else return 0; /* not found */ @@ -352,6 +403,8 @@ static int qc_setscanmode(struct qcam_device *q) { + int old_mode = q->mode; + switch (q->transfer_scale) { case 1: @@ -383,6 +436,10 @@ case QC_UNIDIR: break; } + + if (q->mode != old_mode) + q->status |= QC_PARAM_CHANGE; + return 0; } @@ -434,8 +491,11 @@ qc_command(q, q->contrast); qc_command(q, 0x1f); qc_command(q, q->whitebal); -} + /* Clear flag that we must update the grabbing parameters on the camera + before we grab the next frame */ + q->status &= (~QC_PARAM_CHANGE); +} /* Qc_readbytes reads some bytes from the QC and puts them in the supplied buffer. It returns the number of bytes read, @@ -539,7 +599,7 @@ long qc_capture(struct qcam_device * q, char *buf, unsigned long len) { - int i, j, k; + int i, j, k, yield; int bytes; int linestotrans, transperline; int divisor; @@ -575,7 +635,7 @@ q->transfer_scale; transperline = (transperline + divisor - 1) / divisor; - for (i = 0; i < linestotrans; i++) + for (i = 0, yield = yieldlines; i < linestotrans; i++) { for (pixels_read = j = 0; j < transperline; j++) { @@ -599,6 +659,18 @@ pixels_read += bytes; } (void) qc_readbytes(q, 0); /* reset state machine */ + + /* Grabbing an entire frame from the quickcam is a lengthy + process. We don't (usually) want to busy-block the + processor for the entire frame. yieldlines is a module + parameter. If we yield every line, the minimum frame + time will be 240 / 200 = 1.2 seconds. The compile-time + default is to yield every 4 lines. */ + if (i >= yield) { + current->state=TASK_INTERRUPTIBLE; + schedule_timeout(HZ/200); + yield = i + yieldlines; + } } if ((q->port_mode & QC_MODE_MASK) == QC_BIDIR) @@ -745,9 +817,12 @@ qcam->bpp = p.depth; qc_setscanmode(qcam); - parport_claim_or_block(qcam->pdev); + qcam->status |= QC_PARAM_CHANGE; + +/* parport_claim_or_block(qcam->pdev); qc_set(qcam); parport_release(qcam->pdev); +*/ return 0; } case VIDIOCSWIN: @@ -779,6 +854,11 @@ qcam->transfer_scale = 1; } qc_setscanmode(qcam); + + /* We must update the camera before we grab. We could + just have changed the grab size */ + qcam->status |= QC_PARAM_CHANGE; + /* Ok we figured out what to use from our wide choice */ return 0; } @@ -824,11 +904,15 @@ parport_claim_or_block(qcam->pdev); /* Probably should have a semaphore against multiple users */ qc_reset(qcam); + + /* Update the camera parameters if we need to */ + if (qcam->status & QC_PARAM_CHANGE) + qc_set(qcam); + len=qc_capture(qcam, buf,count); parport_release(qcam->pdev); return len; } - static struct video_device qcam_template= { @@ -909,6 +993,17 @@ for (port = parport_enumerate(); port; port=port->next) init_bwqcam(port); + + /* Do some sanity checks on the module parameters. */ + if (maxpoll > 5000) { + printk("Connectix Quickcam max-poll was above 5000. Using 5000.\n"); + maxpoll = 5000; + } + + if (yieldlines < 1) { + printk("Connectix Quickcam yieldlines was less than 1. Using 1.\n"); + yieldlines = 1; + } return (num_cams)?0:-ENODEV; } diff -u --recursive --new-file v2.2.3/linux/drivers/char/bw-qcam.h linux/drivers/char/bw-qcam.h --- v2.2.3/linux/drivers/char/bw-qcam.h Mon Jan 12 14:46:16 1998 +++ linux/drivers/char/bw-qcam.h Wed Mar 10 17:03:52 1999 @@ -48,6 +48,9 @@ #define MAX_HEIGHT 243 #define MAX_WIDTH 336 +/* Bit fields for status flags */ +#define QC_PARAM_CHANGE 0x01 /* Camera status change has occurred */ + struct qcam_device { struct video_device vdev; struct pardevice *pdev; @@ -59,6 +62,6 @@ int port_mode; int transfer_scale; int top, left; + int status; unsigned int saved_bits; }; - diff -u --recursive --new-file v2.2.3/linux/drivers/char/console.c linux/drivers/char/console.c --- v2.2.3/linux/drivers/char/console.c Wed Mar 10 15:29:45 1999 +++ linux/drivers/char/console.c Wed Mar 10 16:51:35 1999 @@ -85,7 +85,6 @@ #include #include #include -#include #include #include #include diff -u --recursive --new-file v2.2.3/linux/drivers/char/epca.c linux/drivers/char/epca.c --- v2.2.3/linux/drivers/char/epca.c Wed Jan 13 15:00:41 1999 +++ linux/drivers/char/epca.c Tue Mar 16 14:21:51 1999 @@ -83,7 +83,6 @@ #include #include #include -#include #include #ifdef MODULE @@ -117,7 +116,7 @@ /* ---------------------- Begin defines ------------------------ */ -#define VERSION "1.1.0" +#define VERSION "1.3.0-K" /* This major needs to be submitted to Linux to join the majors list */ @@ -1814,9 +1813,10 @@ pc_callout.subtype = SERIAL_TYPE_CALLOUT; pc_info = pc_driver; - pc_info.name = "digiCtl"; + pc_info.name = "digi_ctl"; pc_info.major = DIGIINFOMAJOR; pc_info.minor_start = 0; + pc_info.num = 1; pc_info.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL; pc_info.subtype = SERIAL_TYPE_INFO; @@ -2398,7 +2398,7 @@ assertgwinon(ch); } /* End DATA_IND */ - else + /* else *//* Fix for DCD transition missed bug */ if (event & MODEMCHG_IND) { /* Begin MODEMCHG_IND */ diff -u --recursive --new-file v2.2.3/linux/drivers/char/lp.c linux/drivers/char/lp.c --- v2.2.3/linux/drivers/char/lp.c Wed Dec 16 10:32:55 1998 +++ linux/drivers/char/lp.c Thu Mar 11 09:27:06 1999 @@ -437,7 +437,7 @@ } else if (!(status & LP_PERRORP)) { if (last != LP_PERRORP) { last = LP_PERRORP; - printk(KERN_ERR "lp%d on fire!\n", minor); + printk(KERN_INFO "lp%d on fire!\n", minor); } } else last = 0; @@ -664,17 +664,15 @@ } if ((i & 1) != 0) { Byte |= (z<<4); - if (temp) { - if (__put_user (Byte, temp)) - { - count = -EFAULT; - temp = NULL; - } else { - temp++; + if (__put_user (Byte, temp)) + { + count = -EFAULT; + break; + } else { + temp++; - if (++count == length) - temp = NULL; - } + if (++count == length) + break; } /* Does the error line indicate end of data? */ if ((parport_read_status(port) & LP_PERRORP) == @@ -952,16 +950,12 @@ default: for (i = 0; i < LP_NO; i++) { - if (parport_nr[i] >= 0) { - char buffer[16]; - sprintf(buffer, "parport%d", parport_nr[i]); - for (port = parport_enumerate(); port; - port = port->next) { - if (!strcmp(port->name, buffer)) { - (void) lp_register(i, port); + for (port = parport_enumerate(); port; + port = port->next) { + if (port->number == parport_nr[i]) { + if (!lp_register(i, port)) count++; - break; - } + break; } } } diff -u --recursive --new-file v2.2.3/linux/drivers/char/pcxx.c linux/drivers/char/pcxx.c --- v2.2.3/linux/drivers/char/pcxx.c Tue Jan 19 11:32:51 1999 +++ linux/drivers/char/pcxx.c Wed Mar 10 16:51:35 1999 @@ -68,7 +68,6 @@ #include #include #include -#include #include #include diff -u --recursive --new-file v2.2.3/linux/drivers/char/rocket.c linux/drivers/char/rocket.c --- v2.2.3/linux/drivers/char/rocket.c Sun Nov 8 14:02:55 1998 +++ linux/drivers/char/rocket.c Wed Mar 10 16:51:35 1999 @@ -80,7 +80,6 @@ #include #include #include -#include #include #ifdef ENABLE_PCI #include diff -u --recursive --new-file v2.2.3/linux/drivers/char/tpqic02.c linux/drivers/char/tpqic02.c --- v2.2.3/linux/drivers/char/tpqic02.c Mon Dec 28 15:00:52 1998 +++ linux/drivers/char/tpqic02.c Wed Mar 10 16:51:35 1999 @@ -75,7 +75,6 @@ #include -#include #include #include #include diff -u --recursive --new-file v2.2.3/linux/drivers/fc4/Config.in linux/drivers/fc4/Config.in --- v2.2.3/linux/drivers/fc4/Config.in Fri Nov 27 13:09:23 1998 +++ linux/drivers/fc4/Config.in Mon Mar 15 16:11:29 1999 @@ -7,12 +7,21 @@ tristate 'Fibre Channel and FC4 SCSI support' CONFIG_FC4 if [ ! "$CONFIG_FC4" = "n" ]; then comment 'FC4 drivers' - tristate 'Sun SOC/Sbus' CONFIG_FC4_SOC + if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then + tristate 'Sun SOC/Sbus' CONFIG_FC4_SOC + tristate 'Sun SOC+ (aka SOCAL)' CONFIG_FC4_SOCAL + fi comment 'FC4 targets' dep_tristate 'SparcSTORAGE Array 100 and 200 series' CONFIG_SCSI_PLUTO $CONFIG_SCSI + if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then + dep_tristate 'Sun Enterprise Network Array (A5000 and EX500)' CONFIG_SCSI_FCAL $CONFIG_SCSI + else + dep_tristate 'Generic FC-AL disk driver' CONFIG_SCSI_FCAL $CONFIG_SCSI + fi else define_bool CONFIG_FC4_SOC n + define_bool CONFIG_FC4_SOCAL n define_bool CONFIG_SCSI_PLUTO n + define_bool CONFIG_SCSI_FCAL n fi endmenu - diff -u --recursive --new-file v2.2.3/linux/drivers/fc4/Makefile linux/drivers/fc4/Makefile --- v2.2.3/linux/drivers/fc4/Makefile Mon Jan 12 15:19:36 1998 +++ linux/drivers/fc4/Makefile Mon Mar 15 16:11:29 1999 @@ -33,6 +33,14 @@ endif endif +ifeq ($(CONFIG_FC4_SOCAL),y) +L_OBJS += socal.o +else + ifeq ($(CONFIG_FC4_SOCAL),m) + M_OBJS += socal.o + endif +endif + include $(TOPDIR)/Rules.make fc4.o: $(MIX_OBJS) fc.o diff -u --recursive --new-file v2.2.3/linux/drivers/fc4/fc-al.h linux/drivers/fc4/fc-al.h --- v2.2.3/linux/drivers/fc4/fc-al.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/fc4/fc-al.h Mon Mar 15 16:11:29 1999 @@ -0,0 +1,27 @@ +/* fc-al.h: Definitions for Fibre Channel Arbitrated Loop topology. + * + * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * + * Sources: + * Fibre Channel Arbitrated Loop (FC-AL), ANSI, Rev. 4.5, 1995 + */ + +#ifndef __FC_AL_H +#define __FC_AL_H + +/* Loop initialization payloads */ +#define FC_AL_LISM 0x11010000 /* Select Master, 12B payload */ +#define FC_AL_LIFA 0x11020000 /* Fabric Assign AL_PA bitmap, 20B payload */ +#define FC_AL_LIPA 0x11030000 /* Previously Acquired AL_PA bitmap, 20B payload */ +#define FC_AL_LIHA 0x11040000 /* Hard Assigned AL_PA bitmap, 20B payload */ +#define FC_AL_LISA 0x11050000 /* Soft Assigned AL_PA bitmap, 20B payload */ +#define FC_AL_LIRP 0x11060000 /* Report AL_PA position map, 132B payload */ +#define FC_AL_LILP 0x11070000 /* Loop AL_PA position map, 132B payload */ + +typedef struct { + u32 magic; + u8 len; + u8 alpa[127]; +} fc_al_posmap; + +#endif /* !(__FC_H) */ diff -u --recursive --new-file v2.2.3/linux/drivers/fc4/fc.c linux/drivers/fc4/fc.c --- v2.2.3/linux/drivers/fc4/fc.c Tue Dec 22 14:16:55 1998 +++ linux/drivers/fc4/fc.c Mon Mar 15 16:11:29 1999 @@ -1,11 +1,24 @@ /* fc.c: Generic Fibre Channel and FC4 SCSI driver. * - * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997,1998,1999 Jakub Jelinek (jj@ultra.linux.cz) * Copyright (C) 1997,1998 Jirka Hanika (geo@ff.cuni.cz) * + * There are two kinds of Fibre Channel adapters used in Linux. Either + * the adapter is "smart" and does all FC bookkeeping by itself and + * just presents a standard SCSI interface to the operating system + * (that's e.g. the case with Qlogic FC cards), or leaves most of the FC + * bookkeeping to the OS (e.g. soc, socal). Drivers for the former adapters + * will look like normal SCSI drivers (with the exception of max_id will be + * usually 127), the latter on the other side allows SCSI, IP over FC and other + * protocols. This driver tree is for the latter adapters. + * + * This file should support both Point-to-Point and Arbitrated Loop topologies. + * * Sources: * Fibre Channel Physical & Signaling Interface (FC-PH), dpANS, 1994 * dpANS Fibre Channel Protocol for SCSI (X3.269-199X), Rev. 012, 1995 + * Fibre Channel Arbitrated Loop (FC-AL), Rev. 4.5, 1995 + * Fibre Channel Private Loop SCSI Direct Attach (FC-PLDA), Rev. 2.1, 1997 */ #include @@ -25,7 +38,7 @@ #include #include #include -#include "fcp_scsi.h" +#include "fcp_impl.h" #include "../scsi/hosts.h" /* #define FCDEBUG */ @@ -43,7 +56,7 @@ #ifdef __sparc__ static inline void *fc_dma_alloc(long size, char *name, dma_handle *dma) { - return (void *) sparc_dvma_malloc (size, "FCP SCSI cmd & rsp queues", dma); + return (void *) sparc_dvma_malloc (size, name, dma); } static inline dma_handle fc_sync_dma_entry(void *buf, int len, fc_channel *fc) @@ -73,6 +86,9 @@ #define FC_SCMND(SCpnt) ((fc_channel *)(SCpnt->host->hostdata[0])) #define SC_FCMND(fcmnd) ((Scsi_Cmnd *)((long)fcmnd - (long)&(((Scsi_Cmnd *)0)->SCp))) +static int fcp_scsi_queue_it(fc_channel *, Scsi_Cmnd *, fcp_cmnd *, int); +void fcp_queue_empty(fc_channel *); + static void fcp_scsi_insert_queue (fc_channel *fc, fcp_cmnd *fcmd) { if (!fc->scsi_que) { @@ -101,7 +117,7 @@ fc_channel *fc_channels = NULL; -#define LSMAGIC 0x2a3b4d2a +#define LSMAGIC 620829043 typedef struct { /* Must be first */ struct semaphore sem; @@ -111,10 +127,10 @@ fcp_cmnd *fcmds; atomic_t todo; struct timer_list timer; - int grace[1]; + unsigned char grace[0]; } ls; -#define LSOMAGIC 0x2a3c4e3c +#define LSOMAGIC 654907799 typedef struct { /* Must be first */ struct semaphore sem; @@ -125,6 +141,15 @@ struct timer_list timer; } lso; +#define LSEMAGIC 84482456 +typedef struct { + /* Must be first */ + struct semaphore sem; + int magic; + int status; + struct timer_list timer; +} lse; + static void fcp_login_timeout(unsigned long data) { ls *l = (ls *)data; @@ -225,6 +250,97 @@ } } +static void fcp_report_map_done(fc_channel *fc, int i, int status) +{ + fcp_cmnd *fcmd; + fc_hdr *fch; + unsigned char j; + ls *l = (ls *)fc->ls; + fc_al_posmap *p; + + FCD(("Report map done %d %d\n", i, status)) + switch (status) { + case FC_STATUS_OK: /* Ok, let's have a fun on a loop */ + fc_sync_dma_exit (l->fcmds[i].cmd, 3 * sizeof(logi), fc); + p = (fc_al_posmap *)(l->logi + 3 * i); +#ifdef FCDEBUG + { + u32 *u = (u32 *)p; + FCD(("%08x\n", u[0])) + u ++; + FCD(("%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x\n", u[0],u[1],u[2],u[3],u[4],u[5],u[6],u[7])) + } +#endif + if ((p->magic & 0xffff0000) != FC_AL_LILP || !p->len) { + printk ("FC: Bad magic from REPORT_AL_MAP on %s - %08x\n", fc->name, p->magic); + fc->state = FC_STATE_OFFLINE; + } else { + fc->posmap = (fcp_posmap *)kmalloc(sizeof(fcp_posmap)+p->len, GFP_KERNEL); + if (!fc->posmap) { + printk("FC: Not enough memory, offlining channel\n"); + fc->state = FC_STATE_OFFLINE; + } else { + int k; + memset(fc->posmap, 0, sizeof(fcp_posmap)+p->len); + /* FIXME: This is where SOCAL transfers our AL-PA. + Keep it here till we found out what other cards do... */ + fc->sid = (p->magic & 0xff); + for (i = 0; i < p->len; i++) + if (p->alpa[i] == fc->sid) + break; + k = p->len; + if (i == p->len) + i = 0; + else { + p->len--; + i++; + } + fc->posmap->len = p->len; + for (j = 0; j < p->len; j++) { + if (i == k) i = 0; + fc->posmap->list[j] = p->alpa[i++]; + } + fc->state = FC_STATE_ONLINE; + } + } + printk ("%s: ONLINE\n", fc->name); + if (atomic_dec_and_test (&l->todo)) + up(&l->sem); + break; + case FC_STATUS_POINTTOPOINT: /* We're Point-to-Point, no AL... */ + FCD(("SID %d DID %d\n", fc->sid, fc->did)) + fcmd = l->fcmds + i; + fc_sync_dma_exit(fcmd->cmd, 3 * sizeof(logi), fc); + fch = &fcmd->fch; + memset(l->logi + 3 * i, 0, 3 * sizeof(logi)); + FILL_FCHDR_RCTL_DID(fch, R_CTL_ELS_REQ, FS_FABRIC_F_PORT); + FILL_FCHDR_SID(fch, 0); + FILL_FCHDR_TYPE_FCTL(fch, TYPE_EXTENDED_LS, F_CTL_FIRST_SEQ | F_CTL_SEQ_INITIATIVE); + FILL_FCHDR_SEQ_DF_SEQ(fch, 0, 0, 0); + FILL_FCHDR_OXRX(fch, 0xffff, 0xffff); + fch->param = 0; + l->logi [3 * i].code = LS_FLOGI; + fcmd->cmd = fc_sync_dma_entry (l->logi + 3 * i, 3 * sizeof(logi), fc); + fcmd->rsp = fcmd->cmd + sizeof(logi); + fcmd->cmdlen = sizeof(logi); + fcmd->rsplen = sizeof(logi); + fcmd->data = (dma_handle)NULL; + fcmd->class = FC_CLASS_SIMPLE; + fcmd->proto = TYPE_EXTENDED_LS; + if (fc->hw_enque (fc, fcmd)) + printk ("FC: Cannot enque FLOGI packet on %s\n", fc->name); + break; + case FC_STATUS_ERR_OFFLINE: + fc->state = FC_STATE_MAYBEOFFLINE; + FCD (("FC is offline %d\n", l->grace[i])) + break; + default: + printk ("FLOGI failed for %s with status %d\n", fc->name, status); + /* Do some sort of error recovery here */ + break; + } +} + void fcp_register(fc_channel *fc, u8 type, int unregister) { int size, i; @@ -247,12 +363,13 @@ for (i = fc->can_queue; i < fc->scsi_bitmap_end; i++) set_bit (i, fc->scsi_bitmap); fc->scsi_free = fc->can_queue; - fc->token_tab = (fcp_cmnd **)kmalloc(slots * sizeof(fcp_cmnd*), GFP_KERNEL); + fc->cmd_slots = (fcp_cmnd **)kmalloc(slots * sizeof(fcp_cmnd*), GFP_KERNEL); + memset(fc->cmd_slots, 0, slots * sizeof(fcp_cmnd*)); fc->abort_count = 0; } else { fc->scsi_name[0] = 0; kfree (fc->scsi_bitmap); - kfree (fc->token_tab); + kfree (fc->cmd_slots); FCND(("Unregistering\n")); if (fc->rst_pkt) { if (fc->rst_pkt->eh_state == SCSI_STATE_UNUSED) @@ -279,7 +396,7 @@ int sense_len; int rsp_status; - fcmd = fc->token_tab[token]; + fcmd = fc->cmd_slots[token]; if (!fcmd) return; rsp = (fcp_rsp *) (fc->scsi_rsp_pool + fc->rsp_size * token); SCpnt = SC_FCMND(fcmd); @@ -343,27 +460,47 @@ fcmd->done=NULL; clear_bit(token, fc->scsi_bitmap); fc->scsi_free++; - FCD(("Calling scsi_done with %08lx\n", SCpnt->result)) + FCD(("Calling scsi_done with %08x\n", SCpnt->result)) SCpnt->scsi_done(SCpnt); } void fcp_receive_solicited(fc_channel *fc, int proto, int token, int status, fc_hdr *fch) { + int magic; FCD(("receive_solicited %d %d %d\n", proto, token, status)) switch (proto) { case TYPE_SCSI_FCP: fcp_scsi_receive(fc, token, status, fch); break; case TYPE_EXTENDED_LS: - if (fc->ls && ((ls *)(fc->ls))->magic == LSMAGIC) { + case PROTO_REPORT_AL_MAP: + magic = 0; + if (fc->ls) + magic = ((ls *)(fc->ls))->magic; + if (magic == LSMAGIC) { ls *l = (ls *)fc->ls; int i = (token >= l->count) ? token - l->count : token; /* Let's be sure */ if ((unsigned)i < l->count && l->fcmds[i].fc == fc) { - fcp_login_done(fc, token, status); + if (proto == TYPE_EXTENDED_LS) + fcp_login_done(fc, token, status); + else + fcp_report_map_done(fc, token, status); break; } } + FCD(("fc %p fc->ls %p fc->cmd_slots %p\n", fc, fc->ls, fc->cmd_slots)) + if (proto == TYPE_EXTENDED_LS && !fc->ls && fc->cmd_slots) { + fcp_cmnd *fcmd; + + fcmd = fc->cmd_slots[token]; + if (fcmd && fcmd->ls && ((ls *)(fcmd->ls))->magic == LSEMAGIC) { + lse *l = (lse *)fcmd->ls; + + l->status = status; + up (&l->sem); + } + } break; case PROTO_OFFLINE: if (fc->ls && ((lso *)(fc->ls))->magic == LSOMAGIC) { @@ -405,12 +542,12 @@ FCND(("fcp_inititialize %08lx\n", (long)fcp_init)) FCND(("fc_channels %08lx\n", (long)fc_channels)) FCND((" SID %d DID %d\n", fcchain->sid, fcchain->did)) - l = kmalloc(sizeof (ls) + count * sizeof(int), GFP_KERNEL); + l = kmalloc(sizeof (ls) + count, GFP_KERNEL); if (!l) { printk ("FC: Cannot allocate memory for initialization\n"); return -ENOMEM; } - memset (l, 0, sizeof(ls) + count * sizeof(int)); + memset (l, 0, sizeof(ls) + count); l->magic = LSMAGIC; l->count = count; FCND(("FCP Init for %d channels\n", count)) @@ -429,36 +566,25 @@ } memset (l->logi, 0, count * 3 * sizeof(logi)); memset (l->fcmds, 0, count * sizeof(fcp_cmnd)); - FCND(("Initializing FLOGI packets\n")) for (fc = fcchain, i = 0; fc && i < count; fc = fc->next, i++) { - fc_hdr *fch; - - FCD(("SID %d DID %d\n", fc->sid, fc->did)) fc->state = FC_STATE_UNINITED; + fc->rst_pkt = NULL; /* kmalloc when first used */ + } + /* First try if we are in a AL topology */ + FCND(("Initializing REPORT_MAP packets\n")) + for (fc = fcchain, i = 0; fc && i < count; fc = fc->next, i++) { fcmd = l->fcmds + i; fc->login = fcmd; fc->ls = (void *)l; - fc->rst_pkt = NULL; /* kmalloc when first used */ - fch = &fcmd->fch; - FILL_FCHDR_RCTL_DID(fch, R_CTL_ELS_REQ, FS_FABRIC_F_PORT); - FILL_FCHDR_SID(fch, 0); - FILL_FCHDR_TYPE_FCTL(fch, TYPE_EXTENDED_LS, F_CTL_FIRST_SEQ | F_CTL_SEQ_INITIATIVE); - FILL_FCHDR_SEQ_DF_SEQ(fch, 0, 0, 0); - FILL_FCHDR_OXRX(fch, 0xffff, 0xffff); - fch->param = 0; - l->logi [3 * i].code = LS_FLOGI; + /* Assumes sizeof(fc_al_posmap) < 3 * sizeof(logi), which is true */ fcmd->cmd = fc_sync_dma_entry (l->logi + 3 * i, 3 * sizeof(logi), fc); - fcmd->rsp = fcmd->cmd + sizeof(logi); - fcmd->cmdlen = sizeof(logi); - fcmd->rsplen = sizeof(logi); - fcmd->data = (dma_handle)NULL; - fcmd->class = FC_CLASS_SIMPLE; - fcmd->proto = TYPE_EXTENDED_LS; + fcmd->proto = PROTO_REPORT_AL_MAP; fcmd->token = i; fcmd->fc = fc; } for (retry = 0; retry < 8; retry++) { - FCND(("Sending FLOGI/PLOGI packets\n")) + int nqueued = 0; + FCND(("Sending REPORT_MAP/FLOGI/PLOGI packets\n")) for (fc = fcchain, i = 0; fc && i < count; fc = fc->next, i++) { if (fc->state == FC_STATE_ONLINE || fc->state == FC_STATE_OFFLINE) continue; @@ -477,27 +603,56 @@ } ret = fc->hw_enque (fc, fc->login); enable_irq(fc->irq); - if (ret) printk ("FC: Cannot enque FLOGI packet on %s\n", fc->name); + if (!ret) { + nqueued++; + continue; + } + if (ret == -ENOSYS && fc->login->proto == PROTO_REPORT_AL_MAP) { + /* Oh yes, this card handles Point-to-Point only, so let's try that. */ + fc_hdr *fch; + + FCD(("SID %d DID %d\n", fc->sid, fc->did)) + fcmd = l->fcmds + i; + fc_sync_dma_exit(fcmd->cmd, 3 * sizeof(logi), fc); + fch = &fcmd->fch; + FILL_FCHDR_RCTL_DID(fch, R_CTL_ELS_REQ, FS_FABRIC_F_PORT); + FILL_FCHDR_SID(fch, 0); + FILL_FCHDR_TYPE_FCTL(fch, TYPE_EXTENDED_LS, F_CTL_FIRST_SEQ | F_CTL_SEQ_INITIATIVE); + FILL_FCHDR_SEQ_DF_SEQ(fch, 0, 0, 0); + FILL_FCHDR_OXRX(fch, 0xffff, 0xffff); + fch->param = 0; + l->logi [3 * i].code = LS_FLOGI; + fcmd->cmd = fc_sync_dma_entry (l->logi + 3 * i, 3 * sizeof(logi), fc); + fcmd->rsp = fcmd->cmd + sizeof(logi); + fcmd->cmdlen = sizeof(logi); + fcmd->rsplen = sizeof(logi); + fcmd->data = (dma_handle)NULL; + fcmd->class = FC_CLASS_SIMPLE; + fcmd->proto = TYPE_EXTENDED_LS; + } else + printk ("FC: Cannot enque FLOGI/REPORT_MAP packet on %s\n", fc->name); } - l->timer.expires = jiffies + 5 * HZ; - add_timer(&l->timer); - - down(&l->sem); - if (!atomic_read(&l->todo)) { - FCND(("All channels answered in time\n")) - break; /* All fc channels have answered us */ + if (nqueued) { + l->timer.expires = jiffies + 5 * HZ; + add_timer(&l->timer); + + down(&l->sem); + if (!atomic_read(&l->todo)) { + FCND(("All channels answered in time\n")) + break; /* All fc channels have answered us */ + } } } all_done: - for (fc = fcchain, i = 0; fc && i < count; fc = fc->next, i += 3) { + for (fc = fcchain, i = 0; fc && i < count; fc = fc->next, i++) { + fc->ls = NULL; switch (fc->state) { case FC_STATE_ONLINE: break; case FC_STATE_OFFLINE: break; default: fc_sync_dma_exit (l->fcmds[i].cmd, 3 * sizeof(logi), fc); break; } - fc->ls = NULL; } del_timer(&l->timer); kfree (l->logi); @@ -534,6 +689,7 @@ fcmd = l.fcmds + i; fc->login = fcmd; fc->ls = (void *)&l; + fcmd->did = fc->did; fcmd->class = FC_CLASS_OFFLINE; fcmd->proto = PROTO_OFFLINE; fcmd->token = i; @@ -549,6 +705,8 @@ down(&l.sem); del_timer(&l.timer); + for (fc = fcchain, i = 0; fc && i < count; fc = fc->next, i++) + fc->ls = NULL; kfree (l.fcmds); return 0; } @@ -565,14 +723,14 @@ } ret = fcp_initialize (fcchain, count); - - if (!ret) { - if (!fc_channels) - fc_channels = fcchain; - else { - for (fc = fc_channels; fc->next; fc = fc->next); - fc->next = fcchain; - } + if (ret) + return ret; + + if (!fc_channels) + fc_channels = fcchain; + else { + for (fc = fc_channels; fc->next; fc = fc->next); + fc->next = fcchain; } return ret; } @@ -622,7 +780,7 @@ fcmd->token = i; cmd = fc->scsi_cmd_pool + i; - if (fc->encode_addr (SCpnt, cmd->fcp_addr)) { + if (fc->encode_addr (SCpnt, cmd->fcp_addr, fc, fcmd)) { /* Invalid channel/id/lun and couldn't map it into fcp_addr */ clear_bit (i, fc->scsi_bitmap); SCpnt->result = (DID_BAD_TARGET << 16); @@ -630,7 +788,7 @@ return 0; } fc->scsi_free--; - fc->token_tab[fcmd->token] = fcmd; + fc->cmd_slots[fcmd->token] = fcmd; if (SCpnt->device->tagged_supported) { if (jiffies - fc->ages[SCpnt->channel * fc->targets + SCpnt->target] > (5 * 60 * HZ)) { @@ -670,14 +828,14 @@ memset (cmd->fcp_cdb+SCpnt->cmd_len, 0, sizeof(cmd->fcp_cdb)-SCpnt->cmd_len); FCD(("XXX: %04x.%04x.%04x.%04x - %08x%08x%08x\n", cmd->fcp_addr[0], cmd->fcp_addr[1], cmd->fcp_addr[2], cmd->fcp_addr[3], *(u32 *)SCpnt->cmnd, *(u32 *)(SCpnt->cmnd+4), *(u32 *)(SCpnt->cmnd+8))) } - FCD(("Trying to enque %08x\n", (int)fcmd)) + FCD(("Trying to enque %p\n", fcmd)) if (!fc->scsi_que) { if (!fc->hw_enque (fc, fcmd)) { - FCD(("hw_enque succeeded for %08x\n", (int)fcmd)) + FCD(("hw_enque succeeded for %p\n", fcmd)) return 0; } } - FCD(("Putting into que1 %08x\n", (int)fcmd)) + FCD(("Putting into que1 %p\n", fcmd)) fcp_scsi_insert_queue (fc, fcmd); return 0; } @@ -687,7 +845,7 @@ fcp_cmnd *fcmd = FCP_CMND(SCpnt); fc_channel *fc = FC_SCMND(SCpnt); - FCD(("Entering SCSI queuecommand %08x\n", (int)fcmd)) + FCD(("Entering SCSI queuecommand %p\n", fcmd)) if (SCpnt->done != fcp_scsi_done) { fcmd->done = SCpnt->done; SCpnt->done = fcp_scsi_done; @@ -708,11 +866,12 @@ void fcp_queue_empty(fc_channel *fc) { fcp_cmnd *fcmd; + FCD(("Queue empty\n")) while ((fcmd = fc->scsi_que)) { /* The hw told us we can try again queue some packet */ if (fc->hw_enque (fc, fcmd)) - return; + break; fcp_scsi_remove_queue (fc, fcmd); } } @@ -725,7 +884,7 @@ int fcp_scsi_abort(Scsi_Cmnd *SCpnt) { - /* Internal bookkeeping only. Lose 1 token_tab slot. */ + /* Internal bookkeeping only. Lose 1 cmd_slots slot. */ fcp_cmnd *fcmd = FCP_CMND(SCpnt); fc_channel *fc = FC_SCMND(SCpnt); @@ -733,14 +892,14 @@ * We react to abort requests by simply forgetting * about the command and pretending everything's sweet. * This may or may not be silly. We can't, however, - * immediately reuse the command's token_tab slot, + * immediately reuse the command's cmd_slots slot, * as its result may arrive later and we cannot * check whether it is the aborted one, can't we? * * Therefore, after the first few aborts are done, * we tell the scsi error handler to do something clever. * It will eventually call host reset, refreshing - * token_tab for us. + * cmd_slots for us. * * There is a theoretical chance that we sometimes allow * more than can_queue packets to the jungle this way, @@ -786,13 +945,13 @@ fcmd->token = 0; cmd = fc->scsi_cmd_pool + 0; FCD(("Preparing rst packet\n")) - if (fc->encode_addr (SCpnt, /*?*/cmd->fcp_addr)) + fc->encode_addr (SCpnt, cmd->fcp_addr, fc, fcmd); fc->rst_pkt->channel = SCpnt->channel; fc->rst_pkt->target = SCpnt->target; fc->rst_pkt->lun = 0; fc->rst_pkt->cmd_len = 0; - fc->token_tab[0] = fcmd; + fc->cmd_slots[0] = fcmd; cmd->fcp_cntl = FCP_CNTL_QTYPE_ORDERED | FCP_CNTL_RESET; fcmd->data = (dma_handle)NULL; @@ -859,16 +1018,137 @@ printk ("FC: host reset\n"); for (i=0; i < fc->can_queue; i++) { - if (fc->token_tab[i] && SCpnt->result != DID_ABORT) { + if (fc->cmd_slots[i] && SCpnt->result != DID_ABORT) { SCpnt->result = DID_RESET; fcmd->done(SCpnt); - fc->token_tab[i] = NULL; + fc->cmd_slots[i] = NULL; } } fc->reset(fc); fc->abort_count = 0; if (fcp_initialize(fc, 1)) return SUCCESS; else return FAILED; +} + +static int fcp_els_queue_it(fc_channel *fc, fcp_cmnd *fcmd) +{ + long i; + + i = find_first_zero_bit (fc->scsi_bitmap, fc->scsi_bitmap_end); + set_bit (i, fc->scsi_bitmap); + fcmd->token = i; + fc->scsi_free--; + fc->cmd_slots[fcmd->token] = fcmd; + return fcp_scsi_queue_it(fc, NULL, fcmd, 0); +} + +static int fc_do_els(fc_channel *fc, unsigned int alpa, void *data, int len) +{ + fcp_cmnd _fcmd, *fcmd; + fc_hdr *fch; + lse l; + int i; + + fcmd = &_fcmd; + memset(fcmd, 0, sizeof(fcmd)); + FCD(("PLOGI SID %d DID %d\n", fc->sid, alpa)) + fch = &fcmd->fch; + FILL_FCHDR_RCTL_DID(fch, R_CTL_ELS_REQ, alpa); + FILL_FCHDR_SID(fch, fc->sid); + FILL_FCHDR_TYPE_FCTL(fch, TYPE_EXTENDED_LS, F_CTL_FIRST_SEQ | F_CTL_SEQ_INITIATIVE); + FILL_FCHDR_SEQ_DF_SEQ(fch, 0, 0, 0); + FILL_FCHDR_OXRX(fch, 0xffff, 0xffff); + fch->param = 0; + fcmd->cmd = fc_sync_dma_entry (data, 2 * len, fc); + fcmd->rsp = fcmd->cmd + len; + fcmd->cmdlen = len; + fcmd->rsplen = len; + fcmd->data = (dma_handle)NULL; + fcmd->fc = fc; + fcmd->class = FC_CLASS_SIMPLE; + fcmd->proto = TYPE_EXTENDED_LS; + + memset (&l, 0, sizeof(lse)); + l.magic = LSEMAGIC; + l.sem = MUTEX_LOCKED; + l.timer.function = fcp_login_timeout; + l.timer.data = (unsigned long)&l; + l.status = FC_STATUS_TIMED_OUT; + fcmd->ls = (void *)&l; + + disable_irq(fc->irq); + fcp_els_queue_it(fc, fcmd); + enable_irq(fc->irq); + + for (i = 0;;) { + l.timer.expires = jiffies + 5 * HZ; + add_timer(&l.timer); + down(&l.sem); + del_timer(&l.timer); + if (l.status != FC_STATUS_TIMED_OUT) break; + if (++i == 3) break; + disable_irq(fc->irq); + fcp_scsi_queue_it(fc, NULL, fcmd, 0); + enable_irq(fc->irq); + } + + clear_bit(fcmd->token, fc->scsi_bitmap); + fc->scsi_free++; + fc_sync_dma_exit (fcmd->cmd, 2 * len, fc); + return l.status; +} + +int fc_do_plogi(fc_channel *fc, unsigned char alpa, fc_wwn *node, fc_wwn *nport) +{ + logi *l; + int status; + + l = (logi *)kmalloc(2 * sizeof(logi), GFP_DMA); + if (!l) return -ENOMEM; + memset(l, 0, 2 * sizeof(logi)); + l->code = LS_PLOGI; + memcpy (&l->nport_wwn, &fc->wwn_nport, sizeof(fc_wwn)); + memcpy (&l->node_wwn, &fc->wwn_node, sizeof(fc_wwn)); + memcpy (&l->common, fc->common_svc, sizeof(common_svc_parm)); + memcpy (&l->class1, fc->class_svcs, 3*sizeof(svc_parm)); + status = fc_do_els(fc, alpa, l, sizeof(logi)); + if (status == FC_STATUS_OK) { + if (l[1].code == LS_ACC) { +#ifdef FCDEBUG + u32 *u = (u32 *)&l[1].nport_wwn; + FCD(("AL-PA %02x: Port WWN %08x%08x Node WWN %08x%08x\n", alpa, + u[0], u[1], u[2], u[3])) +#endif + memcpy(nport, &l[1].nport_wwn, sizeof(fc_wwn)); + memcpy(node, &l[1].node_wwn, sizeof(fc_wwn)); + } else + status = FC_STATUS_BAD_RSP; + } + kfree(l); + return status; +} + +typedef struct { + unsigned int code; + unsigned params[4]; +} prli; + +int fc_do_prli(fc_channel *fc, unsigned char alpa) +{ + prli *p; + int status; + + p = (prli *)kmalloc(2 * sizeof(prli), GFP_DMA); + if (!p) return -ENOMEM; + memset(p, 0, 2 * sizeof(prli)); + p->code = LS_PRLI; + p->params[0] = 0x08002000; + p->params[3] = 0x00000022; + status = fc_do_els(fc, alpa, p, sizeof(prli)); + if (status == FC_STATUS_OK && p[1].code != LS_PRLI_ACC && p[1].code != LS_ACC) + status = FC_STATUS_BAD_RSP; + kfree(p); + return status; } #ifdef MODULE diff -u --recursive --new-file v2.2.3/linux/drivers/fc4/fc.h linux/drivers/fc4/fc.h --- v2.2.3/linux/drivers/fc4/fc.h Mon Jan 12 15:19:36 1998 +++ linux/drivers/fc4/fc.h Mon Mar 15 16:11:29 1999 @@ -1,6 +1,6 @@ /* fc.h: Definitions for Fibre Channel Physical and Signaling Interface. * - * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1996-1997,1999 Jakub Jelinek (jj@ultra.linux.cz) * * Sources: * Fibre Channel Physical & Signaling Interface (FC-PH), dpANS, 1994 @@ -11,13 +11,16 @@ #define __FC_H /* World Wide Name */ -#define NAAID_IEEE 1 -#define NAAID_IEEE_EXT 2 -#define NAAID_LOCAL 3 -#define NAAID_IP 4 -#define NAAID_CCITT 12 -#define NAAID_CCITT_GRP 14 +#define NAAID_IEEE 1 +#define NAAID_IEEE_EXT 2 +#define NAAID_LOCAL 3 +#define NAAID_IP 4 +#define NAAID_IEEE_REG 5 +#define NAAID_IEEE_REG_EXT 6 +#define NAAID_CCITT 12 +#define NAAID_CCITT_GRP 14 +/* This is NAAID_IEEE_EXT scheme */ typedef struct { u32 naaid:4; u32 nportid:12; @@ -165,6 +168,7 @@ /* Extended SVC commands */ #define LS_RJT 0x01000000 #define LS_ACC 0x02000000 +#define LS_PRLI_ACC 0x02100014 #define LS_PLOGI 0x03000000 #define LS_FLOGI 0x04000000 #define LS_LOGO 0x05000000 @@ -182,7 +186,11 @@ #define LS_TEST 0x11000000 #define LS_RRQ 0x12000000 #define LS_IDENT 0x20000000 +#define LS_PRLI 0x20100014 #define LS_DISPLAY 0x21000000 +#define LS_PRLO 0x21100014 +#define LS_PDISC 0x50000000 +#define LS_ADISC 0x52000000 typedef struct { u8 fcph_hi, fcph_lo; diff -u --recursive --new-file v2.2.3/linux/drivers/fc4/fc_syms.c linux/drivers/fc4/fc_syms.c --- v2.2.3/linux/drivers/fc4/fc_syms.c Thu Apr 23 20:21:33 1998 +++ linux/drivers/fc4/fc_syms.c Mon Mar 15 16:11:29 1999 @@ -13,7 +13,7 @@ #include #include -#include "fcp_scsi.h" +#include "fcp_impl.h" EXPORT_SYMBOL(fcp_init); EXPORT_SYMBOL(fcp_release); @@ -21,6 +21,8 @@ EXPORT_SYMBOL(fcp_receive_solicited); EXPORT_SYMBOL(fc_channels); EXPORT_SYMBOL(fcp_state_change); +EXPORT_SYMBOL(fc_do_plogi); +EXPORT_SYMBOL(fc_do_prli); /* SCSI stuff */ EXPORT_SYMBOL(fcp_scsi_queuecommand); diff -u --recursive --new-file v2.2.3/linux/drivers/fc4/fcp_impl.h linux/drivers/fc4/fcp_impl.h --- v2.2.3/linux/drivers/fc4/fcp_impl.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/fc4/fcp_impl.h Mon Mar 15 16:11:29 1999 @@ -0,0 +1,168 @@ +/* fcp_impl.h: Generic SCSI on top of FC4 - our interface defines. + * + * Copyright (C) 1997-1999 Jakub Jelinek (jj@ultra.linux.cz) + * Copyright (C) 1998 Jirka Hanika (geo@ff.cuni.cz) + */ + +#ifndef _FCP_SCSI_H +#define _FCP_SCSI_H + +#include +#include +#include "../scsi/scsi.h" + +#include "fc.h" +#include "fcp.h" +#include "fc-al.h" + +#ifdef __sparc__ + +#include +typedef u32 dma_handle; + +#else +#error Need to port FC layer to your architecture +#endif + +/* 0 or 1 */ +#define FCP_SCSI_USE_NEW_EH_CODE 0 + +#define FC_CLASS_OUTBOUND 0x01 +#define FC_CLASS_INBOUND 0x02 +#define FC_CLASS_SIMPLE 0x03 +#define FC_CLASS_IO_WRITE 0x04 +#define FC_CLASS_IO_READ 0x05 +#define FC_CLASS_UNSOLICITED 0x06 +#define FC_CLASS_OFFLINE 0x08 + +#define PROTO_OFFLINE 0x02 +#define PROTO_REPORT_AL_MAP 0x03 +#define PROTO_FORCE_LIP 0x06 + +struct _fc_channel; + +typedef struct fcp_cmnd { + struct fcp_cmnd *next; + struct fcp_cmnd *prev; + void (*done)(Scsi_Cmnd *); + unsigned short proto; + unsigned short token; + unsigned int did; + /* FCP SCSI stuff */ + dma_handle data; + /* From now on this cannot be touched for proto == TYPE_SCSI_FCP */ + fc_hdr fch; + dma_handle cmd; + dma_handle rsp; + int cmdlen; + int rsplen; + int class; + int datalen; + /* This is just used as a verification during login */ + struct _fc_channel *fc; + void *ls; +} fcp_cmnd; + +typedef struct { + unsigned int len; + unsigned char list[0]; +} fcp_posmap; + +typedef struct _fc_channel { + struct _fc_channel *next; + int irq; + int state; + int sid; + int did; + char name[16]; + void (*fcp_register)(struct _fc_channel *, u8, int); + void (*reset)(struct _fc_channel *); + int (*hw_enque)(struct _fc_channel *, fcp_cmnd *); + fc_wwn wwn_node; + fc_wwn wwn_nport; + fc_wwn wwn_dest; + common_svc_parm *common_svc; + svc_parm *class_svcs; +#ifdef __sparc__ + struct linux_sbus_device *dev; +#endif + struct module *module; + /* FCP SCSI stuff */ + short can_queue; + short abort_count; + int rsp_size; + fcp_cmd *scsi_cmd_pool; + char *scsi_rsp_pool; + dma_handle dma_scsi_cmd, dma_scsi_rsp; + long *scsi_bitmap; + long scsi_bitmap_end; + int scsi_free; + int (*encode_addr)(Scsi_Cmnd *, u16 *, struct _fc_channel *, fcp_cmnd *); + fcp_cmnd *scsi_que; + char scsi_name[4]; + fcp_cmnd **cmd_slots; + int channels; + int targets; + long *ages; + Scsi_Cmnd *rst_pkt; + fcp_posmap *posmap; + /* LOGIN stuff */ + fcp_cmnd *login; + void *ls; +} fc_channel; + +extern fc_channel *fc_channels; + +#define FC_STATE_UNINITED 0 +#define FC_STATE_ONLINE 1 +#define FC_STATE_OFFLINE 2 +#define FC_STATE_RESETING 3 +#define FC_STATE_FPORT_OK 4 +#define FC_STATE_MAYBEOFFLINE 5 + +#define FC_STATUS_OK 0 +#define FC_STATUS_P_RJT 2 +#define FC_STATUS_F_RJT 3 +#define FC_STATUS_P_BSY 4 +#define FC_STATUS_F_BSY 5 +#define FC_STATUS_ERR_OFFLINE 0x11 +#define FC_STATUS_TIMEOUT 0x12 +#define FC_STATUS_ERR_OVERRUN 0x13 +#define FC_STATUS_POINTTOPOINT 0x15 +#define FC_STATUS_AL 0x16 +#define FC_STATUS_UNKNOWN_CQ_TYPE 0x20 +#define FC_STATUS_BAD_SEG_CNT 0x21 +#define FC_STATUS_MAX_XCHG_EXCEEDED 0x22 +#define FC_STATUS_BAD_XID 0x23 +#define FC_STATUS_XCHG_BUSY 0x24 +#define FC_STATUS_BAD_POOL_ID 0x25 +#define FC_STATUS_INSUFFICIENT_CQES 0x26 +#define FC_STATUS_ALLOC_FAIL 0x27 +#define FC_STATUS_BAD_SID 0x28 +#define FC_STATUS_NO_SEQ_INIT 0x29 +#define FC_STATUS_TIMED_OUT -1 +#define FC_STATUS_BAD_RSP -2 + +void fcp_queue_empty(fc_channel *); +int fcp_init(fc_channel *); +void fcp_release(fc_channel *fc_chain, int count); +void fcp_receive_solicited(fc_channel *, int, int, int, fc_hdr *); +void fcp_state_change(fc_channel *, int); +int fc_do_plogi(fc_channel *, unsigned char, fc_wwn *, fc_wwn *); +int fc_do_prli(fc_channel *, unsigned char); + +#define for_each_fc_channel(fc) \ + for (fc = fc_channels; fc; fc = fc->next) + +#define for_each_online_fc_channel(fc) \ + for_each_fc_channel(fc) \ + if (fc->state == FC_STATE_ONLINE) + +int fcp_scsi_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *)); +int fcp_old_abort(Scsi_Cmnd *); +int fcp_scsi_abort(Scsi_Cmnd *); +int fcp_scsi_dev_reset(Scsi_Cmnd *); +int fcp_scsi_bus_reset(Scsi_Cmnd *); +int fcp_scsi_host_reset(Scsi_Cmnd *); + +#endif /* !(_FCP_SCSI_H) */ diff -u --recursive --new-file v2.2.3/linux/drivers/fc4/fcp_scsi.h linux/drivers/fc4/fcp_scsi.h --- v2.2.3/linux/drivers/fc4/fcp_scsi.h Tue Dec 22 14:16:55 1998 +++ linux/drivers/fc4/fcp_scsi.h Wed Dec 31 16:00:00 1969 @@ -1,151 +0,0 @@ -/* fcp_scsi.h: Generic SCSI on top of FC4 - interface defines. - * - * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) - * Copyright (C) 1998 Jirka Hanika (geo@ff.cuni.cz) - */ - -#ifndef _FCP_SCSI_H -#define _FCP_SCSI_H - -#include -#include -#include "../scsi/scsi.h" - -#include "fc.h" -#include "fcp.h" - -#ifdef __sparc__ - -#include -typedef u32 dma_handle; - -#else -#error Need to port FC layer to your architecture -#endif - -/* 0 or 1 */ -#define FCP_SCSI_USE_NEW_EH_CODE 0 - -#define FC_CLASS_OUTBOUND 0x01 -#define FC_CLASS_INBOUND 0x02 -#define FC_CLASS_SIMPLE 0x03 -#define FC_CLASS_IO_WRITE 0x04 -#define FC_CLASS_IO_READ 0x05 -#define FC_CLASS_UNSOLICITED 0x06 -#define FC_CLASS_OFFLINE 0x08 - -#define PROTO_OFFLINE 0x02 - -struct _fc_channel; - -typedef struct fcp_cmnd { - struct fcp_cmnd *next; - struct fcp_cmnd *prev; - void (*done)(Scsi_Cmnd *); - int proto; - int token; - /* FCP SCSI stuff */ - dma_handle data; - /* From now on this cannot be touched for proto == TYPE_SCSI_FCP */ - fc_hdr fch; - dma_handle cmd; - dma_handle rsp; - int cmdlen; - int rsplen; - int class; - int datalen; - /* This is just used as a verification during login */ - struct _fc_channel *fc; -} fcp_cmnd; - -typedef struct _fc_channel { - struct _fc_channel *next; - int irq; - int state; - int sid; - int did; - char name[16]; - void (*fcp_register)(struct _fc_channel *, u8, int); - void (*reset)(struct _fc_channel *); - int (*hw_enque)(struct _fc_channel *, fcp_cmnd *); - fc_wwn wwn_node; - fc_wwn wwn_nport; - fc_wwn wwn_dest; - common_svc_parm *common_svc; - svc_parm *class_svcs; -#ifdef __sparc__ - struct linux_sbus_device *dev; -#endif - struct module *module; - /* FCP SCSI stuff */ - short can_queue; - short abort_count; - int rsp_size; - fcp_cmd *scsi_cmd_pool; - char *scsi_rsp_pool; - dma_handle dma_scsi_cmd, dma_scsi_rsp; - long *scsi_bitmap; - long scsi_bitmap_end; - int scsi_free; - int (*encode_addr)(Scsi_Cmnd *cmnd, u16 *addr); - fcp_cmnd *scsi_que; - char scsi_name[4]; - fcp_cmnd **token_tab; - int channels; - int targets; - long *ages; - Scsi_Cmnd *rst_pkt; - /* LOGIN stuff */ - fcp_cmnd *login; - void *ls; -} fc_channel; - -extern fc_channel *fc_channels; - -#define FC_STATE_UNINITED 0 -#define FC_STATE_ONLINE 1 -#define FC_STATE_OFFLINE 2 -#define FC_STATE_RESETING 3 -#define FC_STATE_FPORT_OK 4 -#define FC_STATE_MAYBEOFFLINE 5 - -#define FC_STATUS_OK 0 -#define FC_STATUS_P_RJT 2 -#define FC_STATUS_F_RJT 3 -#define FC_STATUS_P_BSY 4 -#define FC_STATUS_F_BSY 5 -#define FC_STATUS_ERR_OFFLINE 0x11 -#define FC_STATUS_TIMEOUT 0x12 -#define FC_STATUS_ERR_OVERRUN 0x13 -#define FC_STATUS_UNKNOWN_CQ_TYPE 0x20 -#define FC_STATUS_BAD_SEG_CNT 0x21 -#define FC_STATUS_MAX_XCHG_EXCEEDED 0x22 -#define FC_STATUS_BAD_XID 0x23 -#define FC_STATUS_XCHG_BUSY 0x24 -#define FC_STATUS_BAD_POOL_ID 0x25 -#define FC_STATUS_INSUFFICIENT_CQES 0x26 -#define FC_STATUS_ALLOC_FAIL 0x27 -#define FC_STATUS_BAD_SID 0x28 -#define FC_STATUS_NO_SEQ_INIT 0x29 - -void fcp_queue_empty(fc_channel *); -int fcp_init(fc_channel *); -void fcp_release(fc_channel *fc_chain, int count); -void fcp_receive_solicited(fc_channel *, int, int, int, fc_hdr *); -void fcp_state_change(fc_channel *, int); - -#define for_each_fc_channel(fc) \ - for (fc = fc_channels; fc; fc = fc->next) - -#define for_each_online_fc_channel(fc) \ - for_each_fc_channel(fc) \ - if (fc->state == FC_STATE_ONLINE) - -int fcp_scsi_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *)); -int fcp_old_abort(Scsi_Cmnd *); -int fcp_scsi_abort(Scsi_Cmnd *); -int fcp_scsi_dev_reset(Scsi_Cmnd *); -int fcp_scsi_bus_reset(Scsi_Cmnd *); -int fcp_scsi_host_reset(Scsi_Cmnd *); - -#endif /* !(_FCP_SCSI_H) */ diff -u --recursive --new-file v2.2.3/linux/drivers/fc4/soc.c linux/drivers/fc4/soc.c --- v2.2.3/linux/drivers/fc4/soc.c Tue Dec 22 14:16:55 1998 +++ linux/drivers/fc4/soc.c Mon Mar 15 16:11:29 1999 @@ -1,6 +1,6 @@ /* soc.c: Sparc SUNW,soc (Serial Optical Channel) Fibre Channel Sbus adapter support. * - * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1996,1997,1999 Jakub Jelinek (jj@ultra.linux.cz) * Copyright (C) 1997,1998 Jirka Hanika (geo@ff.cuni.cz) * * Sources: @@ -9,8 +9,6 @@ * * Supported hardware: * Tested on SOC sbus card bought with SS1000 in Linux running on SS5 and Ultra1. - * Should run on on-board SOC/SOC+ cards of Ex000 servers as well, but it is not - * tested (let us know if you succeed). * For SOC sbus cards, you have to make sure your FCode is 1.52 or later. * If you have older FCode, you should try to upgrade or get SOC microcode from Sun * (the microcode is present in Solaris soc driver as well). In that case you need @@ -20,7 +18,7 @@ */ static char *version = - "soc.c:v1.2 27/Feb/98 Jakub Jelinek (jj@sunsite.mff.cuni.cz), Jirka Hanika (geo@ff.cuni.cz)\n"; + "soc.c:v1.3 9/Feb/99 Jakub Jelinek (jj@ultra.linux.cz), Jirka Hanika (geo@ff.cuni.cz)\n"; #include #include @@ -34,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -51,10 +48,10 @@ /* #define SOCDEBUG */ /* #define HAVE_SOC_UCODE */ -#include "fcp_scsi.h" +#include "fcp_impl.h" #include "soc.h" #ifdef HAVE_SOC_UCODE -#include "soc_asm.c" +#include "soc_asm.h" #endif #define soc_printk printk ("soc%d: ", s->soc_no); printk @@ -397,6 +394,10 @@ FILL_FCHDR_OXRX(fch, 0xffff, 0xffff); request->shdr.flags = port->flags; break; + + case PROTO_REPORT_AL_MAP: + /* SOC only supports Point-to-Point topology, no FC-AL, sorry... */ + return -ENOSYS; default: request->shdr.token = TOKEN(fcmd->proto, port->mask, fcmd->token); diff -u --recursive --new-file v2.2.3/linux/drivers/fc4/soc.h linux/drivers/fc4/soc.h --- v2.2.3/linux/drivers/fc4/soc.h Mon Jan 12 15:19:36 1998 +++ linux/drivers/fc4/soc.h Mon Mar 15 16:11:29 1999 @@ -8,7 +8,7 @@ #include "fc.h" #include "fcp.h" -#include "fcp_scsi.h" +#include "fcp_impl.h" /* Hardware structures and constants first {{{ */ diff -u --recursive --new-file v2.2.3/linux/drivers/fc4/socal.c linux/drivers/fc4/socal.c --- v2.2.3/linux/drivers/fc4/socal.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/fc4/socal.c Mon Mar 15 16:11:29 1999 @@ -0,0 +1,854 @@ +/* socal.c: Sparc SUNW,socal (SOC+) Fibre Channel Sbus adapter support. + * + * Copyright (C) 1998,1999 Jakub Jelinek (jj@ultra.linux.cz) + * + * Sources: + * Fibre Channel Physical & Signaling Interface (FC-PH), dpANS, 1994 + * dpANS Fibre Channel Protocol for SCSI (X3.269-199X), Rev. 012, 1995 + * SOC+ Programming Guide 0.1 + * Fibre Channel Arbitrated Loop (FC-AL), dpANS rev. 4.5, 1995 + * + * Supported hardware: + * On-board SOC+ adapters of Ultra Enterprise servers and sun4d. + */ + +static char *version = + "socal.c: SOC+ driver v1.1 9/Feb/99 Jakub Jelinek (jj@ultra.linux.cz)\n"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* #define SOCALDEBUG */ +/* #define HAVE_SOCAL_UCODE */ +/* #define USE_64BIT_MODE */ + +#include "fcp_impl.h" +#include "socal.h" +#ifdef HAVE_SOCAL_UCODE +#include "socal_asm.h" +#endif + +#define socal_printk printk ("socal%d: ", s->socal_no); printk + +#ifdef SOCALDEBUG +#define SOD(x) socal_printk x; +#else +#define SOD(x) +#endif + +#define for_each_socal(s) for (s = socals; s; s = s->next) +struct socal *socals = NULL; + +/* I don't think our VIS mem* routines will behave well + in IO... */ +static void socal_memcpy(void *d, void *s, int size) +{ + u32 *dp = (u32 *)d, *sp = (u32 *)s; + while (size) { + *dp++ = *sp++; + size -= sizeof(u32); + } +} + +static void socal_bzero(void *d, int size) +{ + u32 *dp = (u32 *)d; + while (size) { + *dp++ = 0; + size -= sizeof(u32); + } +} + +static inline void socal_disable(struct socal *s) +{ + s->regs->imask = 0; s->regs->cmd = SOCAL_CMD_SOFT_RESET; +} + +static inline void socal_enable(struct socal *s) +{ + SOD(("enable %08x\n", s->cfg)) + s->regs->sae = 0; s->regs->cfg = s->cfg; + s->regs->cmd = SOCAL_CMD_RSP_QALL; + SOCAL_SETIMASK(s, SOCAL_IMASK_RSP_QALL | SOCAL_IMASK_SAE); + SOD(("imask %08x %08x\n", s->imask, s->regs->imask)); +} + +static void socal_reset(fc_channel *fc) +{ + socal_port *port = (socal_port *)fc; + struct socal *s = port->s; + + /* FIXME */ + socal_disable(s); + s->req[0].seqno = 1; + s->req[1].seqno = 1; + s->rsp[0].seqno = 1; + s->rsp[1].seqno = 1; + s->req[0].in = 0; + s->req[1].in = 0; + s->rsp[0].in = 0; + s->rsp[1].in = 0; + s->req[0].out = 0; + s->req[1].out = 0; + s->rsp[0].out = 0; + s->rsp[1].out = 0; + + /* FIXME */ + socal_enable(s); +} + +static void inline socal_solicited (struct socal *s, int qno) +{ + fc_hdr fchdr; + socal_rsp *hwrsp; + socal_cq *sw_cq; + int token; + int status; + fc_channel *fc; + + sw_cq = &s->rsp[qno]; + + if (sw_cq->pool == NULL) { + SOD(("address %08x xram %p\n", sw_cq->hw_cq->address, s->xram)) + sw_cq->pool = + (socal_req *)(s->xram + (sw_cq->hw_cq->address & 0xfffe)); + } + /* Finally an improvement against old SOC :) */ + sw_cq->in = s->regs->respr[qno]; + SOD (("socal_solicited, %d packets arrived\n", (sw_cq->in - sw_cq->out) & sw_cq->last)) + for (;;) { + hwrsp = (socal_rsp *)sw_cq->pool + sw_cq->out; + SOD(("hwrsp %p out %d\n", hwrsp, sw_cq->out)) + +#if defined(SOCALDEBUG) && 0 + { + u32 *u = (u32 *)hwrsp; + SOD(("%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x\n", u[0],u[1],u[2],u[3],u[4],u[5],u[6],u[7])) + u += 8; + SOD(("%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x\n", u[0],u[1],u[2],u[3],u[4],u[5],u[6],u[7])) + u = (u32 *)s->xram; + while (u < ((u32 *)s->regs)) { + if (u[0] == 0x00003000 || u[0] == 0x00003801) { + SOD(("Found at %04lx\n", (unsigned long)u - (unsigned long)s->xram)) + SOD((" %08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x\n", u[0],u[1],u[2],u[3],u[4],u[5],u[6],u[7])) + u += 8; + SOD((" %08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x\n", u[0],u[1],u[2],u[3],u[4],u[5],u[6],u[7])) + u -= 8; + } + u++; + } + } +#endif + + token = hwrsp->shdr.token; + status = hwrsp->status; + fc = (fc_channel *)(&s->port[(token >> 11) & 1]); + + SOD(("Solicited token %08x status %08x\n", token, status)) + if (status == SOCAL_OK) + fcp_receive_solicited(fc, token >> 12, token & ((1 << 11) - 1), FC_STATUS_OK, NULL); + else { + socal_memcpy(&fchdr, &hwrsp->fchdr, sizeof(fchdr)); + /* We have intentionally defined FC_STATUS_* constants to match SOCAL_* constants, otherwise + we'd have to translate status */ + fcp_receive_solicited(fc, token >> 12, token & ((1 << 11) - 1), status, &fchdr); + } + + if (++sw_cq->out > sw_cq->last) { + sw_cq->seqno++; + sw_cq->out = 0; + } + + if (sw_cq->out == sw_cq->in) { + sw_cq->in = s->regs->respr[qno]; + if (sw_cq->out == sw_cq->in) { + /* Tell the hardware about it */ + s->regs->cmd = (sw_cq->out << 24) | (SOCAL_CMD_RSP_QALL & ~(SOCAL_CMD_RSP_Q0 << qno)); + /* Read it, so that we're sure it has been updated */ + s->regs->cmd; + sw_cq->in = s->regs->respr[qno]; + if (sw_cq->out == sw_cq->in) + break; + } + } + } +} + +static void inline socal_request (struct socal *s, u32 cmd) +{ + SOCAL_SETIMASK(s, s->imask & ~(cmd & SOCAL_CMD_REQ_QALL)); + SOD(("imask %08x %08x\n", s->imask, s->regs->imask)); + + SOD(("Queues available %08x OUT %X\n", cmd, s->regs->reqpr[0])) + if (s->port[s->curr_port].fc.state != FC_STATE_OFFLINE) { + fcp_queue_empty ((fc_channel *)&(s->port[s->curr_port])); + if (((s->req[1].in + 1) & s->req[1].last) != (s->req[1].out)) + fcp_queue_empty ((fc_channel *)&(s->port[1 - s->curr_port])); + } else + fcp_queue_empty ((fc_channel *)&(s->port[1 - s->curr_port])); + if (s->port[1 - s->curr_port].fc.state != FC_STATE_OFFLINE) + s->curr_port ^= 1; +} + +static void inline socal_unsolicited (struct socal *s, int qno) +{ + socal_rsp *hwrsp, *hwrspc; + socal_cq *sw_cq; + int count; + int status; + int flags; + fc_channel *fc; + + sw_cq = &s->rsp[qno]; + if (sw_cq->pool == NULL) { + SOD(("address %08x xram %p\n", sw_cq->hw_cq->address, s->xram)) + sw_cq->pool = + (socal_req *)(s->xram + (sw_cq->hw_cq->address & 0xfffe)); + } + + sw_cq->in = s->regs->respr[qno]; + SOD (("socal_unsolicited, %d packets arrived, in %d\n", (sw_cq->in - sw_cq->out) & sw_cq->last, sw_cq->in)) + while (sw_cq->in != sw_cq->out) { + /* ...real work per entry here... */ + hwrsp = (socal_rsp *)sw_cq->pool + sw_cq->out; + SOD(("hwrsp %p out %d\n", hwrsp, sw_cq->out)) + +#if defined(SOCALDEBUG) && 0 + { + u32 *u = (u32 *)hwrsp; + SOD(("%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x\n", u[0],u[1],u[2],u[3],u[4],u[5],u[6],u[7])) + u += 8; + SOD(("%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x\n", u[0],u[1],u[2],u[3],u[4],u[5],u[6],u[7])) + } +#endif + + hwrspc = NULL; + flags = hwrsp->shdr.flags; + count = hwrsp->count; + fc = (fc_channel *)&s->port[flags & SOCAL_PORT_B]; + SOD(("FC %08lx\n", (long)fc)) + + if (count != 1) { + /* Ugh, continuation entries */ + u8 in; + + if (count != 2) { + printk("%s: Too many continuations entries %d\n", fc->name, count); + goto update_out; + } + + in = sw_cq->in; + if (in < sw_cq->out) in += sw_cq->last + 1; + if (in < sw_cq->out + 2) { + /* Ask the hardware about it if they haven't arrived yet */ + s->regs->cmd = (sw_cq->out << 24) | (SOCAL_CMD_RSP_QALL & ~(SOCAL_CMD_RSP_Q0 << qno)); + /* Read it, so that we're sure it has been updated */ + s->regs->cmd; + sw_cq->in = s->regs->respr[qno]; + in = sw_cq->in; + if (in < sw_cq->out) in += sw_cq->last + 1; + if (in < sw_cq->out + 2) /* Nothing came, let us wait */ + return; + } + if (sw_cq->out == sw_cq->last) + hwrspc = (socal_rsp *)sw_cq->pool; + else + hwrspc = hwrsp + 1; + } + + switch (flags & ~SOCAL_PORT_B) { + case SOCAL_STATUS: + status = hwrsp->status; + switch (status) { + case SOCAL_ONLINE: + SOD(("State change to ONLINE\n")); + fcp_state_change(fc, FC_STATE_ONLINE); + break; + case SOCAL_ONLINE_LOOP: + SOD(("State change to ONLINE_LOOP\n")); + fcp_state_change(fc, FC_STATE_ONLINE); + break; + case SOCAL_OFFLINE: + SOD(("State change to OFFLINE\n")); + fcp_state_change(fc, FC_STATE_OFFLINE); + break; + default: + printk ("%s: Unknown STATUS no %d\n", fc->name, status); + break; + } + break; + case (SOCAL_UNSOLICITED|SOCAL_FC_HDR): + { + int r_ctl = *((u8 *)&hwrsp->fchdr); + unsigned len; + char buf[64]; + + if ((r_ctl & 0xf0) == R_CTL_EXTENDED_SVC) { + len = hwrsp->shdr.bytecnt; + if (len < 4 || !hwrspc) + printk ("%s: Invalid R_CTL %02x continuation entries\n", fc->name, r_ctl); + else { + if (len > 60) len = 60; + socal_memcpy (buf, hwrspc, (len + 3) & ~3); + if (*(u32 *)buf == LS_DISPLAY) { + int i; + + for (i = 4; i < len; i++) + if (buf[i] == '\n') buf[i] = ' '; + buf[len] = 0; + printk ("%s message: %s\n", fc->name, buf + 4); + } else { + printk ("%s: Unknown LS_CMD %08x\n", fc->name, *(u32 *)buf); + } + } + } else + printk ("%s: Unsolicited R_CTL %02x not handled\n", fc->name, r_ctl); + } + break; + default: + printk ("%s: Unexpected flags %08x\n", fc->name, flags); + break; + } +update_out: + if (++sw_cq->out > sw_cq->last) { + sw_cq->seqno++; + sw_cq->out = 0; + } + + if (hwrspc) { + if (++sw_cq->out > sw_cq->last) { + sw_cq->seqno++; + sw_cq->out = 0; + } + } + + if (sw_cq->out == sw_cq->in) { + sw_cq->in = s->regs->respr[qno]; + if (sw_cq->out == sw_cq->in) { + /* Tell the hardware about it */ + s->regs->cmd = (sw_cq->out << 24) | (SOCAL_CMD_RSP_QALL & ~(SOCAL_CMD_RSP_Q0 << qno)); + /* Read it, so that we're sure it has been updated */ + s->regs->cmd; + sw_cq->in = s->regs->respr[qno]; + } + } + } +} + +static void socal_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + u32 cmd; + unsigned long flags; + register struct socal *s = (struct socal *)dev_id; + + spin_lock_irqsave(&io_request_lock, flags); + cmd = s->regs->cmd; + for (; (cmd = SOCAL_INTR (s, cmd)); cmd = s->regs->cmd) { +#ifdef SOCALDEBUG + static int cnt = 0; + if (cnt++ < 50) printk("soc_intr %08x\n", cmd); +#endif + if (cmd & SOCAL_CMD_RSP_Q2) socal_unsolicited (s, SOCAL_UNSOLICITED_RSP_Q); + if (cmd & SOCAL_CMD_RSP_Q1) socal_unsolicited (s, SOCAL_SOLICITED_BAD_RSP_Q); + if (cmd & SOCAL_CMD_RSP_Q0) socal_solicited (s, SOCAL_SOLICITED_RSP_Q); + if (cmd & SOCAL_CMD_REQ_QALL) socal_request (s, cmd); + } + spin_unlock_irqrestore(&io_request_lock, flags); +} + +#define TOKEN(proto, port, token) (((proto)<<12)|(token)|(port)) + +static int socal_hw_enque (fc_channel *fc, fcp_cmnd *fcmd) +{ + socal_port *port = (socal_port *)fc; + struct socal *s = port->s; + int qno; + socal_cq *sw_cq; + int cq_next_in; + socal_req *request; + fc_hdr *fch; + int i; + + if (fcmd->proto == TYPE_SCSI_FCP) + qno = 1; + else + qno = 0; + SOD(("Putting a FCP packet type %d into hw queue %d\n", fcmd->proto, qno)) + if (s->imask & (SOCAL_IMASK_REQ_Q0 << qno)) { + SOD(("EIO %08x\n", s->imask)) + return -EIO; + } + sw_cq = s->req + qno; + cq_next_in = (sw_cq->in + 1) & sw_cq->last; + + if (cq_next_in == sw_cq->out + && cq_next_in == (sw_cq->out = s->regs->reqpr[qno])) { + SOD(("%d IN %d OUT %d LAST %d\n", qno, sw_cq->in, sw_cq->out, sw_cq->last)) + SOCAL_SETIMASK(s, s->imask | (SOCAL_IMASK_REQ_Q0 << qno)); + SOD(("imask %08x %08x\n", s->imask, s->regs->imask)); + /* If queue is full, just say NO */ + return -EBUSY; + } + + request = sw_cq->pool + sw_cq->in; + fch = &request->fchdr; + + switch (fcmd->proto) { + case TYPE_SCSI_FCP: + request->shdr.token = TOKEN(TYPE_SCSI_FCP, port->mask, fcmd->token); + request->data[0].base = fc->dma_scsi_cmd + fcmd->token * sizeof(fcp_cmd); + request->data[0].count = sizeof(fcp_cmd); + request->data[1].base = fc->dma_scsi_rsp + fcmd->token * fc->rsp_size; + request->data[1].count = fc->rsp_size; + if (fcmd->data) { + request->shdr.segcnt = 3; + i = fc->scsi_cmd_pool[fcmd->token].fcp_data_len; + request->shdr.bytecnt = i; + request->data[2].base = fcmd->data; + request->data[2].count = i; + request->type = (fc->scsi_cmd_pool[fcmd->token].fcp_cntl & FCP_CNTL_WRITE) ? + SOCAL_CQTYPE_IO_WRITE : SOCAL_CQTYPE_IO_READ; + } else { + request->shdr.segcnt = 2; + request->shdr.bytecnt = 0; + request->data[2].base = 0; + request->data[2].count = 0; + request->type = SOCAL_CQTYPE_SIMPLE; + } + FILL_FCHDR_RCTL_DID(fch, R_CTL_COMMAND, fcmd->did); + FILL_FCHDR_SID(fch, fc->sid); + FILL_FCHDR_TYPE_FCTL(fch, TYPE_SCSI_FCP, F_CTL_FIRST_SEQ | F_CTL_SEQ_INITIATIVE); + FILL_FCHDR_SEQ_DF_SEQ(fch, 0, 0, 0); + FILL_FCHDR_OXRX(fch, 0xffff, 0xffff); + fch->param = 0; + request->shdr.flags = port->flags; + request->shdr.class = fc->posmap ? 3 : 2; + break; + + case PROTO_OFFLINE: + memset (request, 0, sizeof(*request)); + request->shdr.token = TOKEN(PROTO_OFFLINE, port->mask, fcmd->token); + request->type = SOCAL_CQTYPE_OFFLINE; + FILL_FCHDR_RCTL_DID(fch, R_CTL_COMMAND, fcmd->did); + FILL_FCHDR_SID(fch, fc->sid); + FILL_FCHDR_TYPE_FCTL(fch, TYPE_SCSI_FCP, F_CTL_FIRST_SEQ | F_CTL_SEQ_INITIATIVE); + FILL_FCHDR_SEQ_DF_SEQ(fch, 0, 0, 0); + FILL_FCHDR_OXRX(fch, 0xffff, 0xffff); + request->shdr.flags = port->flags; + break; + + case PROTO_REPORT_AL_MAP: + memset (request, 0, sizeof(*request)); + request->shdr.token = TOKEN(PROTO_REPORT_AL_MAP, port->mask, fcmd->token); + request->type = SOCAL_CQTYPE_REPORT_MAP; + request->shdr.flags = port->flags; + request->shdr.segcnt = 1; + request->shdr.bytecnt = sizeof(fc_al_posmap); + request->data[0].base = fcmd->cmd; + request->data[0].count = sizeof(fc_al_posmap); + break; + + default: + request->shdr.token = TOKEN(fcmd->proto, port->mask, fcmd->token); + request->shdr.class = fc->posmap ? 3 : 2; + request->shdr.flags = port->flags; + memcpy (fch, &fcmd->fch, sizeof(fc_hdr)); + request->data[0].count = fcmd->cmdlen; + request->data[1].count = fcmd->rsplen; + request->type = fcmd->class; + switch (fcmd->class) { + case FC_CLASS_OUTBOUND: + request->data[0].base = fcmd->cmd; + request->data[0].count = fcmd->cmdlen; + request->type = SOCAL_CQTYPE_OUTBOUND; + request->shdr.bytecnt = fcmd->cmdlen; + request->shdr.segcnt = 1; + break; + case FC_CLASS_INBOUND: + request->data[0].base = fcmd->rsp; + request->data[0].count = fcmd->rsplen; + request->type = SOCAL_CQTYPE_INBOUND; + request->shdr.bytecnt = 0; + request->shdr.segcnt = 1; + break; + case FC_CLASS_SIMPLE: + request->data[0].base = fcmd->cmd; + request->data[1].base = fcmd->rsp; + request->data[0].count = fcmd->cmdlen; + request->data[1].count = fcmd->rsplen; + request->type = SOCAL_CQTYPE_SIMPLE; + request->shdr.bytecnt = fcmd->cmdlen; + request->shdr.segcnt = 2; + break; + case FC_CLASS_IO_READ: + case FC_CLASS_IO_WRITE: + request->data[0].base = fcmd->cmd; + request->data[1].base = fcmd->rsp; + request->data[0].count = fcmd->cmdlen; + request->data[1].count = fcmd->rsplen; + request->type = (fcmd->class == FC_CLASS_IO_READ) ? SOCAL_CQTYPE_IO_READ : SOCAL_CQTYPE_IO_WRITE; + if (fcmd->data) { + request->data[2].base = fcmd->data; + request->data[2].count = fcmd->datalen; + request->shdr.bytecnt = fcmd->datalen; + request->shdr.segcnt = 3; + } else { + request->shdr.bytecnt = 0; + request->shdr.segcnt = 2; + } + break; + } + break; + } + + request->count = 1; + request->flags = 0; + request->seqno = sw_cq->seqno; + + SOD(("queueing token %08x\n", request->shdr.token)) + + /* And now tell the SOCAL about it */ + + if (++sw_cq->in > sw_cq->last) { + sw_cq->in = 0; + sw_cq->seqno++; + } + + SOD(("Putting %08x into cmd\n", SOCAL_CMD_RSP_QALL | (sw_cq->in << 24) | (SOCAL_CMD_REQ_Q0 << qno))) + + s->regs->cmd = SOCAL_CMD_RSP_QALL | (sw_cq->in << 24) | (SOCAL_CMD_REQ_Q0 << qno); + /* Read so that command is completed */ + s->regs->cmd; + + return 0; +} + +static inline void socal_download_fw(struct socal *s) +{ +#ifdef HAVE_SOCAL_UCODE + SOD(("Loading %ld bytes from %p to %p\n", sizeof(socal_ucode), socal_ucode, s->xram)) + socal_memcpy (s->xram, socal_ucode, sizeof(socal_ucode)); + SOD(("Clearing the rest of memory\n")) + socal_bzero (s->xram + sizeof(socal_ucode), 65536 - sizeof(socal_ucode)); + SOD(("Done\n")) +#endif +} + +/* Check for what the best SBUS burst we can use happens + * to be on this machine. + */ +static inline void socal_init_bursts(struct socal *s, struct linux_sbus_device *sdev) +{ + int bsizes, bsizes_more; + u32 cfg; + + bsizes = (prom_getintdefault(sdev->prom_node,"burst-sizes",0xff) & 0xff); + bsizes_more = (prom_getintdefault(sdev->my_bus->prom_node, "burst-sizes", 0xff) & 0xff); + bsizes &= bsizes_more; +#ifdef USE_64BIT_MODE +#ifdef __sparc_v9__ + mmu_set_sbus64(sdev, bsizes >> 16); +#endif +#endif + if ((bsizes & 0x7f) == 0x7f) + cfg = SOCAL_CFG_BURST_64; + else if ((bsizes & 0x3f) == 0x3f) + cfg = SOCAL_CFG_BURST_32; + else if ((bsizes & 0x1f) == 0x1f) + cfg = SOCAL_CFG_BURST_16; + else + cfg = SOCAL_CFG_BURST_4; +#ifdef USE_64BIT_MODE +#ifdef __sparc_v9__ + /* What is BURST_128? -jj */ + if ((bsizes & 0x780000) == 0x780000) + cfg |= (SOCAL_CFG_BURST_64 << 8) | SOCAL_CFG_SBUS_ENHANCED; + else if ((bsizes & 0x380000) == 0x380000) + cfg |= (SOCAL_CFG_BURST_32 << 8) | SOCAL_CFG_SBUS_ENHANCED; + else if ((bsizes & 0x180000) == 0x180000) + cfg |= (SOCAL_CFG_BURST_16 << 8) | SOCAL_CFG_SBUS_ENHANCED; + else + cfg |= (SOCAL_CFG_BURST_8 << 8) | SOCAL_CFG_SBUS_ENHANCED; +#endif +#endif + s->cfg = cfg; +} + +static inline void socal_init(struct linux_sbus_device *sdev, int no) +{ + unsigned char tmp[60]; + int propl; + struct socal *s; + static unsigned version_printed = 0; + socal_hw_cq cq[8]; + int size, i; + int irq, node; + + s = kmalloc (sizeof (struct socal), GFP_KERNEL); + if (!s) return; + memset (s, 0, sizeof(struct socal)); + s->socal_no = no; + + SOD(("socals %08lx socal_intr %08lx socal_hw_enque %08lx\n", (long)socals, (long)socal_intr, (long)socal_hw_enque)) + if (version_printed++ == 0) + printk (version); +#ifdef MODULE + s->port[0].fc.module = &__this_module; + s->port[1].fc.module = &__this_module; +#else + s->port[0].fc.module = NULL; + s->port[1].fc.module = NULL; +#endif + + s->next = socals; + socals = s; + s->port[0].fc.dev = sdev; + s->port[1].fc.dev = sdev; + s->port[0].s = s; + s->port[1].s = s; + + s->port[0].fc.next = &s->port[1].fc; + + /* World Wide Name of SOCAL */ + propl = prom_getproperty (sdev->prom_node, "wwn", tmp, sizeof(tmp)); + if (propl != sizeof (fc_wwn)) { + s->wwn.naaid = NAAID_IEEE_REG; + s->wwn.nportid = 0x123; + s->wwn.hi = 0x1234; + s->wwn.lo = 0x12345678; + } else + memcpy (&s->wwn, tmp, sizeof (fc_wwn)); + + memcpy (&s->port[0].fc.wwn_nport, &s->wwn, sizeof (fc_wwn)); + s->port[0].fc.wwn_nport.lo++; + memcpy (&s->port[1].fc.wwn_nport, &s->wwn, sizeof (fc_wwn)); + s->port[1].fc.wwn_nport.lo+=2; + + node = prom_getchild (sdev->prom_node); + while (node && (node = prom_searchsiblings (node, "sf"))) { + int port; + + port = prom_getintdefault(node, "port#", -1); + switch (port) { + case 0: + case 1: + if (prom_getproplen(node, "port-wwn") == sizeof (fc_wwn)) + prom_getproperty (node, "port-wwn", + (char *)&s->port[port].fc.wwn_nport, + sizeof (fc_wwn)); + break; + default: + break; + } + node = prom_getsibling(node); + } + + memcpy (&s->port[0].fc.wwn_node, &s->wwn, sizeof (fc_wwn)); + memcpy (&s->port[1].fc.wwn_node, &s->wwn, sizeof (fc_wwn)); + SOD(("Got wwns %08x%08x ports %08x%08x and %08x%08x\n", + *(u32 *)&s->port[0].fc.wwn_node, s->port[0].fc.wwn_node.lo, + *(u32 *)&s->port[0].fc.wwn_nport, s->port[0].fc.wwn_nport.lo, + *(u32 *)&s->port[1].fc.wwn_nport, s->port[1].fc.wwn_nport.lo)) + + s->port[0].fc.sid = 1; + s->port[1].fc.sid = 17; + s->port[0].fc.did = 2; + s->port[1].fc.did = 18; + + s->port[0].fc.reset = socal_reset; + s->port[1].fc.reset = socal_reset; + + /* Setup the reg property for this device. */ + prom_apply_sbus_ranges(sdev->my_bus, sdev->reg_addrs, sdev->num_registers, sdev); + + if (sdev->num_registers == 1) { + s->eeprom = (u8 *) + sparc_alloc_io (sdev->reg_addrs [0].phys_addr, 0, + sdev->reg_addrs [0].reg_size, "socal_xram", + sdev->reg_addrs [0].which_io, 0); + if (sdev->reg_addrs [0].reg_size > 0x20000) + s->xram = s->eeprom + 0x10000; + else + s->xram = s->eeprom; + s->regs = (struct socal_regs *)(s->xram + 0x10000); + } else { + /* E.g. starfire presents 3 registers for SOCAL */ + s->xram = (u8 *) + sparc_alloc_io (sdev->reg_addrs [1].phys_addr, 0, + sdev->reg_addrs [1].reg_size, "socal_xram", + sdev->reg_addrs [1].which_io, 0); + s->regs = (struct socal_regs *) + sparc_alloc_io (sdev->reg_addrs [2].phys_addr, 0, + sdev->reg_addrs [2].reg_size, "socal_regs", + sdev->reg_addrs [2].which_io, 0); + } + + socal_init_bursts(s, sdev); + + SOD(("Disabling SOCAL\n")) + + socal_disable (s); + + irq = sdev->irqs[0]; + + if (request_irq (irq, socal_intr, SA_SHIRQ, "SOCAL", (void *)s)) { + socal_printk ("Cannot order irq %d to go\n", irq); + socals = s->next; + return; + } + + SOD(("SOCAL uses IRQ %s\n", __irq_itoa(irq))) + + s->port[0].fc.irq = irq; + s->port[1].fc.irq = irq; + + sprintf (s->port[0].fc.name, "socal%d port A", no); + sprintf (s->port[1].fc.name, "socal%d port B", no); + s->port[0].flags = SOCAL_FC_HDR | SOCAL_PORT_A; + s->port[1].flags = SOCAL_FC_HDR | SOCAL_PORT_B; + s->port[1].mask = (1 << 11); + + s->port[0].fc.hw_enque = socal_hw_enque; + s->port[1].fc.hw_enque = socal_hw_enque; + + socal_download_fw (s); + + SOD(("Downloaded firmware\n")) + + /* Now setup xram circular queues */ + memset (cq, 0, sizeof(cq)); + + size = (SOCAL_CQ_REQ0_SIZE + SOCAL_CQ_REQ1_SIZE + SOCAL_CQ_RSP0_SIZE + SOCAL_CQ_RSP1_SIZE + SOCAL_CQ_RSP2_SIZE) * sizeof(socal_req); + s->req[0].pool = (socal_req *) sparc_dvma_malloc (size, "SOCAL request queues", &cq[0].address); + s->req[1].pool = s->req[0].pool + SOCAL_CQ_REQ0_SIZE; + s->rsp[0].pool = s->req[1].pool + SOCAL_CQ_REQ1_SIZE; + s->rsp[1].pool = s->rsp[0].pool + SOCAL_CQ_RSP0_SIZE; + s->rsp[2].pool = s->rsp[1].pool + SOCAL_CQ_RSP1_SIZE; + + s->req[0].hw_cq = (socal_hw_cq *)(s->xram + SOCAL_CQ_REQ_OFFSET); + s->req[1].hw_cq = (socal_hw_cq *)(s->xram + SOCAL_CQ_REQ_OFFSET + sizeof(socal_hw_cq)); + s->rsp[0].hw_cq = (socal_hw_cq *)(s->xram + SOCAL_CQ_RSP_OFFSET); + s->rsp[1].hw_cq = (socal_hw_cq *)(s->xram + SOCAL_CQ_RSP_OFFSET + sizeof(socal_hw_cq)); + s->rsp[2].hw_cq = (socal_hw_cq *)(s->xram + SOCAL_CQ_RSP_OFFSET + 2 * sizeof(socal_hw_cq)); + + cq[1].address = cq[0].address + (SOCAL_CQ_REQ0_SIZE * sizeof(socal_req)); + cq[4].address = cq[1].address + (SOCAL_CQ_REQ1_SIZE * sizeof(socal_req)); + cq[5].address = cq[4].address + (SOCAL_CQ_RSP0_SIZE * sizeof(socal_req)); + cq[6].address = cq[5].address + (SOCAL_CQ_RSP1_SIZE * sizeof(socal_req)); + + cq[0].last = SOCAL_CQ_REQ0_SIZE - 1; + cq[1].last = SOCAL_CQ_REQ1_SIZE - 1; + cq[4].last = SOCAL_CQ_RSP0_SIZE - 1; + cq[5].last = SOCAL_CQ_RSP1_SIZE - 1; + cq[6].last = SOCAL_CQ_RSP2_SIZE - 1; + for (i = 0; i < 8; i++) + cq[i].seqno = 1; + + s->req[0].last = SOCAL_CQ_REQ0_SIZE - 1; + s->req[1].last = SOCAL_CQ_REQ1_SIZE - 1; + s->rsp[0].last = SOCAL_CQ_RSP0_SIZE - 1; + s->rsp[1].last = SOCAL_CQ_RSP1_SIZE - 1; + s->rsp[2].last = SOCAL_CQ_RSP2_SIZE - 1; + + s->req[0].seqno = 1; + s->req[1].seqno = 1; + s->rsp[0].seqno = 1; + s->rsp[1].seqno = 1; + s->rsp[2].seqno = 1; + + socal_memcpy (s->xram + SOCAL_CQ_REQ_OFFSET, cq, sizeof(cq)); + + SOD(("Setting up params\n")) + + /* Make our sw copy of SOCAL service parameters */ + socal_memcpy (s->serv_params, s->xram + 0x280, sizeof (s->serv_params)); + + s->port[0].fc.common_svc = (common_svc_parm *)s->serv_params; + s->port[0].fc.class_svcs = (svc_parm *)(s->serv_params + 0x20); + s->port[1].fc.common_svc = (common_svc_parm *)&s->serv_params; + s->port[1].fc.class_svcs = (svc_parm *)(s->serv_params + 0x20); + + socal_enable (s); + + SOD(("Enabled SOCAL\n")) +} + +#ifndef MODULE +__initfunc(int socal_probe(void)) +#else +int init_module(void) +#endif +{ + struct linux_sbus *bus; + struct linux_sbus_device *sdev = 0; + struct socal *s; + int cards = 0; + + for_each_sbus(bus) { + for_each_sbusdev(sdev, bus) { + if(!strcmp(sdev->prom_name, "SUNW,socal")) { + socal_init(sdev, cards); + cards++; + } + } + } + if (!cards) return -EIO; + + for_each_socal(s) + if (s->next) + s->port[1].fc.next = &s->next->port[0].fc; + + fcp_init (&socals->port[0].fc); + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE +void cleanup_module(void) +{ + struct socal *s; + int irq; + struct linux_sbus_device *sdev; + + for_each_socal(s) { + irq = s->port[0].fc.irq; + disable_irq (irq); + free_irq (irq, s); + + fcp_release(&(s->port[0].fc), 2); + + sdev = s->port[0].fc.dev; + if (sdev->num_registers == 1) + sparc_free_io (s->eeprom, sdev->reg_addrs [0].reg_size); + else { + sparc_free_io (s->xram, sdev->reg_addrs [1].reg_size); + sparc_free_io ((char *)s->regs, sdev->reg_addrs [2].reg_size); + } + /* FIXME: sparc_dvma_free() ??? */ + } +} +#endif diff -u --recursive --new-file v2.2.3/linux/drivers/fc4/socal.h linux/drivers/fc4/socal.h --- v2.2.3/linux/drivers/fc4/socal.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/fc4/socal.h Mon Mar 15 16:11:29 1999 @@ -0,0 +1,319 @@ +/* socal.h: Definitions for Sparc SUNW,socal (SOC+) Fibre Channel Sbus driver. + * + * Copyright (C) 1998,1999 Jakub Jelinek (jj@ultra.linux.cz) + */ + +#ifndef __SOCAL_H +#define __SOCAL_H + +#include "fc.h" +#include "fcp.h" +#include "fcp_impl.h" + +/* Hardware structures and constants first {{{ */ + +union socal_rq_reg { + volatile u8 read[4]; + volatile u32 write; +}; +struct socal_regs { + volatile u32 cfg; /* Config Register */ + volatile u32 sae; /* Slave Access Error Register */ + volatile u32 cmd; /* Command & Status Register */ + volatile u32 imask; /* Interrupt Mask Register */ + union socal_rq_reg reqp; /* Request Queue Index Register */ + union socal_rq_reg resp; /* Response Queue Index Register */ +#define reqpr reqp.read +#define reqpw reqp.write +#define respr resp.read +#define respw resp.write +}; + +/* Config Register */ +#define SOCAL_CFG_EXT_RAM_BANK_MASK 0x07000000 +#define SOCAL_CFG_EEPROM_BANK_MASK 0x00030000 +#define SOCAL_CFG_BURST64_MASK 0x00000700 +#define SOCAL_CFG_SBUS_PARITY_TEST 0x00000020 +#define SOCAL_CFG_SBUS_PARITY_CHECK 0x00000010 +#define SOCAL_CFG_SBUS_ENHANCED 0x00000008 +#define SOCAL_CFG_BURST_MASK 0x00000007 +/* Bursts */ +#define SOCAL_CFG_BURST_4 0x00000000 +#define SOCAL_CFG_BURST_8 0x00000003 +#define SOCAL_CFG_BURST_16 0x00000004 +#define SOCAL_CFG_BURST_32 0x00000005 +#define SOCAL_CFG_BURST_64 0x00000006 +#define SOCAL_CFG_BURST_128 0x00000007 + +/* Slave Access Error Register */ +#define SOCAL_SAE_ALIGNMENT 0x00000004 +#define SOCAL_SAE_UNSUPPORTED 0x00000002 +#define SOCAL_SAE_PARITY 0x00000001 + +/* Command & Status Register */ +#define SOCAL_CMD_RSP_QALL 0x000f0000 +#define SOCAL_CMD_RSP_Q0 0x00010000 +#define SOCAL_CMD_RSP_Q1 0x00020000 +#define SOCAL_CMD_RSP_Q2 0x00040000 +#define SOCAL_CMD_RSP_Q3 0x00080000 +#define SOCAL_CMD_REQ_QALL 0x00000f00 +#define SOCAL_CMD_REQ_Q0 0x00000100 +#define SOCAL_CMD_REQ_Q1 0x00000200 +#define SOCAL_CMD_REQ_Q2 0x00000400 +#define SOCAL_CMD_REQ_Q3 0x00000800 +#define SOCAL_CMD_SAE 0x00000080 +#define SOCAL_CMD_INTR_PENDING 0x00000008 +#define SOCAL_CMD_NON_QUEUED 0x00000004 +#define SOCAL_CMD_IDLE 0x00000002 +#define SOCAL_CMD_SOFT_RESET 0x00000001 + +/* Interrupt Mask Register */ +#define SOCAL_IMASK_RSP_QALL 0x000f0000 +#define SOCAL_IMASK_RSP_Q0 0x00010000 +#define SOCAL_IMASK_RSP_Q1 0x00020000 +#define SOCAL_IMASK_RSP_Q2 0x00040000 +#define SOCAL_IMASK_RSP_Q3 0x00080000 +#define SOCAL_IMASK_REQ_QALL 0x00000f00 +#define SOCAL_IMASK_REQ_Q0 0x00000100 +#define SOCAL_IMASK_REQ_Q1 0x00000200 +#define SOCAL_IMASK_REQ_Q2 0x00000400 +#define SOCAL_IMASK_REQ_Q3 0x00000800 +#define SOCAL_IMASK_SAE 0x00000080 +#define SOCAL_IMASK_NON_QUEUED 0x00000004 + +#define SOCAL_INTR(s, cmd) \ + (((cmd & SOCAL_CMD_RSP_QALL) | ((~cmd) & SOCAL_CMD_REQ_QALL)) \ + & s->imask) + +#define SOCAL_SETIMASK(s, i) \ + (s)->imask = (i); (s)->regs->imask = (i) + +#define SOCAL_MAX_EXCHANGES 1024 + +/* XRAM + * + * This is a 64KB register area. + * From the documentation, it seems like it is finally able to cope + * at least with 1,2,4 byte accesses for read and 2,4 byte accesses for write. + */ + +/* Circular Queue */ + +#define SOCAL_CQ_REQ_OFFSET 0x200 +#define SOCAL_CQ_RSP_OFFSET 0x220 + +typedef struct { + u32 address; + u8 in; + u8 out; + u8 last; + u8 seqno; +} socal_hw_cq; + +#define SOCAL_PORT_A 0x0000 /* From/To Port A */ +#define SOCAL_PORT_B 0x0001 /* From/To Port A */ +#define SOCAL_FC_HDR 0x0002 /* Contains FC Header */ +#define SOCAL_NORSP 0x0004 /* Don't generate response nor interrupt */ +#define SOCAL_NOINT 0x0008 /* Generate response but not interrupt */ +#define SOCAL_XFERRDY 0x0010 /* Generate XFERRDY */ +#define SOCAL_IGNOREPARAM 0x0020 /* Ignore PARAM field in the FC header */ +#define SOCAL_COMPLETE 0x0040 /* Command completed */ +#define SOCAL_UNSOLICITED 0x0080 /* For request this is the packet to establish unsolicited pools, */ + /* for rsp this is unsolicited packet */ +#define SOCAL_STATUS 0x0100 /* State change (on/off line) */ +#define SOCAL_RSP_HDR 0x0200 /* Return frame header in any case */ + +typedef struct { + u32 token; + u16 flags; + u8 class; + u8 segcnt; + u32 bytecnt; +} socal_hdr; + +typedef struct { + u32 base; + u32 count; +} socal_data; + +#define SOCAL_CQTYPE_NOP 0x00 +#define SOCAL_CQTYPE_OUTBOUND 0x01 +#define SOCAL_CQTYPE_INBOUND 0x02 +#define SOCAL_CQTYPE_SIMPLE 0x03 +#define SOCAL_CQTYPE_IO_WRITE 0x04 +#define SOCAL_CQTYPE_IO_READ 0x05 +#define SOCAL_CQTYPE_UNSOLICITED 0x06 +#define SOCAL_CQTYPE_BYPASS_DEV 0x06 +#define SOCAL_CQTYPE_DIAG 0x07 +#define SOCAL_CQTYPE_OFFLINE 0x08 +#define SOCAL_CQTYPE_ADD_POOL 0x09 +#define SOCAL_CQTYPE_DELETE_POOL 0x0a +#define SOCAL_CQTYPE_ADD_BUFFER 0x0b +#define SOCAL_CQTYPE_ADD_POOL_BUFFER 0x0c +#define SOCAL_CQTYPE_REQUEST_ABORT 0x0d +#define SOCAL_CQTYPE_REQUEST_LIP 0x0e +#define SOCAL_CQTYPE_REPORT_MAP 0x0f +#define SOCAL_CQTYPE_RESPONSE 0x10 +#define SOCAL_CQTYPE_INLINE 0x20 + +#define SOCAL_CQFLAGS_CONT 0x01 +#define SOCAL_CQFLAGS_FULL 0x02 +#define SOCAL_CQFLAGS_BADHDR 0x04 +#define SOCAL_CQFLAGS_BADPKT 0x08 + +typedef struct { + socal_hdr shdr; + socal_data data[3]; + fc_hdr fchdr; + u8 count; + u8 type; + u8 flags; + u8 seqno; +} socal_req; + +#define SOCAL_OK 0 +#define SOCAL_P_RJT 2 +#define SOCAL_F_RJT 3 +#define SOCAL_P_BSY 4 +#define SOCAL_F_BSY 5 +#define SOCAL_ONLINE 0x10 +#define SOCAL_OFFLINE 0x11 +#define SOCAL_TIMEOUT 0x12 +#define SOCAL_OVERRUN 0x13 +#define SOCAL_ONLINE_LOOP 0x14 +#define SOCAL_OLD_PORT 0x15 +#define SOCAL_AL_PORT 0x16 +#define SOCAL_UNKOWN_CQ_TYPE 0x20 +#define SOCAL_BAD_SEG_CNT 0x21 +#define SOCAL_MAX_XCHG_EXCEEDED 0x22 +#define SOCAL_BAD_XID 0x23 +#define SOCAL_XCHG_BUSY 0x24 +#define SOCAL_BAD_POOL_ID 0x25 +#define SOCAL_INSUFFICIENT_CQES 0x26 +#define SOCAL_ALLOC_FAIL 0x27 +#define SOCAL_BAD_SID 0x28 +#define SOCAL_NO_SEG_INIT 0x29 +#define SOCAL_BAD_DID 0x2a +#define SOCAL_ABORTED 0x30 +#define SOCAL_ABORT_FAILED 0x31 + +typedef struct { + socal_hdr shdr; + u32 status; + socal_data data; + u8 xxx1[10]; + u16 ncmds; + fc_hdr fchdr; + u8 count; + u8 type; + u8 flags; + u8 seqno; +} socal_rsp; + +typedef struct { + socal_hdr shdr; + u8 xxx1[48]; + u8 count; + u8 type; + u8 flags; + u8 seqno; +} socal_cmdonly; + +#define SOCAL_DIAG_NOP 0x00 +#define SOCAL_DIAG_INT_LOOP 0x01 +#define SOCAL_DIAG_EXT_LOOP 0x02 +#define SOCAL_DIAG_REM_LOOP 0x03 +#define SOCAL_DIAG_XRAM_TEST 0x04 +#define SOCAL_DIAG_SOC_TEST 0x05 +#define SOCAL_DIAG_HCB_TEST 0x06 +#define SOCAL_DIAG_SOCLB_TEST 0x07 +#define SOCAL_DIAG_SRDSLB_TEST 0x08 +#define SOCAL_DIAG_EXTOE_TEST 0x09 + +typedef struct { + socal_hdr shdr; + u32 cmd; + u8 xxx1[44]; + u8 count; + u8 type; + u8 flags; + u8 seqno; +} socal_diag_req; + +#define SOCAL_POOL_MASK_RCTL 0x800000 +#define SOCAL_POOL_MASK_DID 0x700000 +#define SOCAL_POOL_MASK_SID 0x070000 +#define SOCAL_POOL_MASK_TYPE 0x008000 +#define SOCAL_POOL_MASK_F_CTL 0x007000 +#define SOCAL_POOL_MASK_SEQ_ID 0x000800 +#define SOCAL_POOL_MASK_D_CTL 0x000400 +#define SOCAL_POOL_MASK_SEQ_CNT 0x000300 +#define SOCAL_POOL_MASK_OX_ID 0x0000f0 +#define SOCAL_POOL_MASK_PARAM 0x00000f + +typedef struct { + socal_hdr shdr; + u32 pool_id; + u32 header_mask; + u32 buf_size; + u32 entries; + u8 xxx1[8]; + fc_hdr fchdr; + u8 count; + u8 type; + u8 flags; + u8 seqno; +} socal_pool_req; + +/* }}} */ + +/* Now our software structures and constants we use to drive the beast {{{ */ + +#define SOCAL_CQ_REQ0_SIZE 4 +#define SOCAL_CQ_REQ1_SIZE 256 +#define SOCAL_CQ_RSP0_SIZE 8 +#define SOCAL_CQ_RSP1_SIZE 4 +#define SOCAL_CQ_RSP2_SIZE 4 + +#define SOCAL_SOLICITED_RSP_Q 0 +#define SOCAL_SOLICITED_BAD_RSP_Q 1 +#define SOCAL_UNSOLICITED_RSP_Q 2 + +struct socal; + +typedef struct { + /* This must come first */ + fc_channel fc; + struct socal *s; + u16 flags; + u16 mask; +} socal_port; + +typedef struct { + socal_hw_cq *hw_cq; /* Related XRAM cq */ + socal_req *pool; + u8 in; + u8 out; + u8 last; + u8 seqno; +} socal_cq; + +struct socal { + socal_port port[2]; /* Every SOCAL has one or two FC ports */ + socal_cq req[4]; /* Request CQs */ + socal_cq rsp[4]; /* Response CQs */ + int socal_no; + struct socal_regs *regs; + u8 *xram; + u8 *eeprom; + fc_wwn wwn; + u32 imask; /* Our copy of regs->imask */ + u32 cfg; /* Our copy of regs->cfg */ + char serv_params[80]; + struct socal *next; + int curr_port; /* Which port will have priority to fcp_queue_empty */ +}; + +/* }}} */ + +#endif /* !(__SOCAL_H) */ diff -u --recursive --new-file v2.2.3/linux/drivers/isdn/Config.in linux/drivers/isdn/Config.in --- v2.2.3/linux/drivers/isdn/Config.in Thu Dec 31 10:29:00 1998 +++ linux/drivers/isdn/Config.in Mon Mar 15 16:11:29 1999 @@ -39,7 +39,8 @@ bool 'HiSax Support for Niccy PnP/PCI card' CONFIG_HISAX_NICCY if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then - bool 'HiSax Support for Am7930' CONFIG_HISAX_AMD7930 + bool 'HiSax Support for SPARC Am7930' CONFIG_HISAX_AMD7930 + bool 'HiSax Support for SPARC DBRI' CONFIG_HISAX_DBRI fi fi fi diff -u --recursive --new-file v2.2.3/linux/drivers/isdn/hisax/Makefile linux/drivers/isdn/hisax/Makefile --- v2.2.3/linux/drivers/isdn/hisax/Makefile Wed Apr 1 20:11:50 1998 +++ linux/drivers/isdn/hisax/Makefile Mon Mar 15 16:11:29 1999 @@ -108,8 +108,10 @@ HFC_2BDS0 := hfc_2bds0.o endif ifeq ($(CONFIG_HISAX_AMD7930),y) - O_OBJS += amd7930.o - RAWHDLC_OBJ := rawhdlc.o + RAWHDLC_OBJ := foreign.o rawhdlc.o +endif +ifeq ($(CONFIG_HISAX_DBRI),y) + RAWHDLC_OBJ := foreign.o rawhdlc.o endif ifeq ($(CONFIG_HISAX_NICCY),y) diff -u --recursive --new-file v2.2.3/linux/drivers/isdn/hisax/config.c linux/drivers/isdn/hisax/config.c --- v2.2.3/linux/drivers/isdn/hisax/config.c Thu Jan 7 15:11:37 1999 +++ linux/drivers/isdn/hisax/config.c Mon Mar 15 16:11:30 1999 @@ -193,6 +193,13 @@ #define DEFAULT_CFG {12,0x3e0,0,0} #endif +#ifdef CONFIG_HISAX_DBRI +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_DBRI +#define DEFAULT_CFG {0,0x0,0,0} +#endif + #ifdef CONFIG_HISAX_NICCY #undef DEFAULT_CARD #undef DEFAULT_CFG @@ -486,6 +493,7 @@ case ISDN_CTYPE_ELSA_PCI: case ISDN_CTYPE_NETJET: case ISDN_CTYPE_AMD7930: + case ISDN_CTYPE_DBRI: break; } } diff -u --recursive --new-file v2.2.3/linux/drivers/isdn/hisax/foreign.c linux/drivers/isdn/hisax/foreign.c --- v2.2.3/linux/drivers/isdn/hisax/foreign.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/foreign.c Mon Mar 15 16:11:30 1999 @@ -0,0 +1,780 @@ +/* $Id: foreign.c,v 1.1 1998/11/09 07:48:48 baccala Exp $ + * + * HiSax ISDN driver - foreign chipset interface + * + * Author Brent Baccala (baccala@FreeSoft.org) + * + * + * + * $Log: foreign.c,v $ + * Revision 1.1 1998/11/09 07:48:48 baccala + * Initial DBRI ISDN code. Sometimes works (brings up the link and you + * can telnet through it), sometimes doesn't (crashes the machine) + * + * Revision 1.2 1998/02/12 23:07:10 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() + * + * Revision 1.1 1998/02/03 23:20:51 keil + * New files for SPARC isdn support + * + * Revision 1.1 1998/01/08 04:17:12 baccala + * ISDN comes to the Sparc. Key points: + * + * - Existing ISDN HiSax driver provides all the smarts + * - it compiles, runs, talks to an isolated phone switch, connects + * to a Cisco, pings go through + * - AMD 7930 support only (no DBRI yet) + * - no US NI-1 support (may not work on US phone system - untested) + * - periodic packet loss, apparently due to lost interrupts + * - ISDN sometimes freezes, requiring reboot before it will work again + * + * The code is unreliable enough to be consider alpha + * + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "isac.h" +#include "isdnl1.h" +#include "foreign.h" +#include "rawhdlc.h" +#include + +static const char *foreign_revision = "$Revision: 1.1 $"; + +#define RCV_BUFSIZE 1024 /* Size of raw receive buffer in bytes */ +#define RCV_BUFBLKS 4 /* Number of blocks to divide buffer into + * (must divide RCV_BUFSIZE) */ + +static void Bchan_fill_fifo(struct BCState *, struct sk_buff *); + +static void +Bchan_xmt_bh(struct BCState *bcs) +{ + struct sk_buff *skb; + + if (bcs->hw.foreign.tx_skb != NULL) { + dev_kfree_skb(bcs->hw.foreign.tx_skb); + bcs->hw.foreign.tx_skb = NULL; + } + + if ((skb = skb_dequeue(&bcs->squeue))) { + Bchan_fill_fifo(bcs, skb); + } else { + clear_bit(BC_FLG_BUSY, &bcs->Flag); + bcs->event |= 1 << B_XMTBUFREADY; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } +} + +static void +Bchan_xmit_callback(struct BCState *bcs) +{ + queue_task(&bcs->hw.foreign.tq_xmt, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +/* B channel transmission: two modes (three, if you count L1_MODE_NULL) + * + * L1_MODE_HDLC - We need to do HDLC encapsulation before transmiting + * the packet (i.e. make_raw_hdlc_data). Since this can be a + * time-consuming operation, our completion callback just schedules + * a bottom half to do encapsulation for the next packet. In between, + * the link will just idle + * + * L1_MODE_TRANS - Data goes through, well, transparent. No HDLC encap, + * and we can't just let the link idle, so the "bottom half" actually + * gets called during the top half (it's our callback routine in this case), + * but it's a lot faster now since we don't call make_raw_hdlc_data + */ + +static void +Bchan_fill_fifo(struct BCState *bcs, struct sk_buff *skb) +{ + struct IsdnCardState *cs = bcs->cs; + struct foreign_hw *hw = &bcs->hw.foreign; + int len; + + if ((cs->debug & L1_DEB_HSCX) || (cs->debug & L1_DEB_HSCX_FIFO)) { + char tmp[2048]; + char *t = tmp; + + t += sprintf(t, "Bchan_fill_fifo %c cnt %d", + bcs->channel ? 'B' : 'A', skb->len); + if (cs->debug & L1_DEB_HSCX_FIFO) + QuickHex(t, skb->data, skb->len); + debugl1(cs, tmp); + } + + if (hw->doHDLCprocessing) { + len = make_raw_hdlc_data(skb->data, skb->len, + bcs->hw.foreign.tx_buff, RAW_BUFMAX); + if (len > 0) + cs->hw.foreign->bxmit(0, bcs->channel, + bcs->hw.foreign.tx_buff, len, + (void *) &Bchan_xmit_callback, + (void *) bcs); + dev_kfree_skb(skb); + } else { + cs->hw.foreign->bxmit(0, bcs->channel, + skb->data, skb->len, + (void *) &Bchan_xmit_callback, + (void *) bcs); + bcs->hw.foreign.tx_skb = skb; + } +} + +static void +Bchan_mode(struct BCState *bcs, int mode, int bc) +{ + struct IsdnCardState *cs = bcs->cs; + + if (cs->debug & L1_DEB_HSCX) { + char tmp[40]; + sprintf(tmp, "foreign mode %d bchan %d/%d", + mode, bc, bcs->channel); + debugl1(cs, tmp); + } + bcs->mode = mode; +} + +/* Bchan_l2l1 is the entry point for upper layer routines that want to + * transmit on the B channel. PH_DATA_REQ is a normal packet that + * we either start transmitting (if idle) or queue (if busy). + * PH_PULL_REQ can be called to request a callback message (PH_PULL_CNF) + * once the link is idle. After a "pull" callback, the upper layer + * routines can use PH_PULL_IND to send data. + */ + +static void +Bchan_l2l1(struct PStack *st, int pr, void *arg) +{ + struct sk_buff *skb = arg; + + switch (pr) { + case (PH_DATA_REQ): + if (test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag)) { + skb_queue_tail(&st->l1.bcs->squeue, skb); + } else { + test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + Bchan_fill_fifo(st->l1.bcs, skb); + } + break; + case (PH_PULL_IND): + if (test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag)) { + printk(KERN_WARNING "foreign: this shouldn't happen\n"); + break; + } + test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + Bchan_fill_fifo(st->l1.bcs, skb); + break; + case (PH_PULL_REQ): + if (!test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag)) { + clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + st->l1.l1l2(st, PH_PULL_CNF, NULL); + } else + set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + } +} + +/* +**************************************************************************** +***************** Receiver callback and bottom half ************************ +**************************************************************************** +*/ + +/* Bchan_recv_done() is called when a frame has been completely decoded + * into hw->rv_skb and we're ready to hand it off to the HiSax upper + * layer. If a "large" packet is received, stick rv_skb on the + * receive queue and alloc a new (large) skb to act as buffer for + * future receives. If a small packet is received, leave rv_skb + * alone, alloc a new skb of the correct size, and copy the packet + * into it. In any case, flag the channel as B_RCVBUFREADY and + * queue the upper layer's task. + */ + +static void +Bchan_recv_done(struct BCState *bcs, unsigned int len) +{ + struct IsdnCardState *cs = bcs->cs; + struct foreign_hw *hw = &bcs->hw.foreign; + struct sk_buff *skb; + + if (cs->debug & L1_DEB_HSCX_FIFO) { + char tmp[2048]; + char *t = tmp; + + t += sprintf(t, "Bchan_rcv %c cnt %d (%x)", bcs->channel ? 'B' : 'A', len, hw->rv_skb->tail); + QuickHex(t, hw->rv_skb->tail, len); + debugl1(cs, tmp); + } + + if (len > HSCX_BUFMAX/2) { + /* Large packet received */ + + if (!(skb = dev_alloc_skb(HSCX_BUFMAX))) { + printk(KERN_WARNING "foreign: receive out of memory\n"); + } else { + skb_put(hw->rv_skb, len); + skb_queue_tail(&bcs->rqueue, hw->rv_skb); + hw->rv_skb = skb; + + bcs->event |= 1 << B_RCVBUFREADY; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + } else { + /* Small packet received */ + + if (!(skb = dev_alloc_skb(len))) { + printk(KERN_WARNING "foreign: receive out of memory\n"); + } else { + memcpy(skb_put(skb, len), hw->rv_skb->tail, len); + skb_queue_tail(&bcs->rqueue, skb); + + bcs->event |= 1 << B_RCVBUFREADY; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + } +} + +/* Bchan_recv_callback()'s behavior depends on whether we're doing local + * HDLC processing. If so, receive into hw->rv_buff and queue Bchan_rcv_bh + * to decode the HDLC at leisure. Otherwise, receive directly into hw->rv_skb + * and call Bchan_recv_done(). In either case, prepare a new buffer for + * further receives and hand it to the hardware driver. + */ + +static void +Bchan_recv_callback(struct BCState *bcs, int error, unsigned int len) +{ + struct IsdnCardState *cs = bcs->cs; + struct foreign_hw *hw = &bcs->hw.foreign; + + if (hw->doHDLCprocessing) { + + hw->rv_buff_in += RCV_BUFSIZE/RCV_BUFBLKS; + hw->rv_buff_in %= RCV_BUFSIZE; + + if (hw->rv_buff_in != hw->rv_buff_out) { + cs->hw.foreign->brecv(0, bcs->channel, + hw->rv_buff + hw->rv_buff_in, + RCV_BUFSIZE/RCV_BUFBLKS, + (void *) &Bchan_recv_callback, + (void *) bcs); + + } + queue_task(&hw->tq_rcv, &tq_immediate); + mark_bh(IMMEDIATE_BH); + + } else { + if (error) { + char tmp[256]; + sprintf(tmp, "B channel %c receive error %x", + bcs->channel ? 'B' : 'A', error); + debugl1(cs, tmp); + } else { + Bchan_recv_done(bcs, len); + } + cs->hw.foreign->brecv(0, bcs->channel, + hw->rv_skb->tail, HSCX_BUFMAX, + (void *) &Bchan_recv_callback, + (void *) bcs); + + } +} + +/* Bchan_rcv_bh() is a "shim" bottom half handler stuck in between + * Bchan_recv_callback() and the HiSax upper layer if we need to + * do local HDLC processing. + */ + +static void +Bchan_rcv_bh(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + struct foreign_hw *hw = &bcs->hw.foreign; + struct sk_buff *skb; + int len; + + if (cs->debug & L1_DEB_HSCX) { + char tmp[1024]; + + sprintf(tmp, "foreign_Bchan_rcv (%d/%d)", + hw->rv_buff_in, hw->rv_buff_out); + debugl1(cs, tmp); + } + + do { + if (cs->debug & L1_DEB_HSCX) { + char tmp[1024]; + + QuickHex(tmp, hw->rv_buff + hw->rv_buff_out, + RCV_BUFSIZE/RCV_BUFBLKS); + debugl1(cs, tmp); + } + + while ((len = read_raw_hdlc_data(hw->hdlc_state, + hw->rv_buff + hw->rv_buff_out, RCV_BUFSIZE/RCV_BUFBLKS, + hw->rv_skb->tail, HSCX_BUFMAX))) { + if (len > 0) { + Bchan_recv_done(bcs, len); + } else { + char tmp[256]; + sprintf(tmp, "B channel %c receive error", + bcs->channel ? 'B' : 'A'); + debugl1(cs, tmp); + } + } + + if (hw->rv_buff_in == hw->rv_buff_out) { + /* Buffer was filled up - need to restart receiver */ + cs->hw.foreign->brecv(0, bcs->channel, + hw->rv_buff + hw->rv_buff_in, + RCV_BUFSIZE/RCV_BUFBLKS, + (void *) &Bchan_recv_callback, + (void *) bcs); + } + + hw->rv_buff_out += RCV_BUFSIZE/RCV_BUFBLKS; + hw->rv_buff_out %= RCV_BUFSIZE; + + } while (hw->rv_buff_in != hw->rv_buff_out); +} + +static void +Bchan_close(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + struct sk_buff *skb; + + Bchan_mode(bcs, 0, 0); + cs->hw.foreign->bclose(0, bcs->channel); + + if (test_bit(BC_FLG_INIT, &bcs->Flag)) { + while ((skb = skb_dequeue(&bcs->rqueue))) { + dev_kfree_skb(skb); + } + while ((skb = skb_dequeue(&bcs->squeue))) { + dev_kfree_skb(skb); + } + } + test_and_clear_bit(BC_FLG_INIT, &bcs->Flag); +} + +static int +Bchan_open(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + struct foreign_hw *hw = &bcs->hw.foreign; + + if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) { + skb_queue_head_init(&bcs->rqueue); + skb_queue_head_init(&bcs->squeue); + } + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + + hw->doHDLCprocessing = 0; + if (bcs->mode == L1_MODE_HDLC) { + if (cs->hw.foreign->bopen(0, bcs->channel, 1, 0xff) == -1) { + if (cs->hw.foreign->bopen(0, bcs->channel, 0, 0xff) == -1) { + return (-1); + } + hw->doHDLCprocessing = 1; + } + } else { + if (cs->hw.foreign->bopen(0, bcs->channel, 0, 0xff) == -1) { + return (-1); + } + } + + hw->rv_buff_in = 0; + hw->rv_buff_out = 0; + hw->tx_skb = NULL; + init_hdlc_state(hw->hdlc_state, 0); + cs->hw.foreign->brecv(0, bcs->channel, + hw->rv_buff + hw->rv_buff_in, + RCV_BUFSIZE/RCV_BUFBLKS, + (void *) &Bchan_recv_callback, (void *) bcs); + + bcs->event = 0; + bcs->tx_cnt = 0; + return (0); +} + +static void +Bchan_init(struct BCState *bcs) +{ + if (!(bcs->hw.foreign.tx_buff = kmalloc(RAW_BUFMAX, GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for foreign.tx_buff\n"); + return; + } + if (!(bcs->hw.foreign.rv_buff = kmalloc(RCV_BUFSIZE, GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for foreign.rv_buff\n"); + return; + } + if (!(bcs->hw.foreign.rv_skb = dev_alloc_skb(HSCX_BUFMAX))) { + printk(KERN_WARNING + "HiSax: No memory for foreign.rv_skb\n"); + return; + } + if (!(bcs->hw.foreign.hdlc_state = kmalloc(sizeof(struct hdlc_state), + GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for foreign.hdlc_state\n"); + return; + } + + bcs->hw.foreign.tq_rcv.sync = 0; + bcs->hw.foreign.tq_rcv.routine = (void (*)(void *)) &Bchan_rcv_bh; + bcs->hw.foreign.tq_rcv.data = (void *) bcs; + + bcs->hw.foreign.tq_xmt.sync = 0; + bcs->hw.foreign.tq_xmt.routine = (void (*)(void *)) &Bchan_xmt_bh; + bcs->hw.foreign.tq_xmt.data = (void *) bcs; +} + +static void +Bchan_manl1(struct PStack *st, int pr, + void *arg) +{ + switch (pr) { + case (PH_ACTIVATE_REQ): + test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + Bchan_mode(st->l1.bcs, st->l1.mode, st->l1.bc); + st->l1.l1man(st, PH_ACTIVATE_CNF, NULL); + break; + case (PH_DEACTIVATE_REQ): + if (!test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag)) + Bchan_mode(st->l1.bcs, 0, 0); + test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + break; + } +} + +int +setstack_foreign(struct PStack *st, struct BCState *bcs) +{ + if (Bchan_open(bcs)) + return (-1); + st->l1.bcs = bcs; + st->l2.l2l1 = Bchan_l2l1; + st->ma.manl1 = Bchan_manl1; + setstack_manager(st); + bcs->st = st; + return (0); +} + + +static void +foreign_drecv_callback(void *arg, int error, unsigned int count) +{ + struct IsdnCardState *cs = (struct IsdnCardState *) arg; + static struct tq_struct task = {0, 0, (void *) &DChannel_proc_rcv, 0}; + struct sk_buff *skb; + + /* NOTE: This function is called directly from an interrupt handler */ + + if (1) { + if (!(skb = alloc_skb(count, GFP_ATOMIC))) + printk(KERN_WARNING "HiSax: D receive out of memory\n"); + else { + memcpy(skb_put(skb, count), cs->rcvbuf, count); + skb_queue_tail(&cs->rq, skb); + } + + task.data = (void *) cs; + queue_task(&task, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + + if (cs->debug & L1_DEB_ISAC_FIFO) { + char tmp[128]; + char *t = tmp; + + t += sprintf(t, "foreign Drecv cnt %d", count); + if (error) t += sprintf(t, " ERR %x", error); + QuickHex(t, cs->rcvbuf, count); + debugl1(cs, tmp); + } + + cs->hw.foreign->drecv(0, cs->rcvbuf, MAX_DFRAME_LEN, + &foreign_drecv_callback, cs); +} + +static void +foreign_dxmit_callback(void *arg, int error) +{ + struct IsdnCardState *cs = (struct IsdnCardState *) arg; + static struct tq_struct task = {0, 0, (void *) &DChannel_proc_xmt, 0}; + + /* NOTE: This function is called directly from an interrupt handler */ + + /* may wish to do retransmission here, if error indicates collision */ + + if (cs->debug & L1_DEB_ISAC_FIFO) { + char tmp[128]; + char *t = tmp; + + t += sprintf(t, "foreign Dxmit cnt %d", cs->tx_skb->len); + if (error) t += sprintf(t, " ERR %x", error); + QuickHex(t, cs->tx_skb->data, cs->tx_skb->len); + debugl1(cs, tmp); + } + + cs->tx_skb = NULL; + + task.data = (void *) cs; + queue_task(&task, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static void +foreign_Dchan_l2l1(struct PStack *st, int pr, void *arg) +{ + struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware; + struct sk_buff *skb = arg; + char str[64]; + + switch (pr) { + case (PH_DATA_REQ): + if (cs->tx_skb) { + skb_queue_tail(&cs->sq, skb); +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + Logl2Frame(cs, skb, "PH_DATA Queued", 0); +#endif + } else { + if ((cs->dlogflag) && (!(skb->data[2] & 1))) { + /* I-FRAME */ + LogFrame(cs, skb->data, skb->len); + sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei); + dlogframe(cs, skb->data+4, skb->len-4, + str); + } + cs->tx_skb = skb; + cs->tx_cnt = 0; +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + Logl2Frame(cs, skb, "PH_DATA", 0); +#endif + cs->hw.foreign->dxmit(0, skb->data, skb->len, + &foreign_dxmit_callback, cs); + } + break; + case (PH_PULL_IND): + if (cs->tx_skb) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, " l2l1 tx_skb exist this shouldn't happen"); + skb_queue_tail(&cs->sq, skb); + break; + } + if ((cs->dlogflag) && (!(skb->data[2] & 1))) { /* I-FRAME */ + LogFrame(cs, skb->data, skb->len); + sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei); + dlogframe(cs, skb->data + 4, skb->len - 4, + str); + } + cs->tx_skb = skb; + cs->tx_cnt = 0; +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + Logl2Frame(cs, skb, "PH_DATA_PULLED", 0); +#endif + cs->hw.foreign->dxmit(0, cs->tx_skb->data, cs->tx_skb->len, + &foreign_dxmit_callback, cs); + break; + case (PH_PULL_REQ): +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + debugl1(cs, "-> PH_REQUEST_PULL"); +#endif + if (!cs->tx_skb) { + test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + st->l1.l1l2(st, PH_PULL_CNF, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + } +} + +int +setDstack_foreign(struct PStack *st, struct IsdnCardState *cs) +{ + st->l2.l2l1 = foreign_Dchan_l2l1; + if (! cs->rcvbuf) { + printk("setDstack_foreign: No cs->rcvbuf!\n"); + } else { + cs->hw.foreign->drecv(0, cs->rcvbuf, MAX_DFRAME_LEN, + &foreign_drecv_callback, cs); + } + return (0); +} + +static void +manl1_msg(struct IsdnCardState *cs, int msg, void *arg) { + struct PStack *st; + + st = cs->stlist; + while (st) { + st->ma.manl1(st, msg, arg); + st = st->next; + } +} + +void +foreign_l1cmd(struct IsdnCardState *cs, int msg, void *arg) +{ + u_char val; + char tmp[32]; + + if (cs->debug & L1_DEB_ISAC) { + sprintf(tmp, "foreign_l1cmd msg %x", msg); + debugl1(cs, tmp); + } + + switch(msg) { + case PH_RESET_REQ: + cs->hw.foreign->liu_deactivate(0); + manl1_msg(cs, PH_POWERUP_CNF, NULL); + break; + case PH_ENABLE_REQ: + break; + case PH_INFO3_REQ: + cs->hw.foreign->liu_activate(0,0); + break; + case PH_TESTLOOP_REQ: + break; + default: + if (cs->debug & L1_DEB_WARN) { + sprintf(tmp, "foreign_l1cmd unknown %4x", msg); + debugl1(cs, tmp); + } + break; + } +} + +static void +foreign_new_ph(struct IsdnCardState *cs) +{ + switch (cs->hw.foreign->get_liu_state(0)) { + case 3: + manl1_msg(cs, PH_POWERUP_CNF, NULL); + break; + + case 7: + manl1_msg(cs, PH_I4_P8_IND, NULL); + break; + + case 8: + manl1_msg(cs, PH_RSYNC_IND, NULL); + break; + } +} + +/* LIU state change callback */ + +static void +foreign_liu_callback(struct IsdnCardState *cs) +{ + static struct tq_struct task = {0, 0, (void *) &foreign_new_ph, 0}; + + if (!cs) + return; + + if (cs->debug & L1_DEB_ISAC) { + char tmp[32]; + sprintf(tmp, "foreign LIU state %d", + cs->hw.foreign->get_liu_state(0)); + debugl1(cs, tmp); + } + + task.data = (void *) cs; + queue_task(&task, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static void init_foreign(struct IsdnCardState *cs) +{ + Bchan_init(&cs->bcs[0]); + Bchan_init(&cs->bcs[1]); + cs->bcs[0].BC_SetStack = setstack_foreign; + cs->bcs[1].BC_SetStack = setstack_foreign; + cs->bcs[0].BC_Close = Bchan_close; + cs->bcs[1].BC_Close = Bchan_close; + Bchan_mode(cs->bcs, 0, 0); + Bchan_mode(cs->bcs + 1, 0, 0); +} + +static int +foreign_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + return(0); + case CARD_RELEASE: + return(0); + case CARD_SETIRQ: + return(0); + case CARD_INIT: + cs->l1cmd = foreign_l1cmd; + cs->setstack_d = setDstack_foreign; + cs->hw.foreign->liu_init(0, &foreign_liu_callback, + (void *)cs); + init_foreign(cs); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +#if CARD_AMD7930 +extern struct foreign_interface amd7930_foreign_interface; +#endif + +#if CARD_DBRI +extern struct foreign_interface dbri_foreign_interface; +#endif + +__initfunc(int +setup_foreign(struct IsdnCard *card)) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, foreign_revision); + printk(KERN_INFO "HiSax: Foreign chip driver Rev. %s\n", + HiSax_getrev(tmp)); + +#if CARD_AMD7930 + if (cs->typ == ISDN_CTYPE_AMD7930) { + cs->hw.foreign = &amd7930_foreign_interface; + cs->irq = cs->hw.foreign->get_irqnum(0); + if (cs->irq == 0) + return (0); + cs->cardmsg = &foreign_card_msg; + return (1); + } +#endif + +#if CARD_DBRI + if (cs->typ == ISDN_CTYPE_DBRI) { + cs->hw.foreign = &dbri_foreign_interface; + cs->irq = cs->hw.foreign->get_irqnum(0); + if (cs->irq == 0) + return (0); + cs->cardmsg = &foreign_card_msg; + return (1); + } +#endif + + return(0); +} diff -u --recursive --new-file v2.2.3/linux/drivers/isdn/hisax/foreign.h linux/drivers/isdn/hisax/foreign.h --- v2.2.3/linux/drivers/isdn/hisax/foreign.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/foreign.h Mon Mar 15 16:11:30 1999 @@ -0,0 +1,193 @@ +/* $Id: foreign.h,v 1.1 1998/11/09 07:48:57 baccala Exp $ + * + * HiSax ISDN driver - foreign chipset interface + * + * Author Brent Baccala (baccala@FreeSoft.org) + * + * + * + * $Log: foreign.h,v $ + * Revision 1.1 1998/11/09 07:48:57 baccala + * Initial DBRI ISDN code. Sometimes works (brings up the link and you + * can telnet through it), sometimes doesn't (crashes the machine) + * + */ + +/* + * ISDN operations + * + * Many of these routines take an "int dev" argument, which is simply + * an index into the drivers[] array. Currently, we only support a + * single foreign chip, so the value should always be 0. B channel + * operations require an "int chan", which should be 0 for channel B1 + * and 1 for channel B2 + * + * int get_irqnum(int dev) + * + * returns the interrupt number being used by the chip. ISDN4linux + * uses this number to watch the interrupt during initialization and + * make sure something is happening. + * + * int get_liu_state(int dev) + * + * returns the current state of the ISDN Line Interface Unit (LIU) + * as a number between 2 (state F2) and 7 (state F7). 0 may also be + * returned if the chip doesn't exist or the LIU hasn't been + * activated. The meanings of the states are defined in I.430, ISDN + * BRI Physical Layer Interface. The most important two states are + * F3 (shutdown) and F7 (syncronized). + * + * void liu_init(int dev, void (*callback)(void *), void *callback_arg) + * + * initializes the LIU and optionally registers a callback to be + * signaled upon a change of LIU state. The callback will be called + * with a single opaque callback_arg. Once the callback has been + * triggered, get_liu_state can be used to determine the LIU + * current state. + * + * void liu_activate(int dev, int priority) + * + * requests LIU activation at a given D-channel priority. + * Successful activatation is achieved upon entering state F7, which + * will trigger any callback previously registered with + * liu_init. + * + * void liu_deactivate(int dev) + * + * deactivates LIU. Outstanding D and B channel transactions are + * terminated rudely and without callback notification. LIU change + * of state callback will be triggered, however. + * + * void dxmit(int dev, __u8 *buffer, unsigned int count, + * void (*callback)(void *, int), void *callback_arg) + * + * transmits a packet - specified with buffer, count - over the D-channel + * interface. Buffer should begin with the LAPD address field and + * end with the information field. FCS and flag sequences should not + * be included, nor is bit-stuffing required - all these functions are + * performed by the chip. The callback function will be called + * DURING THE TOP HALF OF AN INTERRUPT HANDLER and will be passed + * both the arbitrary callback_arg and an integer error indication: + * + * 0 - successful transmission; ready for next packet + * non-0 - error value + * + * The callback routine should defer any time-consuming operations + * to a bottom-half handler; however, dxmit may be called + * from within the callback to request back-to-back transmission of + * a second packet (without repeating the priority/collision mechanism) + * + * A comment about the "collision detect" error, which is signalled + * whenever the echoed D-channel data didn't match the transmitted + * data. This is part of ISDN's normal multi-drop T-interface + * operation, indicating that another device has attempted simultaneous + * transmission, but can also result from line noise. An immediate + * requeue via dxmit is suggested, but repeated collision + * errors may indicate a more serious problem. + * + * void drecv(int dev, __u8 *buffer, unsigned int size, + * void (*callback)(void *, int, unsigned int), + * void *callback_arg) + * + * register a buffer - buffer, size - into which a D-channel packet + * can be received. The callback function will be called DURING + * THE TOP HALF OF AN INTERRUPT HANDLER and will be passed an + * arbitrary callback_arg, an integer error indication and the length + * of the received packet, which will start with the address field, + * end with the information field, and not contain flag or FCS + * bytes. Bit-stuffing will already have been corrected for. + * Possible values of second callback argument "error": + * + * 0 - successful reception + * non-0 - error value + * + * int bopen(int dev, int chan, int hdlcmode, u_char xmit_idle_char) + * + * This function should be called before any other operations on a B + * channel. mode is either non-0 to (de)encapsulate using HDLC or 0 + * for transparent operation. In addition to arranging for interrupt + * handling and channel multiplexing, it sets the xmit_idle_char + * which is transmitted on the interface when no data buffer is + * available. Suggested values are: 0 for ISDN audio; FF for HDLC + * mark idle; 7E for HDLC flag idle. Returns 0 on a successful + * open; -1 on error. + * + * If the chip doesn't support HDLC encapsulation (the Am7930 + * doesn't), an error will be returned opening L1_MODE_HDLC; the + * HiSax driver should retry with L1_MODE_TRANS, then be prepared to + * bit-stuff the data before shipping it to the driver. + * + * void bclose(int dev, int chan) + * + * Shuts down a B channel when no longer in use. + * + * void bxmit(int dev, int chan, __u8 *buffer, unsigned int count, + * void (*callback)(void *, int), void *callback_arg) + * + * transmits a data block - specified with buffer, count - over the + * B channel interface specified by dev/chan. In mode L1_MODE_HDLC, + * a complete HDLC frames should be relayed with a single bxmit. + * The callback function will be called DURING THE TOP HALF OF AN + * INTERRUPT HANDLER and will be passed the arbitrary callback_arg + * and an integer error indication: + * + * 0 - successful transmission; ready for next packet + * non-0 - error + * + * The callback routine should defer any time-consuming operations + * to a bottom-half handler; however, bxmit may be called + * from within the callback to request back-to-back transmission of + * another data block + * + * void brecv(int dev, int chan, __u8 *buffer, unsigned int size, + * void (*callback)(void *, int, unsigned int), void *callback_arg) + * + * receive a raw data block - specified with buffer, size - over the + * B channel interface specified by dev/chan. The callback function + * will be called DURING THE TOP HALF OF AN INTERRUPT HANDLER and + * will be passed the arbitrary callback_arg, an integer error + * indication and the length of the received packet. In HDLC mode, + * the packet will start with the address field, end with the + * information field, and will not contain flag or FCS bytes. + * Bit-stuffing will already have been corrected for. + * + * Possible values of second callback argument "error": + * + * 0 - successful reception + * non-0 - error value + * + * The callback routine should defer any time-consuming operations + * to a bottom-half handler; however, brecv may be called + * from within the callback to register another buffer and ensure + * continuous B channel reception without loss of data + * */ + +struct foreign_interface { + int (*get_irqnum)(int dev); + int (*get_liu_state)(int dev); + void (*liu_init)(int dev, void (*callback)(void *), void *callback_arg); + void (*liu_activate)(int dev, int priority); + void (*liu_deactivate)(int dev); + void (*dxmit)(int dev, __u8 *buffer, unsigned int count, + void (*callback)(void *, int), + void *callback_arg); + void (*drecv)(int dev, __u8 *buffer, unsigned int size, + void (*callback)(void *, int, unsigned int), + void *callback_arg); + int (*bopen)(int dev, unsigned int chan, + int hdlcmode, u_char xmit_idle_char); + void (*bclose)(int dev, unsigned int chan); + void (*bxmit)(int dev, unsigned int chan, + __u8 *buffer, unsigned long count, + void (*callback)(void *, int), + void *callback_arg); + void (*brecv)(int dev, unsigned int chan, + __u8 *buffer, unsigned long size, + void (*callback)(void *, int, unsigned int), + void *callback_arg); + + struct foreign_interface *next; +}; + +extern struct foreign_interface amd7930_foreign_interface; +extern struct foreign_interface dbri_foreign_interface; diff -u --recursive --new-file v2.2.3/linux/drivers/isdn/hisax/hisax.h linux/drivers/isdn/hisax/hisax.h --- v2.2.3/linux/drivers/isdn/hisax/hisax.h Wed Apr 1 20:11:50 1998 +++ linux/drivers/isdn/hisax/hisax.h Mon Mar 15 16:11:30 1999 @@ -397,7 +397,8 @@ u_char s_state; }; -struct amd7930_hw { +struct foreign_hw { + int doHDLCprocessing; u_char *tx_buff; u_char *rv_buff; int rv_buff_in; @@ -437,7 +438,7 @@ struct hscx_hw hscx; struct hfcB_hw hfc; struct tiger_hw tiger; - struct amd7930_hw amd7930; + struct foreign_hw foreign; } hw; }; @@ -640,6 +641,7 @@ struct njet_hw njet; struct hfcD_hw hfcD; struct ix1_hw niccy; + struct foreign_interface *foreign; } hw; int myid; isdn_if iif; @@ -710,8 +712,9 @@ #define ISDN_CTYPE_SEDLBAUER_PCMCIA 22 #define ISDN_CTYPE_AMD7930 23 #define ISDN_CTYPE_NICCY 24 +#define ISDN_CTYPE_DBRI 25 -#define ISDN_CTYPE_COUNT 24 +#define ISDN_CTYPE_COUNT 25 #ifdef ISDN_CHIP_ISAC #undef ISDN_CHIP_ISAC @@ -856,12 +859,18 @@ #define CARD_NICCY 0 #endif +#ifdef CONFIG_HISAX_DBRI +#define CARD_DBRI (1 << ISDN_CTYPE_DBRI) +#else +#define CARD_DBRI 0 +#endif + #define SUPORTED_CARDS (CARD_TELES0 | CARD_TELES3 | CARD_AVM_A1 | CARD_ELSA \ | CARD_IX1MICROR2 | CARD_DIEHLDIVA | CARD_ASUSCOM \ | CARD_TELEINT | CARD_SEDLBAUER | CARD_SPORTSTER \ | CARD_MIC | CARD_NETJET | CARD_TELES3C | CARD_AMD7930 \ - | CARD_NICCY) + | CARD_NICCY | CARD_DBRI) #define TEI_PER_CARD 0 diff -u --recursive --new-file v2.2.3/linux/drivers/isdn/hisax/isdnl1.c linux/drivers/isdn/hisax/isdnl1.c --- v2.2.3/linux/drivers/isdn/hisax/isdnl1.c Sun Nov 8 14:02:57 1998 +++ linux/drivers/isdn/hisax/isdnl1.c Mon Mar 15 16:11:30 1999 @@ -144,8 +144,8 @@ extern int setup_t163c(struct IsdnCard *card); #endif -#if CARD_AMD7930 -extern int setup_amd7930(struct IsdnCard *card); +#if CARD_AMD7930 || CARD_DBRI +extern int setup_foreign(struct IsdnCard *card); #endif #if CARD_NICCY @@ -163,7 +163,7 @@ "Elsa PCMCIA", "Eicon.Diehl Diva", "ISDNLink", "TeleInt", "Teles 16.3c", "Sedlbauer Speed Card", "USR Sportster", "ith mic Linux", "Elsa PCI", "Compaq ISA", "NETjet", "Teles PCI", "Sedlbauer Speed Star (PCMCIA)", - "AMD 7930", "NICCY" + "AMD 7930", "NICCY", "DBRI" }; extern struct IsdnCard cards[]; @@ -366,7 +366,7 @@ void debugl1(struct IsdnCardState *cs, char *msg) { - char tmp[256], tm[32]; + char tmp[1024], tm[32]; jiftime(tm, jiffies); sprintf(tmp, "%s Card %d %s\n", tm, cs->cardnr + 1, msg); @@ -842,9 +842,10 @@ ret = setup_niccy(card); break; #endif -#if CARD_AMD7930 +#if CARD_AMD7930 || CARD_DBRI case ISDN_CTYPE_AMD7930: - ret = setup_amd7930(card); + case ISDN_CTYPE_DBRI: + ret = setup_foreign(card); break; #endif default: diff -u --recursive --new-file v2.2.3/linux/drivers/macintosh/adb.c linux/drivers/macintosh/adb.c --- v2.2.3/linux/drivers/macintosh/adb.c Thu Nov 19 09:56:28 1998 +++ linux/drivers/macintosh/adb.c Wed Mar 10 21:48:46 1999 @@ -4,6 +4,8 @@ * * Copyright (C) 1996 Paul Mackerras. */ + +#include #include #include #include @@ -184,8 +186,13 @@ return adb_send_request(req, flags & ADBREQ_SYNC); } -/* Ultimately this should return the number of devices with - the given default id. */ + /* Ultimately this should return the number of devices with + the given default id. + And it does it now ! Note: changed behaviour: This function + will now register if default_id _and_ handler_id both match + but handler_id can be left to 0 to match with default_id only. + When handler_id is set, this function will try to adjust + the handler_id id it doesn't match. */ int adb_register(int default_id, int handler_id, struct adb_ids *ids, void (*handler)(unsigned char *, int, struct pt_regs *, int)) @@ -194,18 +201,20 @@ ids->nids = 0; for (i = 1; i < 16; i++) { - if (adb_handler[i].original_address == default_id) { + if ((adb_handler[i].original_address == default_id) && + (!handler_id || (handler_id == adb_handler[i].handler_id) || + adb_try_handler_change(i, handler_id))) { if (adb_handler[i].handler != 0) { printk(KERN_ERR "Two handlers for ADB device %d\n", default_id); - return 0; + continue; } adb_handler[i].handler = handler; ids->id[ids->nids++] = i; } } - return 1; + return ids->nids; } void @@ -225,6 +234,41 @@ (*adb_handler[id].handler)(buf, nb, regs, autopoll); } } + +/* Try to change handler to new_id. Will return 1 if successful */ +int +adb_try_handler_change(int address, int new_id) +{ + struct adb_request req; + + if (adb_handler[address].handler_id == new_id) + return 1; + adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1, + ADB_READREG(address,3)); + if (req.reply_len < 2) + return 0; + adb_request(&req, NULL, ADBREQ_SYNC, 3, + ADB_WRITEREG(address, 3), req.reply[1], new_id); + adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1, + ADB_READREG(address, 3)); + if (req.reply_len < 2) + return 0; + if (req.reply[2] != new_id) + return 0; + adb_handler[address].handler_id = req.reply[2]; + + return 1; +} + +int +adb_get_infos(int address, int *original_address, int *handler_id) +{ + *original_address = adb_handler[address].original_address; + *handler_id = adb_handler[address].handler_id; + + return (*original_address != 0); +} + /* * /dev/adb device driver. diff -u --recursive --new-file v2.2.3/linux/drivers/macintosh/mac_keyb.c linux/drivers/macintosh/mac_keyb.c --- v2.2.3/linux/drivers/macintosh/mac_keyb.c Thu Nov 19 09:56:28 1998 +++ linux/drivers/macintosh/mac_keyb.c Wed Mar 10 21:48:46 1999 @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -174,6 +175,12 @@ static void leds_done(struct adb_request *); static void mac_put_queue(int); +static void buttons_input(unsigned char *, int, struct pt_regs *, int); + +static void init_trackpad(int id); +static void init_trackball(int id); +static void init_turbomouse(int id); + #ifdef CONFIG_ADBMOUSE /* XXX: Hook for mouse driver */ void (*adb_mouse_interrupt_hook)(unsigned char *, int); @@ -191,6 +198,17 @@ static struct adb_ids keyboard_ids; static struct adb_ids mouse_ids; +static struct adb_ids buttons_ids; + +/* Kind of mouse */ +#define ADBMOUSE_STANDARD_100 0 /* Standard 100cpi mouse (handler 1) */ +#define ADBMOUSE_STANDARD_200 1 /* Standard 200cpi mouse (handler 2) */ +#define ADBMOUSE_EXTENDED 2 /* Apple Extended mouse (handler 4) */ +#define ADBMOUSE_TRACKBALL 3 /* TrackBall (handler 4) */ +#define ADBMOUSE_TRACKPAD 4 /* Apple's PowerBook trackpad (handler 4) */ +#define ADBMOUSE_TURBOMOUSE5 5 /* Turbomouse 5 (previously req. mousehack) */ + +static int adb_mouse_kinds[16]; /* this map indicates which keys shouldn't autorepeat. */ static unsigned char dont_repeat[128] = { @@ -198,7 +216,7 @@ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* esc...option */ - 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, /* num lock */ + 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, /* fn, num lock */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, /* scroll lock */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -423,6 +441,16 @@ */ struct kbd_struct *kbd; + /* If it's a trackpad, we alias the second button to the first. + NOTE: Apple sends an ADB flush command to the trackpad when + the first (the real) button is released. We could do + this here using async flush requests. + */ + if (adb_mouse_kinds[(data[0]>>4) & 0xf] == ADBMOUSE_TRACKPAD) { + data[1] = (data[1] & 0x7f) | ((data[1] & data[2]) & 0x80); + data[2] = (data[2] & 0x7f) | 0x80; + } + if (adb_mouse_interrupt_hook) adb_mouse_interrupt_hook(data, nb); @@ -467,6 +495,64 @@ } #endif /* CONFIG_ADBMOUSE */ +/* XXX Needs to get rid of this, see comments in pmu.c */ +extern int backlight_level; + +static void +buttons_input(unsigned char *data, int nb, struct pt_regs *regs, int autopoll) +{ + /* + * XXX: Where is the contrast control for the passive? + * -- Cort + */ + + /* Ignore data from register other than 0 */ + if ((adb_hardware != ADB_VIAPMU) || (data[0] & 0x3) || (nb < 2)) + return; + + switch (data[1]&0xf ) + { + /* mute */ + case 0x8: + /* down event */ + if ( data[1] == (data[1]&0xf) ) { + } + break; + /* contrast decrease */ + case 0x7: + /* down event */ + if ( data[1] == (data[1]&0xf) ) { + } + break; + /* contrast increase */ + case 0x6: + /* down event */ + if ( data[1] == (data[1]&0xf) ) { + } + break; + /* brightness decrease */ + case 0xa: + /* down event */ + if ( data[1] == (data[1]&0xf) ) { + if (backlight_level > 2) + pmu_set_brightness(backlight_level-2); + else + pmu_set_brightness(0); + } + break; + /* brightness increase */ + case 0x9: + /* down event */ + if ( data[1] == (data[1]&0xf) ) { + if (backlight_level < 0x1e) + pmu_set_brightness(backlight_level+2); + else + pmu_set_brightness(0x1f); + } + break; + } +} + /* Map led flags as defined in kbd_kern.h to bits for Apple keyboard. */ static unsigned char mac_ledmap[8] = { 0, /* none */ @@ -546,10 +632,11 @@ /* initialize mouse interrupt hook */ adb_mouse_interrupt_hook = NULL; - adb_register(ADB_MOUSE, 1, &mouse_ids, mouse_input); + adb_register(ADB_MOUSE, 0, &mouse_ids, mouse_input); #endif /* CONFIG_ADBMOUSE */ - adb_register(ADB_KEYBOARD, 5, &keyboard_ids, keyboard_input); + adb_register(ADB_KEYBOARD, 0, &keyboard_ids, keyboard_input); + adb_register(0x07, 0x1F, &buttons_ids, buttons_input); for(i = 0; i < keyboard_ids.nids; i++) { /* turn off all leds */ @@ -557,54 +644,173 @@ ADB_WRITEREG(keyboard_ids.id[i], KEYB_LEDREG), 0xff, 0xff); } - /* get the keyboard to send separate codes for - left and right shift, control, option keys. */ + /* Enable full feature set of the keyboard + ->get it to send separate codes for left and right shift, + control, option keys */ for(i = 0;i < keyboard_ids.nids; i++) { - /* get the keyboard to send separate codes for - left and right shift, control, option keys. */ - adb_request(&req, NULL, ADBREQ_SYNC, 3, - ADB_WRITEREG(keyboard_ids.id[i], 3), 0, 3); + if (adb_try_handler_change(keyboard_ids.id[i], 5)) + printk("ADB keyboard at %d, handler set to 5\n", keyboard_ids.id[i]); + else if (adb_try_handler_change(keyboard_ids.id[i], 3)) + printk("ADB keyboard at %d, handler set to 3\n", keyboard_ids.id[i]); + else + printk("ADB keyboard at %d, handler 1\n", keyboard_ids.id[i]); } led_request.complete = 1; - /* Try to switch the mouse (id 3) to handler 4, for three-button - mode. (0x20 is Service Request Enable, 0x03 is Device ID). */ + /* Try to switch all mice to handler 4, or 2 for three-button + mode and full resolution. */ for(i = 0; i < mouse_ids.nids; i++) { + if (adb_try_handler_change(mouse_ids.id[i], 4)) { + printk("ADB mouse at %d, handler set to 4", mouse_ids.id[i]); + adb_mouse_kinds[mouse_ids.id[i]] = ADBMOUSE_EXTENDED; + } + else if (adb_try_handler_change(mouse_ids.id[i], 2)) { + printk("ADB mouse at %d, handler set to 2", mouse_ids.id[i]); + adb_mouse_kinds[mouse_ids.id[i]] = ADBMOUSE_STANDARD_200; + } + else { + printk("ADB mouse at %d, handler 1", mouse_ids.id[i]); + adb_mouse_kinds[mouse_ids.id[i]] = ADBMOUSE_STANDARD_100; + } + + /* Register 1 is usually used for device identification. + Here, we try to identify a known device and call the + appropriate init function */ adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1, ADB_READREG(mouse_ids.id[i], 1)); - if ((req.reply_len) && - (req.reply[1] == 0x9a) && (req.reply[2] == 0x21)) { + if ((req.reply_len) && + (req.reply[1] == 0x9a) && (req.reply[2] == 0x21)) + init_trackball(mouse_ids.id[i]); + else if ((req.reply_len >= 4) && + (req.reply[1] == 0x74) && (req.reply[2] == 0x70) && + (req.reply[3] == 0x61) && (req.reply[4] == 0x64)) + init_trackpad(mouse_ids.id[i]); + else if ((req.reply_len >= 4) && + (req.reply[1] == 0x4b) && (req.reply[2] == 0x4d) && + (req.reply[3] == 0x4c) && (req.reply[4] == 0x31)) + init_turbomouse(mouse_ids.id[i]); + printk("\n"); + } +} + +__init static void +init_trackpad(int id) +{ + struct adb_request req; + unsigned char r1_buffer[8]; + + printk(" (trackpad)"); + + adb_mouse_kinds[id] = ADBMOUSE_TRACKPAD; + + adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1, + ADB_READREG(id,1)); + if (req.reply_len < 8) + printk("bad length for reg. 1\n"); + else + { + memcpy(r1_buffer, &req.reply[1], 8); + adb_request(&req, NULL, ADBREQ_SYNC, 9, + ADB_WRITEREG(id,1), + r1_buffer[0], + r1_buffer[1], + r1_buffer[2], + r1_buffer[3], + r1_buffer[4], + r1_buffer[5], + 0x0d, /*r1_buffer[6],*/ + r1_buffer[7]); + + adb_request(&req, NULL, ADBREQ_SYNC, 9, + ADB_WRITEREG(id,2), + 0x99, + 0x94, + 0x19, + 0xff, + 0xb2, + 0x8a, + 0x1b, + 0x50); + + adb_request(&req, NULL, ADBREQ_SYNC, 9, + ADB_WRITEREG(id,1), + r1_buffer[0], + r1_buffer[1], + r1_buffer[2], + r1_buffer[3], + r1_buffer[4], + r1_buffer[5], + 0x03, /*r1_buffer[6],*/ + r1_buffer[7]); + } +} + +__init static void +init_trackball(int id) +{ + struct adb_request req; + + printk(" (trackball)"); + + adb_mouse_kinds[id] = ADBMOUSE_TRACKBALL; - printk("aha, trackball found at %d\n", mouse_ids.id[i]); + adb_request(&req, NULL, ADBREQ_SYNC, 3, + ADB_WRITEREG(id,1), 00,0x81); - adb_request(&req, NULL, ADBREQ_SYNC, 3, - ADB_WRITEREG(mouse_ids.id[i], 3), 0x63, 4 ); + adb_request(&req, NULL, ADBREQ_SYNC, 3, + ADB_WRITEREG(id,1), 01,0x81); - adb_request(&req, NULL, ADBREQ_SYNC, 3, - ADB_WRITEREG(mouse_ids.id[i],1), 00,0x81); + adb_request(&req, NULL, ADBREQ_SYNC, 3, + ADB_WRITEREG(id,1), 02,0x81); - adb_request(&req, NULL, ADBREQ_SYNC, 3, - ADB_WRITEREG(mouse_ids.id[i],1), 01,0x81); + adb_request(&req, NULL, ADBREQ_SYNC, 3, + ADB_WRITEREG(id,1), 03,0x38); - adb_request(&req, NULL, ADBREQ_SYNC, 3, - ADB_WRITEREG(mouse_ids.id[i],1), 02,0x81); + adb_request(&req, NULL, ADBREQ_SYNC, 3, + ADB_WRITEREG(id,1), 00,0x81); - adb_request(&req, NULL, ADBREQ_SYNC, 3, - ADB_WRITEREG(mouse_ids.id[i],1), 03,0x38); + adb_request(&req, NULL, ADBREQ_SYNC, 3, + ADB_WRITEREG(id,1), 01,0x81); - adb_request(&req, NULL, ADBREQ_SYNC, 3, - ADB_WRITEREG(mouse_ids.id[i],1), 00,0x81); + adb_request(&req, NULL, ADBREQ_SYNC, 3, + ADB_WRITEREG(id,1), 02,0x81); + + adb_request(&req, NULL, ADBREQ_SYNC, 3, + ADB_WRITEREG(id,1), 03,0x38); +} - adb_request(&req, NULL, ADBREQ_SYNC, 3, - ADB_WRITEREG(mouse_ids.id[i],1), 01,0x81); +__init static void +init_turbomouse(int id) +{ + struct adb_request req; - adb_request(&req, NULL, ADBREQ_SYNC, 3, - ADB_WRITEREG(mouse_ids.id[i],1), 02,0x81); + printk(" (TurboMouse 5)"); - adb_request(&req, NULL, ADBREQ_SYNC, 3, - ADB_WRITEREG(mouse_ids.id[i],1), 03,0x38); - } - } + adb_mouse_kinds[id] = ADBMOUSE_TURBOMOUSE5; + + adb_request(&req, NULL, ADBREQ_SYNC, 9, + ADB_WRITEREG(id,2), + 0xe7, + 0x8c, + 0, + 0, + 0, + 0xff, + 0xff, + 0x94); + + adb_request(&req, NULL, ADBREQ_SYNC, 1, ADB_FLUSH(id)); + + adb_request(&req, NULL, ADBREQ_SYNC, 9, + ADB_WRITEREG(id,2), + 0xa5, + 0x14, + 0, + 0, + 0x69, + 0xff, + 0xff, + 0x27); } diff -u --recursive --new-file v2.2.3/linux/drivers/macintosh/macserial.c linux/drivers/macintosh/macserial.c --- v2.2.3/linux/drivers/macintosh/macserial.c Mon Dec 28 15:00:52 1998 +++ linux/drivers/macintosh/macserial.c Wed Mar 10 21:48:46 1999 @@ -34,6 +34,8 @@ #include #include #include +#include +#include #ifdef CONFIG_KGDB #include #endif @@ -62,6 +64,8 @@ struct tty_struct zs_ttys[NUM_CHANNELS]; +static int is_powerbook; + #ifdef CONFIG_SERIAL_CONSOLE static struct console sercons; #endif @@ -99,6 +103,10 @@ #undef SERIAL_DEBUG_INTR #undef SERIAL_DEBUG_OPEN #undef SERIAL_DEBUG_FLOW +#undef SERIAL_DEBUG_POWER +#undef SERIAL_DEBUG_THROTTLE +#undef SERIAL_DEBUG_STOP +#undef SERIAL_DEBUG_BAUDS #define RS_STROBE_TIME 10 #define RS_ISR_PASS_LIMIT 256 @@ -106,8 +114,10 @@ #define _INLINE_ inline static void probe_sccs(void); -static void change_speed(struct mac_serial *info); +static void change_speed(struct mac_serial *info, struct termios *old); static void rs_wait_until_sent(struct tty_struct *tty, int timeout); +static void set_scc_power(struct mac_serial * info, int state); +static int setup_scc(struct mac_serial * info); static struct tty_struct *serial_table[NUM_CHANNELS]; static struct termios *serial_termios[NUM_CHANNELS]; @@ -152,13 +162,6 @@ return 0; } -/* - * This is used to figure out the divisor speeds and the timeouts - */ -static int baud_table[] = { - 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, - 9600, 19200, 38400, 57600, 115200, 230400, 0 }; - /* * Reading and writing Z8530 registers. */ @@ -320,12 +323,14 @@ #endif if (!tty) continue; - tty_flip_buffer_push(tty); - + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + tty_flip_buffer_push(tty); + if (tty->flip.count >= TTY_FLIPBUF_SIZE) { static int flip_buf_ovf; ++flip_buf_ovf; - continue; + printk("FB. overflow: %d\n", flip_buf_ovf); + break; } tty->flip.count++; { @@ -347,6 +352,8 @@ *tty->flip.flag_buf_ptr++ = flag; *tty->flip.char_buf_ptr++ = ch; } + if (tty) + tty_flip_buffer_push(tty); } static void transmit_chars(struct mac_serial *info) @@ -407,11 +414,17 @@ */ if ((status & CTS) == 0) { if (info->tx_stopped) { +#ifdef SERIAL_DEBUG_FLOW + printk("CTS up\n"); +#endif info->tx_stopped = 0; if (!info->tx_active) transmit_chars(info); } } else { +#ifdef SERIAL_DEBUG_FLOW + printk("CTS down\n"); +#endif info->tx_stopped = 1; } } @@ -446,12 +459,17 @@ for (;;) { zs_intreg = read_zsreg(info->zs_chan_a, 3) >> shift; #ifdef SERIAL_DEBUG_INTR -// printk("rs_interrupt: irq %d, zs_intreg 0x%x\n", irq, (int)zs_intreg); + printk("rs_interrupt: irq %d, zs_intreg 0x%x\n", irq, (int)zs_intreg); #endif if ((zs_intreg & CHAN_IRQMASK) == 0) break; + if (!(info->flags & ZILOG_INITIALIZED)) { + printk("rs_interrupt: irq %d, port not initialized\n", irq); + break; + } + if (zs_intreg & CHBRxIP) receive_chars(info, regs); if (zs_intreg & CHBTxIP) @@ -478,6 +496,11 @@ { struct mac_serial *info = (struct mac_serial *)tty->driver_data; +#ifdef SERIAL_DEBUG_STOP + printk("rs_stop %ld....\n", + tty->ldisc.chars_in_buffer(tty)); +#endif + if (serial_paranoia_check(info, tty->device, "rs_stop")) return; @@ -497,6 +520,11 @@ struct mac_serial *info = (struct mac_serial *)tty->driver_data; unsigned long flags; +#ifdef SERIAL_DEBUG_STOP + printk("rs_start %ld....\n", + tty->ldisc.chars_in_buffer(tty)); +#endif + if (serial_paranoia_check(info, tty->device, "rs_start")) return; @@ -552,10 +580,16 @@ static int startup(struct mac_serial * info) { - unsigned long flags; - - if (info->flags & ZILOG_INITIALIZED) - return 0; +#ifdef SERIAL_DEBUG_OPEN + printk("startup() (ttyS%d, irq %d)\n", info->line, info->irq); +#endif + + if (info->flags & ZILOG_INITIALIZED) { +#ifdef SERIAL_DEBUG_OPEN + printk(" -> already inited\n"); +#endif + return 0; + } if (!info->xmit_buf) { info->xmit_buf = (unsigned char *) get_free_page(GFP_KERNEL); @@ -563,12 +597,42 @@ return -ENOMEM; } - save_flags(flags); cli(); +#ifdef SERIAL_DEBUG_OPEN + printk("starting up ttyS%d (irq %d)...\n", info->line, info->irq); +#endif + + set_scc_power(info, 1); + + setup_scc(info); + +#ifdef SERIAL_DEBUG_OPEN + printk("enabling IRQ on ttyS%d (irq %d)...\n", info->line, info->irq); +#endif + + info->flags |= ZILOG_INITIALIZED; + enable_irq(info->irq); + + return 0; +} +static int setup_scc(struct mac_serial * info) +{ + unsigned long flags; + #ifdef SERIAL_DEBUG_OPEN - printk("starting up ttyS%d (irq %d)...", info->line, info->irq); + printk("setting up ttys%d SCC...\n", info->line); #endif + save_flags(flags); cli(); /* Disable interrupts */ + + /* + * Reset the chip. + */ + write_zsreg(info->zs_channel, 9, + (info->zs_channel == info->zs_chan_a? CHRA: CHRB)); + udelay(10); + write_zsreg(info->zs_channel, 9, 0); + /* * Clear the receive FIFO. */ @@ -608,13 +672,13 @@ /* * Set the speed of the serial port */ - change_speed(info); + change_speed(info, 0); /* Save the current value of RR0 */ info->read_reg_zero = read_zsreg(info->zs_channel, 0); - info->flags |= ZILOG_INITIALIZED; restore_flags(flags); + return 0; } @@ -624,22 +688,20 @@ */ static void shutdown(struct mac_serial * info) { - unsigned long flags; - - if (!(info->flags & ZILOG_INITIALIZED)) - return; - #ifdef SERIAL_DEBUG_OPEN - printk("Shutting down serial port %d (irq %d)....", info->line, + printk("Shutting down serial port %d (irq %d)....\n", info->line, info->irq); #endif - save_flags(flags); cli(); /* Disable interrupts */ + if (!(info->flags & ZILOG_INITIALIZED)) { +#ifdef SERIAL_DEBUG_OPEN + printk("(already shutdown)\n"); +#endif - if (info->xmit_buf) { - free_page((unsigned long) info->xmit_buf); - info->xmit_buf = 0; + return; } + + disable_irq(info->irq); info->pendregs[1] = info->curregs[1] = 0; write_zsreg(info->zs_channel, 1, 0); /* no interrupts */ @@ -657,34 +719,156 @@ if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); + set_scc_power(info, 0); + + if (info->xmit_buf) { + free_page((unsigned long) info->xmit_buf); + info->xmit_buf = 0; + } + + memset(info->curregs, 0, sizeof(info->curregs)); + memset(info->curregs, 0, sizeof(info->pendregs)); + info->flags &= ~ZILOG_INITIALIZED; - restore_flags(flags); } +static void set_scc_power(struct mac_serial * info, int state) +{ + if (feature_test(info->dev_node, FEATURE_Serial_enable) < 0) + return; /* don't have serial power control */ + + /* The timings looks strange but that's the ones MacOS seems + to use for the internal modem. I think we can use a lot faster + ones, at least whe not using the modem, this should be tested. + */ + if (state) { +#ifdef SERIAL_DEBUG_POWER + printk(KERN_INFO "ttyS%02d: powering up hardware\n", info->line); +#endif + if (feature_test(info->dev_node, FEATURE_Serial_enable) == 0) { + feature_clear(info->dev_node, FEATURE_Serial_reset); + mdelay(5); + feature_set(info->dev_node, FEATURE_Serial_enable); + } + if (info->zs_chan_a == info->zs_channel) + feature_set(info->dev_node, FEATURE_Serial_IO_A); + else + feature_set(info->dev_node, FEATURE_Serial_IO_B); + mdelay(1); + + if (info->is_cobalt_modem){ + feature_set(info->dev_node, FEATURE_Modem_Reset); + mdelay(15); + feature_clear(info->dev_node, FEATURE_Modem_Reset); + /* XXX Note the big 250ms, we should probably replace this + by something better since we have irqs disabled here + */ + mdelay(250); + } + if (info->is_pwbk_ir) + pmu_enable_irled(1); + } else { +#ifdef SERIAL_DEBUG_POWER + printk(KERN_INFO "ttyS%02d: shutting down hardware\n", info->line); +#endif +#ifdef CONFIG_KGDB + if (info->kgdb_channel) { +#ifdef SERIAL_DEBUG_POWER + printk(KERN_INFO " (canceled by KGDB)\n"); +#endif + return; + } +#endif +#ifdef CONFIG_XMON + if (!info->is_cobalt_modem) { +#ifdef SERIAL_DEBUG_POWER + printk(KERN_INFO " (canceled by XMON)\n"); +#endif + return; + } +#endif + if (info->is_cobalt_modem) { +#ifdef SERIAL_DEBUG_POWER + printk(KERN_INFO "ttyS%02d: shutting down modem\n", info->line); +#endif + feature_set(info->dev_node, FEATURE_Modem_Reset); + mdelay(15); + feature_clear(info->dev_node, FEATURE_Modem_Reset); + mdelay(25); + } + if (info->is_pwbk_ir) + pmu_enable_irled(0); + + if (info->zs_chan_a == info->zs_channel) { +#ifdef SERIAL_DEBUG_POWER + printk(KERN_INFO "ttyS%02d: shutting down SCC channel A\n", info->line); +#endif + feature_clear(info->dev_node, FEATURE_Serial_IO_A); + } else { +#ifdef SERIAL_DEBUG_POWER + printk(KERN_INFO "ttyS%02d: shutting down SCC channel B\n", info->line); +#endif + feature_clear(info->dev_node, FEATURE_Serial_IO_B); + } + /* XXX for now, shut down SCC core only on powerbooks */ + if (is_powerbook + && !(feature_test(info->dev_node, FEATURE_Serial_IO_A) || + feature_test(info->dev_node, FEATURE_Serial_IO_B))) { +#ifdef SERIAL_DEBUG_POWER + printk(KERN_INFO "ttyS%02d: shutting down SCC core\n", info->line); +#endif + feature_set(info->dev_node, FEATURE_Serial_reset); + mdelay(10); + feature_clear(info->dev_node, FEATURE_Serial_enable); + mdelay(5); + } + } +} + + /* * This routine is called to set the UART divisor registers to match * the specified baud rate for a serial port. */ -static void change_speed(struct mac_serial *info) +static void change_speed(struct mac_serial *info, struct termios *old_termios) { unsigned short port; unsigned cflag; - int i; - int brg; + int bits; + int brg, baud; unsigned long flags; if (!info->tty || !info->tty->termios) return; - cflag = info->tty->termios->c_cflag; if (!(port = info->port)) return; - i = cflag & CBAUD; + + cflag = info->tty->termios->c_cflag; + baud = tty_get_baud_rate(info->tty); + if (baud == 0) { + if (old_termios) { + info->tty->termios->c_cflag &= ~CBAUD; + info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD); + cflag = info->tty->termios->c_cflag; + baud = tty_get_baud_rate(info->tty); + } + else + baud = info->zs_baud; + } + if (baud > 230400) + baud = 230400; + else if (baud == 0) + baud = 38400; save_flags(flags); cli(); - info->zs_baud = baud_table[i]; + info->zs_baud = baud; info->clk_divisor = 16; - switch (info->zs_baud) { +#ifdef SERIAL_DEBUG_BAUDS + printk("set speed to %d bds, ", baud); +#endif + + switch (baud) { case ZS_CLOCK/16: /* 230400 */ info->curregs[4] = X16CLK; info->curregs[11] = 0; @@ -696,7 +880,7 @@ default: info->curregs[4] = X16CLK; info->curregs[11] = TCBR | RCBR; - brg = BPS_TO_BRG(info->zs_baud, ZS_CLOCK/info->clk_divisor); + brg = BPS_TO_BRG(baud, ZS_CLOCK/info->clk_divisor); info->curregs[12] = (brg & 255); info->curregs[13] = ((brg >> 8) & 255); info->curregs[14] = BRENABL; @@ -709,19 +893,35 @@ case CS5: info->curregs[3] |= Rx5; info->curregs[5] |= Tx5; +#ifdef SERIAL_DEBUG_BAUDS + printk("5 bits, "); +#endif + bits = 7; break; case CS6: info->curregs[3] |= Rx6; info->curregs[5] |= Tx6; +#ifdef SERIAL_DEBUG_BAUDS + printk("6 bits, "); +#endif + bits = 8; break; case CS7: info->curregs[3] |= Rx7; info->curregs[5] |= Tx7; +#ifdef SERIAL_DEBUG_BAUDS + printk("7 bits, "); +#endif + bits = 9; break; case CS8: default: /* defaults to 8 bits */ info->curregs[3] |= Rx8; info->curregs[5] |= Tx8; +#ifdef SERIAL_DEBUG_BAUDS + printk("8 bits, "); +#endif + bits = 10; break; } info->pendregs[3] = info->curregs[3]; @@ -730,11 +930,22 @@ info->curregs[4] &= ~(SB_MASK | PAR_ENA | PAR_EVEN); if (cflag & CSTOPB) { info->curregs[4] |= SB2; + bits++; +#ifdef SERIAL_DEBUG_BAUDS + printk("2 stop, "); +#endif } else { info->curregs[4] |= SB1; +#ifdef SERIAL_DEBUG_BAUDS + printk("1 stop, "); +#endif } if (cflag & PARENB) { - info->curregs[4] |= PAR_ENA; + bits++; + info->curregs[4] |= PAR_ENA; +#ifdef SERIAL_DEBUG_BAUDS + printk("parity, "); +#endif } if (!(cflag & PARODD)) { info->curregs[4] |= PAR_EVEN; @@ -757,6 +968,17 @@ } info->pendregs[15] = info->curregs[15]; + /* Calc timeout value. This is pretty broken with high baud rates with HZ=100. + This code would love a larger HZ and a >1 fifo size, but this is not + a priority. The resulting value must be >HZ/2 + */ + info->timeout = ((info->xmit_fifo_size*HZ*bits) / baud); + info->timeout += HZ/50+1; /* Add .02 seconds of slop */ + +#ifdef SERIAL_DEBUG_BAUDS + printk("timeout=%d/%ds, base:%d\n", (int)info->timeout, (int)HZ, (int)info->baud_base); +#endif + /* Load up the new values */ load_zsregs(info->zs_channel, info->curregs); @@ -877,8 +1099,7 @@ #ifdef SERIAL_DEBUG_THROTTLE char buf[64]; - printk("throttle %s: %d....\n", _tty_name(tty, buf), - tty->ldisc.chars_in_buffer(tty)); + printk("throttle %ld....\n",tty->ldisc.chars_in_buffer(tty)); #endif if (serial_paranoia_check(info, tty->device, "rs_throttle")) @@ -915,8 +1136,7 @@ #ifdef SERIAL_DEBUG_THROTTLE char buf[64]; - printk("unthrottle %s: %d....\n", _tty_name(tty, buf), - tty->ldisc.chars_in_buffer(tty)); + printk("unthrottle %s: %d....\n",tty->ldisc.chars_in_buffer(tty)); #endif if (serial_paranoia_check(info, tty->device, "rs_unthrottle")) @@ -1011,7 +1231,8 @@ info->closing_wait = new_serial.closing_wait; check_and_exit: - retval = startup(info); + if (info->flags & ZILOG_INITIALIZED) + retval = setup_scc(info); return retval; } @@ -1180,10 +1401,12 @@ return; was_stopped = info->tx_stopped; - change_speed(info); + change_speed(info, old_termios); - if (was_stopped && !info->tx_stopped) + if (was_stopped && !info->tx_stopped) { + tty->hw_stopped = 0; rs_start(tty); + } } /* @@ -1246,6 +1469,9 @@ * Now we wait for the transmit buffer to clear; and we notify * the line discipline to only process XON/XOFF characters. */ +#ifdef SERIAL_DEBUG_OPEN + printk("waiting end of Tx... (timeout:%d)\n", info->closing_wait); +#endif tty->closing = 1; if (info->closing_wait != ZILOG_CLOSING_WAIT_NONE) tty_wait_until_sent(tty, info->closing_wait); @@ -1265,10 +1491,17 @@ * Before we drop DTR, make sure the SCC transmitter * has completely drained. */ +#ifdef SERIAL_DEBUG_OPEN + printk("waiting end of Rx...\n"); +#endif rs_wait_until_sent(tty, info->timeout); } shutdown(info); + /* restore flags now since shutdown() will have disabled this port's + specific irqs */ + restore_flags(flags); + if (tty->driver.flush_buffer) tty->driver.flush_buffer(tty); if (tty->ldisc.flush_buffer) @@ -1277,14 +1510,6 @@ info->event = 0; info->tty = 0; - if (info->is_cobalt_modem) { - /* Power down modem */ - feature_set(info->dev_node, FEATURE_Modem_Reset); - mdelay(15); - feature_clear(info->dev_node, FEATURE_Modem_PowerOn); - mdelay(15); - } - if (info->blocked_open) { if (info->close_delay) { current->state = TASK_INTERRUPTIBLE; @@ -1295,8 +1520,6 @@ info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE| ZILOG_CLOSING); wake_up_interruptible(&info->close_wait); - - restore_flags(flags); } /* @@ -1310,15 +1533,26 @@ if (serial_paranoia_check(info, tty->device, "rs_wait_until_sent")) return; +/* printk("rs_wait_until_sent, timeout:%d, tty_stopped:%d, tx_stopped:%d\n", + timeout, tty->stopped, info->tx_stopped); +*/ orig_jiffies = jiffies; /* * Set the check interval to be 1/5 of the estimated time to * send a single character, and make it at least 1. The check * interval should also be less than the timeout. */ + if (info->timeout <= HZ/50) { + printk("macserial: invalid info->timeout=%d\n", info->timeout); + info->timeout = HZ/50+1; + } + char_time = (info->timeout - HZ/50) / info->xmit_fifo_size; char_time = char_time / 5; - if (char_time == 0) + if (char_time > HZ) { + printk("macserial: char_time %ld >HZ !!!\n", char_time); + char_time = 1; + } else if (char_time == 0) char_time = 1; if (timeout) char_time = MIN(char_time, timeout); @@ -1534,38 +1768,16 @@ * Start up serial port */ - if (info->is_cobalt_modem) { - /* Power up modem */ - feature_set(info->dev_node, FEATURE_Modem_PowerOn); - mdelay(250); - feature_clear(info->dev_node, FEATURE_Modem_Reset); - mdelay(10); - } retval = startup(info); - if (retval) { - if (info->is_cobalt_modem) { - /* Power down modem */ - feature_set(info->dev_node, FEATURE_Modem_Reset); - mdelay(15); - feature_clear(info->dev_node, FEATURE_Modem_PowerOn); - mdelay(15); - } + if (retval) return retval; - } retval = block_til_ready(tty, filp, info); if (retval) { #ifdef SERIAL_DEBUG_OPEN printk("rs_open returning after block_til_ready with %d\n", - retval); + retval); #endif - if (info->is_cobalt_modem) { - /* Power down modem */ - feature_set(info->dev_node, FEATURE_Modem_Reset); - mdelay(15); - feature_clear(info->dev_node, FEATURE_Modem_PowerOn); - mdelay(15); - } return retval; } @@ -1574,13 +1786,13 @@ *tty->termios = info->normal_termios; else *tty->termios = info->callout_termios; - change_speed(info); + change_speed(info, 0); } #ifdef CONFIG_SERIAL_CONSOLE if (sercons.cflag && sercons.index == line) { tty->termios->c_cflag = sercons.cflag; sercons.cflag = 0; - change_speed(info); + change_speed(info, 0); } #endif @@ -1588,7 +1800,7 @@ info->pgrp = current->pgrp; #ifdef SERIAL_DEBUG_OPEN - printk("rs_open ttys%d successful...", info->line); + printk("rs_open ttys%d successful...\n", info->line); #endif return 0; } @@ -1597,7 +1809,7 @@ static void show_serial_version(void) { - printk("PowerMac Z8530 serial driver version 1.00\n"); + printk("PowerMac Z8530 serial driver version 1.01\n"); } /* Ask the PROM how many Z8530s we have and initialize their zs_channels */ @@ -1606,7 +1818,7 @@ { struct device_node *dev, *ch; struct mac_serial **pp; - int n; + int n, lenp; n = 0; pp = &zs_chain; @@ -1616,12 +1828,6 @@ dev->full_name); continue; } - feature_clear(dev, FEATURE_Serial_reset); - mdelay(5); - feature_set(dev, FEATURE_Serial_enable); - feature_set(dev, FEATURE_Serial_IO_A); - feature_set(dev, FEATURE_Serial_IO_B); - mdelay(5); for (ch = dev->child; ch != 0; ch = ch->sibling) { if (ch->n_addrs < 1 || (ch ->n_intrs < 1)) { printk("Can't use %s: %d addrs %d intrs\n", @@ -1630,21 +1836,17 @@ } zs_channels[n].control = (volatile unsigned char *) ioremap(ch->addrs[0].address, 0x1000); - zs_channels[n].data = zs_channels[n].control - + ch->addrs[0].size / 2; + zs_channels[n].data = zs_channels[n].control + 0x10; spin_lock_init(&zs_channels[n].lock); zs_soft[n].zs_channel = &zs_channels[n]; zs_soft[n].dev_node = ch; zs_soft[n].irq = ch->intrs[0].line; + zs_soft[n].zs_channel->parent = &zs_soft[n]; zs_soft[n].is_cobalt_modem = device_is_compatible(ch, "cobalt"); - if (zs_soft[n].is_cobalt_modem) - { - /* Just in case the modem is up, shut it down */ - feature_set(ch, FEATURE_Modem_Reset); - mdelay(15); - feature_clear(ch, FEATURE_Modem_PowerOn); - mdelay(15); - } + + /* XXX tested only with wallstreet PowerBook, should do no harm anyway */ + zs_soft[n].is_pwbk_ir = (strcmp(get_property(ch, "AAPL,connector", + &lenp), "infrared") == 0); /* XXX this assumes the prom puts chan A before B */ if (n & 1) @@ -1677,13 +1879,19 @@ if (zs_chain == 0) probe_sccs(); + /* XXX assume it's a powerbook if we have a via-pmu */ + is_powerbook = find_devices("via-pmu") != 0; + /* Register the interrupt handler for each one */ + save_flags(flags); cli(); for (i = 0; i < zs_channels_found; ++i) { if (request_irq(zs_soft[i].irq, rs_interrupt, 0, "SCC", &zs_soft[i])) printk(KERN_ERR "macserial: can't get irq %d\n", zs_soft[i].irq); + disable_irq(zs_soft[i].irq); } + restore_flags(flags); show_serial_version(); @@ -1739,8 +1947,6 @@ if (tty_register_driver(&callout_driver)) panic("Couldn't register callout driver\n"); - save_flags(flags); cli(); - for (channel = 0; channel < zs_channels_found; ++channel) { #ifdef CONFIG_KGDB if (zs_soft[channel].kgdb_channel) { @@ -1749,10 +1955,15 @@ } #endif zs_soft[channel].clk_divisor = 16; - zs_soft[channel].zs_baud = get_zsbaud(&zs_soft[channel]); +/* -- we are not sure the SCC is powered ON at this point + zs_soft[channel].zs_baud = get_zsbaud(&zs_soft[channel]); +*/ + zs_soft[channel].zs_baud = 38400; /* If console serial line, then enable interrupts. */ if (zs_soft[channel].is_cons) { + printk("macserial: console line, enabling interrupt %d\n", zs_soft[channel].irq); + panic("macserial: console not supported yet !"); write_zsreg(zs_soft[channel].zs_channel, R1, (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB)); write_zsreg(zs_soft[channel].zs_channel, R9, @@ -1787,6 +1998,7 @@ info->normal_termios = serial_driver.init_termios; info->open_wait = 0; info->close_wait = 0; + info->timeout = HZ; printk("tty%02d at 0x%08x (irq = %d)", info->line, info->port, info->irq); printk(" is a Z8530 ESCC"); @@ -1795,10 +2007,21 @@ printk(", port = %s", connector); if (info->is_cobalt_modem) printk(" (cobalt modem)"); + if (info->is_pwbk_ir) + printk(" (powerbook IR)"); printk("\n"); - } - restore_flags(flags); +#ifdef CONFIG_KGDB + if (info->kgdb_channel) + continue; +#endif +#ifdef CONFIG_XMON + if (!info->is_cobalt_modem) + continue; +#endif + /* By default, disable the port */ + set_scc_power(info, 0); + } return 0; } @@ -2022,6 +2245,8 @@ if (zs_chain == 0) probe_sccs(); + set_scc_power(&zs_soft[n], 1); + zs_kgdbchan = zs_soft[tty_num].zs_channel; zs_soft[tty_num].change_needed = 0; zs_soft[tty_num].clk_divisor = 16; diff -u --recursive --new-file v2.2.3/linux/drivers/macintosh/macserial.h linux/drivers/macintosh/macserial.h --- v2.2.3/linux/drivers/macintosh/macserial.h Thu Nov 19 09:56:28 1998 +++ linux/drivers/macintosh/macserial.h Wed Mar 10 21:48:46 1999 @@ -38,27 +38,27 @@ /* * Definitions for ZILOG_struct (and serial_struct) flags field */ -#define ZILOG_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes - on the callout port */ -#define ZILOG_FOURPORT 0x0002 /* Set OU1, OUT2 per AST Fourport settings */ -#define ZILOG_SAK 0x0004 /* Secure Attention Key (Orange book) */ -#define ZILOG_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */ - -#define ZILOG_SPD_MASK 0x0030 -#define ZILOG_SPD_HI 0x0010 /* Use 56000 instead of 38400 bps */ - -#define ZILOG_SPD_VHI 0x0020 /* Use 115200 instead of 38400 bps */ -#define ZILOG_SPD_CUST 0x0030 /* Use user-specified divisor */ - -#define ZILOG_SKIP_TEST 0x0040 /* Skip UART test during autoconfiguration */ -#define ZILOG_AUTO_IRQ 0x0080 /* Do automatic IRQ during autoconfiguration */ -#define ZILOG_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */ -#define ZILOG_PGRP_LOCKOUT 0x0200 /* Lock out cua opens based on pgrp */ -#define ZILOG_CALLOUT_NOHUP 0x0400 /* Don't do hangups for cua device */ - -#define ZILOG_FLAGS 0x0FFF /* Possible legal ZILOG flags */ -#define ZILOG_USR_MASK 0x0430 /* Legal flags that non-privileged - * users can set or reset */ +#define ZILOG_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes + * on the callout port */ +#define ZILOG_FOURPORT 0x0002 /* Set OU1, OUT2 per AST Fourport settings */ +#define ZILOG_SAK 0x0004 /* Secure Attention Key (Orange book) */ +#define ZILOG_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */ + +#define ZILOG_SPD_MASK 0x0030 +#define ZILOG_SPD_HI 0x0010 /* Use 56000 instead of 38400 bps */ + +#define ZILOG_SPD_VHI 0x0020 /* Use 115200 instead of 38400 bps */ +#define ZILOG_SPD_CUST 0x0030 /* Use user-specified divisor */ + +#define ZILOG_SKIP_TEST 0x0040 /* Skip UART test during autoconfiguration */ +#define ZILOG_AUTO_IRQ 0x0080 /* Do automatic IRQ during autoconfiguration */ +#define ZILOG_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */ +#define ZILOG_PGRP_LOCKOUT 0x0200 /* Lock out cua opens based on pgrp */ +#define ZILOG_CALLOUT_NOHUP 0x0400 /* Don't do hangups for cua device */ + +#define ZILOG_FLAGS 0x0FFF /* Possible legal ZILOG flags */ +#define ZILOG_USR_MASK 0x0430 /* Legal flags that non-privileged + * users can set or reset */ /* Internal flags used only by kernel/chr_drv/serial.c */ #define ZILOG_INITIALIZED 0x80000000 /* Serial port was initialized */ @@ -81,10 +81,14 @@ * For definitions of the flags field, see tty.h */ +struct mac_serial; + struct mac_zschannel { - volatile unsigned char *control; - volatile unsigned char *data; - spinlock_t lock; + volatile unsigned char* control; + volatile unsigned char* data; + spinlock_t lock; + /* Used for debugging */ + struct mac_serial* parent; }; struct mac_serial { @@ -99,6 +103,7 @@ char kgdb_channel; /* Kgdb is running on this channel */ char is_cons; /* Is this our console. */ char is_cobalt_modem; /* is a gatwick-based cobalt modem */ + char is_pwbk_ir; /* is connected to an IR led on powerbooks */ unsigned char tx_active; /* character is being xmitted */ unsigned char tx_stopped; /* output is suspended */ diff -u --recursive --new-file v2.2.3/linux/drivers/macintosh/via-pmu.c linux/drivers/macintosh/via-pmu.c --- v2.2.3/linux/drivers/macintosh/via-pmu.c Mon Dec 28 15:00:52 1998 +++ linux/drivers/macintosh/via-pmu.c Wed Mar 10 21:48:46 1999 @@ -31,6 +31,7 @@ #include #include #include +#include /* Misc minor number allocated for /dev/pmu */ #define PMU_MINOR 154 @@ -89,8 +90,9 @@ static int adb_int_pending; static int pmu_adb_flags; static int adb_dev_map = 0; -static struct adb_request bright_req_1, bright_req_2; +static struct adb_request bright_req_1, bright_req_2, bright_req_3; static struct device_node *vias; +static int pmu_kind = PMU_UNKNOWN; int asleep; struct notifier_block *sleep_notifier_list; @@ -108,7 +110,6 @@ static void pmu_done(struct adb_request *req); static void pmu_handle_data(unsigned char *data, int len, struct pt_regs *regs); -static void set_brightness(int level); static void set_volume(int level); /* @@ -118,7 +119,7 @@ * - the number of response bytes which the PMU will return, or * -1 if it will send a length byte. */ -static s8 pmu_data_len[256][2] = { +static s8 pmu_data_len[256][2] __openfirmwaredata = { /* 0 1 2 3 4 5 6 7 */ /*00*/ {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, /*08*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, @@ -154,9 +155,8 @@ /*f8*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, }; -__openfirmware -void +void __openfirmware find_via_pmu() { vias = find_devices("via-pmu"); @@ -185,6 +185,15 @@ if (vias->n_addrs < 1 || vias->n_intrs < 1) return; } + + if (vias->parent->name && strcmp(vias->parent->name, "ohare") == 0 + || device_is_compatible(vias->parent, "ohare")) + pmu_kind = PMU_OHARE_BASED; + else if (device_is_compatible(vias->parent, "heathrow")) + pmu_kind = PMU_HEATHROW_BASED; + else + pmu_kind = PMU_UNKNOWN; + via = (volatile unsigned char *) ioremap(vias->addrs->address, 0x2000); out_8(&via[IER], IER_CLR | 0x7f); /* disable all intrs */ @@ -195,9 +204,15 @@ via = NULL; adb_hardware = ADB_VIAPMU; + + if (via) + printk(KERN_INFO "PMU driver initialized for %s\n", + (pmu_kind == PMU_OHARE_BASED) ? "PowerBook 2400/3400/3500(G3)" : + ((pmu_kind == PMU_HEATHROW_BASED) ? "PowerBook G3 Series" : + "Unknown PowerBook")); } -void +void __openfirmware via_pmu_init(void) { if (vias == NULL) @@ -205,6 +220,7 @@ bright_req_1.complete = 1; bright_req_2.complete = 1; + bright_req_3.complete = 1; if (request_irq(vias->intrs[0].line, via_pmu_interrupt, 0, "VIA-PMU", (void *)0)) { @@ -220,9 +236,12 @@ adb_send_request = pmu_adb_send_request; adb_autopoll = pmu_adb_autopoll; adb_reset_bus = pmu_reset_bus; + + /* Enable backlight */ + pmu_enable_backlight(1); } -static int +static int __openfirmware init_pmu() { int timeout; @@ -232,7 +251,7 @@ out_8(&via[DIRB], (via[DIRB] | TREQ) & ~TACK); /* TACK in, TREQ out */ pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, 0xff); - timeout = 100000; + timeout = 100000; while (!req.complete) { if (--timeout < 0) { printk(KERN_ERR "init_pmu: no response from PMU\n"); @@ -259,8 +278,14 @@ return 1; } +int +pmu_get_model(void) +{ + return pmu_kind; +} + /* Send an ADB command */ -static int +static int __openfirmware pmu_adb_send_request(struct adb_request *req, int sync) { int i; @@ -285,7 +310,7 @@ } /* Enable/disable autopolling */ -static int +static int __openfirmware pmu_adb_autopoll(int devs) { struct adb_request req; @@ -305,7 +330,7 @@ } /* Reset the ADB bus */ -static int +static int __openfirmware pmu_reset_bus(void) { struct adb_request req; @@ -348,7 +373,7 @@ } /* Construct and send a pmu request */ -int +int __openfirmware pmu_request(struct adb_request *req, void (*done)(struct adb_request *), int nbytes, ...) { @@ -380,7 +405,7 @@ * first byte is CUDA_PACKET or PMU_PACKET. For CUDA_PACKET, we * emulate a few CUDA requests. */ -int +int __openfirmware pmu_send_request(struct adb_request *req) { int i; @@ -426,7 +451,7 @@ return -EINVAL; } -int +int __openfirmware pmu_queue_request(struct adb_request *req) { unsigned long flags; @@ -465,7 +490,7 @@ return 0; } -static void +static void __openfirmware send_byte(int x) { out_8(&via[ACR], 0x1c); @@ -473,7 +498,7 @@ out_8(&via[B], via[B] & ~0x10); /* assert TREQ */ } -static void +static void __openfirmware recv_byte() { out_8(&via[ACR], 0x0c); @@ -481,7 +506,7 @@ out_8(&via[B], via[B] & ~0x10); } -static void +static void __openfirmware pmu_start() { unsigned long flags; @@ -506,7 +531,7 @@ restore_flags(flags); } -void +void __openfirmware pmu_poll() { int ie; @@ -517,7 +542,7 @@ _enable_interrupts(ie); } -static void +static void __openfirmware via_pmu_interrupt(int irq, void *arg, struct pt_regs *regs) { int intr; @@ -553,7 +578,7 @@ } } -static void +static void __openfirmware pmu_sr_intr(struct pt_regs *regs) { struct adb_request *req; @@ -649,7 +674,7 @@ } } -static void +static void __openfirmware pmu_done(struct adb_request *req) { req->complete = 1; @@ -658,7 +683,7 @@ } /* Interrupt data could be the result data from an ADB cmd */ -static void +static void __openfirmware pmu_handle_data(unsigned char *data, int len, struct pt_regs *regs) { static int show_pmu_ints = 1; @@ -689,7 +714,7 @@ } else { if (data[0] == 0x08 && len == 3) { /* sound/brightness buttons pressed */ - set_brightness(data[1]); + pmu_set_brightness(data[1] >> 3); set_volume(data[2]); } else if (show_pmu_ints && !(data[0] == PMU_INT_TICK && len == 1)) { @@ -702,54 +727,101 @@ } } -int backlight_bright = -1; +int backlight_level = -1; int backlight_enabled = 0; -#define LEVEL_TO_BRIGHT(lev) ((lev) < 8? 0x7f: 0x4a - ((lev) >> 2)) +#define LEVEL_TO_BRIGHT(lev) ((lev) < 1? 0x7f: 0x4a - ((lev) << 1)) -void +void __openfirmware pmu_enable_backlight(int on) { struct adb_request req; + if (adb_hardware != ADB_VIAPMU) + return ; + if (on) { - if (backlight_bright < 0) { + /* first call: get current backlight value */ + if (backlight_level < 0) { + switch(pmu_kind) { + case PMU_OHARE_BASED: pmu_request(&req, NULL, 2, 0xd9, 0); while (!req.complete) pmu_poll(); - backlight_bright = LEVEL_TO_BRIGHT(req.reply[1]); + backlight_level = req.reply[1] >> 3; + printk(KERN_DEBUG "pmu: controls returned bright: %d\n", (int)req.reply[1]); + break; + case PMU_HEATHROW_BASED: + pmu_request(&req, NULL, 3, PMU_READ_NVRAM, 0x14, 0xe); + while (!req.complete) + pmu_poll(); + printk(KERN_DEBUG "pmu: nvram returned bright: %d\n", (int)req.reply[1]); + backlight_level = req.reply[1]; + break; + default: + backlight_enabled = 0; + return; } - pmu_request(&req, NULL, 2, PMU_BACKLIGHT_BRIGHT, - backlight_bright); - while (!req.complete) - pmu_poll(); + } + pmu_request(&req, NULL, 2, PMU_BACKLIGHT_BRIGHT, + LEVEL_TO_BRIGHT(backlight_level)); + while (!req.complete) + pmu_poll(); } - pmu_request(&req, NULL, 2, PMU_BACKLIGHT_CTRL, on? 0x81: 1); + pmu_request(&req, NULL, 2, PMU_POWER_CTRL, + PMU_POW_BACKLIGHT | (on ? PMU_POW_ON : PMU_POW_OFF)); while (!req.complete) pmu_poll(); backlight_enabled = on; } -static void -set_brightness(int level) +void __openfirmware +pmu_set_brightness(int level) { - backlight_bright = LEVEL_TO_BRIGHT(level); + int bright; + + if (adb_hardware != ADB_VIAPMU) + return ; + + backlight_level = level; + bright = LEVEL_TO_BRIGHT(level); if (!backlight_enabled) return; if (bright_req_1.complete) pmu_request(&bright_req_1, NULL, 2, PMU_BACKLIGHT_BRIGHT, - backlight_bright); + bright); if (bright_req_2.complete) - pmu_request(&bright_req_2, NULL, 2, PMU_BACKLIGHT_CTRL, - backlight_bright < 0x7f? 0x81: 1); + pmu_request(&bright_req_2, NULL, 2, PMU_POWER_CTRL, + PMU_POW_BACKLIGHT | (bright < 0x7f ? PMU_POW_ON : PMU_POW_OFF)); + + /* XXX nvram address is hard-coded and looks ok on wallstreet, please + test on your machine. Note that newer MacOS system software may break + the nvram layout. */ + if ((pmu_kind == PMU_HEATHROW_BASED) && bright_req_3.complete) + pmu_request(&bright_req_3, NULL, 4, PMU_WRITE_NVRAM, + 0x14, 0xe, level); } -static void +void __openfirmware +pmu_enable_irled(int on) +{ + struct adb_request req; + + if (adb_hardware != ADB_VIAPMU) + return ; + + pmu_request(&req, NULL, 2, PMU_POWER_CTRL, PMU_POW_IRLED | + (on ? PMU_POW_ON : PMU_POW_OFF)); + while (!req.complete) + pmu_poll(); +} + +static void __openfirmware set_volume(int level) { } -void +void __openfirmware pmu_restart(void) { struct adb_request req; @@ -768,7 +840,7 @@ ; } -void +void __openfirmware pmu_shutdown(void) { struct adb_request req; @@ -802,7 +874,7 @@ } *pbook_pci_saves; static int n_pbook_pci_saves; -static inline void +static inline void __openfirmware pbook_pci_save(void) { int npci; @@ -829,7 +901,7 @@ } } -static inline void +static inline void __openfirmware pbook_pci_restore(void) { u16 cmd; @@ -868,7 +940,7 @@ #define IRQ_ENABLE ((unsigned int *)0xf3000024) #define MEM_CTRL ((unsigned int *)0xf8000070) -int powerbook_sleep(void) +int __openfirmware powerbook_sleep(void) { int ret, i, x; static int save_backlight; @@ -967,29 +1039,44 @@ /* * Support for /dev/pmu device */ -static int pmu_open(struct inode *inode, struct file *file) +static int __openfirmware pmu_open(struct inode *inode, struct file *file) { return 0; } -static ssize_t pmu_read(struct file *file, char *buf, +static ssize_t __openfirmware pmu_read(struct file *file, char *buf, size_t count, loff_t *ppos) { return 0; } -static ssize_t pmu_write(struct file *file, const char *buf, +static ssize_t __openfirmware pmu_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { return 0; } -static int pmu_ioctl(struct inode * inode, struct file *filp, +/* Note: removed __openfirmware here since it causes link errors */ +static int /*__openfirmware*/ pmu_ioctl(struct inode * inode, struct file *filp, u_int cmd, u_long arg) { + int error; + __u32 value; + switch (cmd) { - case PMU_IOC_SLEEP: + case PMU_IOC_SLEEP: + if (pmu_kind != PMU_OHARE_BASED) + return -ENOSYS; return powerbook_sleep(); + case PMU_IOC_GET_BACKLIGHT: + return put_user(backlight_level, (__u32 *)arg); + case PMU_IOC_SET_BACKLIGHT: + error = get_user(value, (__u32 *)arg); + if (!error) + pmu_set_brightness(value); + return error; + case PMU_IOC_GET_MODEL: + return put_user(pmu_kind, (__u32 *)arg); } return -EINVAL; } diff -u --recursive --new-file v2.2.3/linux/drivers/misc/parport_ax.c linux/drivers/misc/parport_ax.c --- v2.2.3/linux/drivers/misc/parport_ax.c Fri Jan 8 22:36:06 1999 +++ linux/drivers/misc/parport_ax.c Mon Mar 15 16:11:30 1999 @@ -1,4 +1,4 @@ -/* $Id: parport_ax.c,v 1.14 1998/11/16 04:48:02 davem Exp $ +/* $Id: parport_ax.c,v 1.17 1999/01/20 06:18:54 davem Exp $ * Parallel-port routines for Sun Ultra/AX architecture * * Author: Eddie C. Dost @@ -220,13 +220,13 @@ /* FIXME check that resources are free */ int err; - if (p->irq != PARPORT_IRQ_NONE) + if (p->irq != PARPORT_IRQ_NONE) { if ((err = request_irq(p->irq, parport_ax_interrupt, 0, p->name, p)) != 0) return err; else parport_ax_enable_irq(p); - + } request_region(p->base, p->size, p->name); if (p->modes & PARPORT_MODE_PCECR) request_region(p->base+0x400, 3, p->name); diff -u --recursive --new-file v2.2.3/linux/drivers/misc/parport_ieee1284.c linux/drivers/misc/parport_ieee1284.c --- v2.2.3/linux/drivers/misc/parport_ieee1284.c Tue Feb 23 15:21:33 1999 +++ linux/drivers/misc/parport_ieee1284.c Thu Mar 11 09:27:06 1999 @@ -69,5 +69,6 @@ & ~1) & ~2); udelay(1); /* Data available? */ - return (parport_wait_peripheral(port, 0x20, 0))?1:2; + parport_wait_peripheral (port, PARPORT_STATUS_ACK, PARPORT_STATUS_ACK); + return (parport_read_status(port) & PARPORT_STATUS_ERROR)?1:2; } diff -u --recursive --new-file v2.2.3/linux/drivers/misc/parport_pc.c linux/drivers/misc/parport_pc.c --- v2.2.3/linux/drivers/misc/parport_pc.c Mon Jan 4 15:08:17 1999 +++ linux/drivers/misc/parport_pc.c Thu Mar 11 09:27:06 1999 @@ -378,34 +378,35 @@ */ static int parport_ECR_present(struct parport *pb) { - unsigned char r, octr = parport_pc_read_control(pb); - unsigned char oecr = parport_pc_read_econtrol(pb); - unsigned char tmp; + unsigned char r; + parport_pc_write_control (pb, 0xc); r = parport_pc_read_control(pb); if ((parport_pc_read_econtrol(pb) & 0x3) == (r & 0x3)) { parport_pc_write_control(pb, r ^ 0x2 ); /* Toggle bit 1 */ r = parport_pc_read_control(pb); - if ((parport_pc_read_econtrol(pb) & 0x2) == (r & 0x2)) { - parport_pc_write_control(pb, octr); - return 0; /* Sure that no ECR register exists */ - } + if ((parport_pc_read_econtrol(pb) & 0x2) == (r & 0x2)) + goto no_reg; /* Sure that no ECR register exists */ } if ((parport_pc_read_econtrol(pb) & 0x3 ) != 0x1) - return 0; + goto no_reg; parport_pc_write_econtrol(pb, 0x34); - tmp = parport_pc_read_econtrol(pb); + if (parport_pc_read_econtrol(pb) != 0x35) + goto no_reg; - parport_pc_write_econtrol(pb, oecr); - parport_pc_write_control(pb, octr); - - if (tmp != 0x35) - return 0; + parport_pc_write_control(pb, 0xc); + /* Go to mode 000; SPP, reset FIFO */ + parport_pc_frob_econtrol (pb, 0xe0, 0x00); + return PARPORT_MODE_PCECR; + + no_reg: + parport_pc_write_control (pb, 0xc); + return 0; } static int parport_ECP_supported(struct parport *pb) @@ -706,14 +707,11 @@ static int probe_one_port(unsigned long int base, int irq, int dma) { - struct parport tmpport, *p; + struct parport *p; int probedirq = PARPORT_IRQ_NONE; if (check_region(base, 3)) return 0; - tmpport.base = base; - tmpport.ops = &parport_pc_ops; - if (!(parport_SPP_supported(&tmpport))) return 0; - if (!(p = parport_register_port(base, irq, dma, &parport_pc_ops))) return 0; - p->modes = PARPORT_MODE_PCSPP | parport_PS2_supported(p); + if (!(p = parport_register_port(base, irq, dma, &parport_pc_ops))) + return 0; if (p->base != 0x3bc) { if (!check_region(base+0x400,3)) { p->modes |= parport_ECR_present(p); @@ -725,6 +723,12 @@ p->modes |= parport_ECPEPP_supported(p); } } + if (!parport_SPP_supported(p)) { + /* No port. */ + parport_unregister_port (p); + return 0; + } + p->modes |= PARPORT_MODE_PCSPP | parport_PS2_supported(p); p->size = (p->modes & (PARPORT_MODE_PCEPP | PARPORT_MODE_PCECPEPP))?8:3; printk(KERN_INFO "%s: PC-style at 0x%lx", p->name, p->base); diff -u --recursive --new-file v2.2.3/linux/drivers/net/3c509.c linux/drivers/net/3c509.c --- v2.2.3/linux/drivers/net/3c509.c Tue Dec 22 14:16:55 1998 +++ linux/drivers/net/3c509.c Wed Mar 10 16:51:35 1999 @@ -1,8 +1,8 @@ /* 3c509.c: A 3c509 EtherLink3 ethernet driver for linux. */ /* - Written 1993-1997 by Donald Becker. + Written 1993-1998 by Donald Becker. - Copyright 1994-1997 by Donald Becker. + Copyright 1994-1998 by Donald Becker. Copyright 1993 United States Government as represented by the Director, National Security Agency. This software may be used and distributed according to the terms of the GNU Public License, @@ -35,19 +35,24 @@ other cleanups. -djb Andrea Arcangeli: Upgraded to Donald Becker's version 1.12. Rick Payne: Fixed SMP race condition + v1.13 9/8/97 Made 'max_interrupt_work' an insmod-settable variable -djb + v1.14 10/15/97 Avoided waiting..discard message for fast machines -djb + v1.15 1/31/98 Faster recovery for Tx errors. -djb + v1.16 2/3/98 Different ID port handling to avoid sound cards. -djb */ -static char *version = "3c509.c:1.12 6/4/97 becker@cesdis.gsfc.nasa.gov\n"; +static char *version = "3c509.c:1.16 (2.2) 2/3/98 becker@cesdis.gsfc.nasa.gov.\n"; /* A few values that may be tweaked. */ /* Time in jiffies before concluding the transmitter is hung. */ #define TX_TIMEOUT (400*HZ/1000) /* Maximum events (Rx packets, etc.) to handle at each interrupt. */ -#define INTR_WORK 10 +static int max_interrupt_work = 10; +#include #include -#include /* for CONFIG_MCA */ +#include #include #include #include @@ -129,12 +134,13 @@ /* skb send-queue */ int head, size; struct sk_buff *queue[SKB_QUEUE_SIZE]; + char mca_slot; }; -static int id_port = 0x100; +static int id_port = 0x110; /* Start with 0x110 to avoid new sound cards.*/ static struct device *el3_root_dev = NULL; static ushort id_read_eeprom(int index); -static ushort read_eeprom(short ioaddr, int index); +static ushort read_eeprom(int ioaddr, int index); static int el3_open(struct device *dev); static int el3_start_xmit(struct sk_buff *skb, struct device *dev); static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs); @@ -144,14 +150,29 @@ static int el3_close(struct device *dev); static void set_multicast_list(struct device *dev); - +#ifdef CONFIG_MCA +struct el3_mca_adapters_struct { + char* name; + int id; +}; + +struct el3_mca_adapters_struct el3_mca_adapters[] = { + { "3Com 3c529 EtherLink III (10base2)", 0x627c }, + { "3Com 3c529 EtherLink III (10baseT)", 0x627d }, + { "3Com 3c529 EtherLink III (test mode)", 0x62db }, + { "3Com 3c529 EtherLink III (TP or coax)", 0x62f6 }, + { "3Com 3c529 EtherLink III (TP)", 0x62f7 }, + { NULL, 0 }, +}; +#endif int el3_probe(struct device *dev) { short lrs_state = 0xff, i; - ushort ioaddr, irq, if_port; - short phys_addr[3]; + int ioaddr, irq, if_port; + u16 phys_addr[3]; static int current_tag = 0; + int mca_slot = -1; /* First check all slots of the EISA bus. The next slot address to probe is kept in 'eisa_addr' to support multiple probe() calls. */ @@ -182,69 +203,72 @@ } #ifdef CONFIG_MCA -#define MCA_NUMBER_OF_SLOTS 8 -#define MCA_PORT_POS_SEL 0x096 -#define MCA_PORT_ID_REG_0 0x100 -#define MCA_PORT_ID_REG_1 0x101 -#define MCA_SELECT_BIT 0x08 - if (MCA_bus) - { - u_int mca_id; - u_char posreg[4]; - int mca_slot; - - if (el3_debug > 2) - printk("3c529: probing...\n"); - /* This should probably be done once early on and read into - * a structure somewhere... */ - for (mca_slot = 0; mca_slot < MCA_NUMBER_OF_SLOTS; mca_slot++) - { - /* Select MCA slot i */ - outb_p(mca_slot | MCA_SELECT_BIT, MCA_PORT_POS_SEL); - mca_id = ((inb_p(MCA_PORT_ID_REG_1)<<8) - + inb_p(MCA_PORT_ID_REG_0)); - if (mca_id == 0x627C /* 10base2 */ - || mca_id == 0x627D /* 10baseT */ - || mca_id == 0x62DB /* Test mode */ - || mca_id == 0x62F6 /* TP or coax */ - || mca_id == 0x62F7) /* TP only */ - { - if (el3_debug > 1) - printk("3c529: Found with id 0x%x at slot %d\n", - mca_id, mca_slot); - posreg[0] = inb_p(0x102); posreg[1] = inb_p(0x103); - posreg[2] = inb_p(0x104); posreg[3] = inb_p(0x105); - break; + /* Based on Erik Nygren's (nygren@mit.edu) 3c529 patch, heavily + * modified by Chris Beauregard (cpbeaure@csclub.uwaterloo.ca) + * to support standard MCA probing. + * + * redone for multi-card detection by ZP Gu (zpg@castle.net) + * now works as a module + */ + + if( MCA_bus ) { + int slot, j; + u_char pos4, pos5; + + for( j = 0; el3_mca_adapters[j].name != NULL; j ++ ) { + slot = 0; + while( slot != MCA_NOTFOUND ) { + slot = mca_find_unused_adapter( + el3_mca_adapters[j].id, slot ); + if( slot == MCA_NOTFOUND ) break; + + /* if we get this far, an adapter has been + * detected and is enabled + */ + + printk("3c509: found %s at slot %d\n", + el3_mca_adapters[j].name, slot + 1 ); + + pos4 = mca_read_stored_pos( slot, 4 ); + pos5 = mca_read_stored_pos( slot, 5 ); + + ioaddr = ((short)((pos4&0xfc)|0x02)) << 8; + irq = pos5 & 0x0f; + + /* probing for a card at a particular IO/IRQ */ + if(dev && ((dev->irq >= 1 && dev->irq != irq) || + (dev->base_addr >= 1 && dev->base_addr != ioaddr))) { + slot++; /* probing next slot */ + continue; + } + + /* claim the slot */ + mca_set_adapter_name(slot, el3_mca_adapters[j].name); + mca_set_adapter_procfn(slot, NULL, NULL); + mca_mark_as_used(slot); + + if_port = pos4 & 0x03; + if (el3_debug > 2) { + printk("3c529: irq %d ioaddr 0x%x ifport %d\n", irq, ioaddr, if_port); + } + for (i = 0; i < 3; i++) { + phys_addr[i] = htons(read_eeprom(ioaddr, i)); + } + + mca_slot = slot; + + goto found; } - mca_id = 0xFFFF; - } - /* Read values from POS registers so now disable */ - outb(0,MCA_PORT_POS_SEL); - if (mca_id != 0xFFFF && !(posreg[0]&0x01)) - printk("3c529: Adapter found but disabled in slot %d\n", mca_slot); - else if (mca_id != 0xFFFF && posreg[0]&0x01) - { - /* Found and adapter is enabled */ - if (el3_debug > 2) - printk("3c529: pos registers 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", - posreg[0], posreg[1], posreg[2], posreg[3]); - ioaddr = ((short)((posreg[2]&0xfc)|0x02)) << 8; - irq = posreg[3] & 0x0f; - if_port = posreg[2] & 0x03; - if (el3_debug > 2) - printk("3c529: irq %d ioaddr 0x%x ifport %d\n", - irq, ioaddr, if_port); - for (i = 0; i < 3; i++) - phys_addr[i] = htons(read_eeprom(ioaddr, i)); - goto found; } + /* if we get here, we didn't find an MCA adapter */ + return -ENODEV; } #endif /* Reset the ISA PnP mechanism on 3c509b. */ outb(0x02, 0x279); /* Select PnP config control register. */ outb(0x02, 0xA79); /* Return to WaitForKey state. */ /* Select an open I/O location at 0x1*0 to do contention select. */ - for (id_port = 0x100; id_port < 0x200; id_port += 0x10) { + for ( ; id_port < 0x200; id_port += 0x10) { if (check_region(id_port, 1)) continue; outb(0x00, id_port); @@ -288,18 +312,23 @@ } { - unsigned short iobase = id_read_eeprom(8); + unsigned int iobase = id_read_eeprom(8); if_port = iobase >> 14; ioaddr = 0x200 + ((iobase & 0x1f) << 4); } - if (dev && dev->irq > 1 && dev->irq < 16) - irq = dev->irq; - else - irq = id_read_eeprom(9) >> 12; + irq = id_read_eeprom(9) >> 12; - if (dev && dev->base_addr != 0 - && dev->base_addr != (unsigned short)ioaddr) { - return -ENODEV; + if (dev) { /* Set passed-in IRQ or I/O Addr. */ + if (dev->irq > 1 && dev->irq < 16) + irq = dev->irq; + + if (dev->base_addr) { + if (dev->mem_end == 0x3c509 /* Magic key */ + && dev->base_addr >= 0x200 && dev->base_addr <= 0x3e0) + ioaddr = dev->base_addr & 0x3f0; + else if (dev->base_addr != ioaddr) + return -ENODEV; + } } /* Set the adaptor tag so that the next card can be found. */ @@ -342,7 +371,8 @@ if (dev->priv == NULL) return -ENOMEM; memset(dev->priv, 0, sizeof(struct el3_private)); - + + ((struct el3_private *)dev->priv)->mca_slot = mca_slot; ((struct el3_private *)dev->priv)->next_dev = el3_root_dev; el3_root_dev = dev; @@ -364,7 +394,7 @@ /* Read a word from the EEPROM using the regular EEPROM access register. Assume that we are in register window zero. */ -static ushort read_eeprom(short ioaddr, int index) +static ushort read_eeprom(int ioaddr, int index) { outw(EEPROM_READ + index, ioaddr + 10); /* Pause for at least 162 us. for the read to take place. */ @@ -394,7 +424,6 @@ } - static int el3_open(struct device *dev) { @@ -464,7 +493,7 @@ /* Ack all pending events, and set active indicator mask. */ outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, ioaddr + EL3_CMD); - outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull, + outw(SetIntrEnb | IntLatch|TxAvailable|TxComplete|RxComplete|StatsFull, ioaddr + EL3_CMD); if (el3_debug > 3) @@ -589,7 +618,7 @@ struct device *dev = (struct device *)dev_id; struct el3_private *lp; int ioaddr, status; - int i = INTR_WORK; + int i = max_interrupt_work; if (dev == NULL) { printk ("el3_interrupt(): irq %d for unknown device.\n", irq); @@ -624,7 +653,7 @@ dev->tbusy = 0; mark_bh(NET_BH); } - if (status & (AdapterFailure | RxEarly | StatsFull)) { + if (status & (AdapterFailure | RxEarly | StatsFull | TxComplete)) { /* Handle all uncommon interrupts. */ if (status & StatsFull) /* Empty statistics. */ update_stats(dev); @@ -632,6 +661,18 @@ el3_rx(dev); outw(AckIntr | RxEarly, ioaddr + EL3_CMD); } + if (status & TxComplete) { /* Really Tx error. */ + struct el3_private *lp = (struct el3_private *)dev->priv; + short tx_status; + int i = 4; + + while (--i>0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) { + if (tx_status & 0x38) lp->stats.tx_aborted_errors++; + if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD); + if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD); + outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */ + } + } if (status & AdapterFailure) { /* Adapter failure requires Rx reset and reinit. */ outw(RxReset, ioaddr + EL3_CMD); @@ -730,6 +771,8 @@ while ((rx_status = inw(ioaddr + RX_STATUS)) > 0) { if (rx_status & 0x4000) { /* Error, update stats. */ short error = rx_status & 0x3800; + + outw(RxDiscard, ioaddr + EL3_CMD); lp->stats.rx_errors++; switch (error) { case 0x0000: lp->stats.rx_over_errors++; break; @@ -761,19 +804,21 @@ (pkt_len + 3) >> 2); #endif + outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */ skb->protocol = eth_type_trans(skb,dev); netif_rx(skb); - outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */ lp->stats.rx_packets++; continue; - } else if (el3_debug) + } + outw(RxDiscard, ioaddr + EL3_CMD); + lp->stats.rx_dropped++; + if (el3_debug) printk("%s: Couldn't allocate a sk_buff of size %d.\n", dev->name, pkt_len); } - lp->stats.rx_dropped++; - outw(RxDiscard, ioaddr + EL3_CMD); + inw(ioaddr + EL3_STATUS); /* Delay. */ while (inw(ioaddr + EL3_STATUS) & 0x1000) - printk(" Waiting for 3c509 to discard packet, status %x.\n", + printk(KERN_DEBUG " Waiting for 3c509 to discard packet, status %x.\n", inw(ioaddr + EL3_STATUS) ); } @@ -786,7 +831,7 @@ static void set_multicast_list(struct device *dev) { - short ioaddr = dev->base_addr; + int ioaddr = dev->base_addr; if (el3_debug > 1) { static int old = 0; if (old != dev->mc_count) { @@ -844,7 +889,7 @@ } #ifdef MODULE -/* Parameter that may be passed into the module. */ +/* Parameters that may be passed into the module. */ static int debug = -1; static int irq[] = {-1, -1, -1, -1, -1, -1, -1, -1}; static int xcvr[] = {-1, -1, -1, -1, -1, -1, -1, -1}; @@ -880,7 +925,12 @@ /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ while (el3_root_dev) { - next_dev = ((struct el3_private *)el3_root_dev->priv)->next_dev; + struct el3_private *lp = (struct el3_private *)el3_root_dev->priv; +#ifdef CONFIG_MCA + if(lp->mca_slot!=-1) + mca_mark_as_unused(lp->mca_slot); +#endif + next_dev = lp->next_dev; unregister_netdev(el3_root_dev); release_region(el3_root_dev->base_addr, EL3_IO_EXTENT); kfree(el3_root_dev); @@ -888,7 +938,7 @@ } } #endif /* MODULE */ - + /* * Local variables: * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c509.c" diff -u --recursive --new-file v2.2.3/linux/drivers/net/3c527.c linux/drivers/net/3c527.c --- v2.2.3/linux/drivers/net/3c527.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/3c527.c Sun Mar 21 07:11:36 1999 @@ -0,0 +1,1152 @@ +/* 3c527.c: 3Com Etherlink/MC32 driver for Linux + * + * (c) Copyright 1998 Red Hat Software Inc + * Written by Alan Cox. + * + * Based on skeleton.c written 1993-94 by Donald Becker and ne2.c + * (for the MCA stuff) written by Wim Dumon. + * + * Thanks to 3Com for making this possible by providing me with the + * documentation. + * + * This software may be used and distributed according to the terms + * of the GNU Public License, incorporated herein by reference. + * + */ + +static const char *version = + "3c527.c:v0.04 1999/03/16 Alan Cox (alan@redhat.com)\n"; + +/* + * Things you need + * o The databook. + * + * Traps for the unwary + * + * The diagram (Figure 1-1) and the POS summary disagree with the + * "Interrupt Level" section in the manual. + * + * The documentation in places seems to miss things. In actual fact + * I've always eventually found everything is documented, it just + * requires careful study. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "3c527.h" + +/* + * The name of the card. Is used for messages and in the requests for + * io regions, irqs and dma channels + */ +static const char* cardname = "3c527"; + +/* use 0 for production, 1 for verification, >2 for debug */ +#ifndef NET_DEBUG +#define NET_DEBUG 2 +#endif +static unsigned int mc32_debug = NET_DEBUG; + +/* The number of low I/O ports used by the ethercard. */ +#define NETCARD_IO_EXTENT 8 + + +struct mc32_mailbox +{ + u16 mbox __attribute((packed)); + u16 data[1] __attribute((packed)); +}; + +/* Information that need to be kept for each board. */ + +#define TX_RING_MAX 16 /* Typically the card supports 37 */ +#define RX_RING_MAX 32 /* " " " */ + +struct mc32_local +{ + struct net_device_stats net_stats; + int slot; + volatile struct mc32_mailbox *rx_box; + volatile struct mc32_mailbox *tx_box; + volatile struct mc32_mailbox *exec_box; + volatile u16 *stats; + u16 tx_chain; + u16 rx_chain; + u16 tx_len; + u16 rx_len; + u32 base; + u16 rx_halted; + u16 tx_halted; + u16 exec_pending; + u16 mc_reload_wait; /* a multicast load request is pending */ + atomic_t tx_count; /* buffers left */ + struct wait_queue *event; + struct sk_buff *tx_skb[TX_RING_MAX]; /* Transmit ring */ + u16 tx_skb_top; + u16 tx_skb_end; + struct sk_buff *rx_skb[RX_RING_MAX]; /* Receive ring */ + void *rx_ptr[RX_RING_MAX]; /* Data pointers */ +}; + +/* The station (ethernet) address prefix, used for a sanity check. */ +#define SA_ADDR0 0x02 +#define SA_ADDR1 0x60 +#define SA_ADDR2 0xAC + +struct mca_adapters_t { + unsigned int id; + char *name; +}; + +const struct mca_adapters_t mc32_adapters[] = { + { 0x0041, "3COM EtherLink MC/32" }, + { 0x8EF5, "IBM High Performance Lan Adapter" }, + { 0x0000, NULL } +}; + + +/* Index to functions, as function prototypes. */ + +extern int mc32_probe(struct device *dev); + +static int mc32_probe1(struct device *dev, int ioaddr); +static int mc32_open(struct device *dev); +static int mc32_send_packet(struct sk_buff *skb, struct device *dev); +static void mc32_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static int mc32_close(struct device *dev); +static struct net_device_stats *mc32_get_stats(struct device *dev); +static void mc32_set_multicast_list(struct device *dev); + +/* + * 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). + */ + +__initfunc(int mc32_probe(struct device *dev)) +{ + static int current_mca_slot = -1; + int i; + int adapter_found = 0; + + /* Do not check any supplied i/o locations. + POS registers usually don't fail :) */ + + /* MCA cards have POS registers. + Autodetecting MCA cards is extremely simple. + Just search for the card. */ + + for(i = 0; (mc32_adapters[i].name != NULL) && !adapter_found; i++) { + current_mca_slot = + mca_find_unused_adapter(mc32_adapters[i].id, 0); + + if((current_mca_slot != MCA_NOTFOUND) && !adapter_found) { + if(!mc32_probe1(dev, current_mca_slot)) + { + mca_set_adapter_name(current_mca_slot, + mc32_adapters[i].name); + mca_mark_as_used(current_mca_slot); + return 0; + } + + } + } + return -ENODEV; +} + +/* + * 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. + */ +__initfunc(static int mc32_probe1(struct device *dev, int slot)) +{ + static unsigned version_printed = 0; + int i; + u8 POS; + u32 base; + struct mc32_local *lp; + static u16 mca_io_bases[]={ + 0x7280,0x7290, + 0x7680,0x7690, + 0x7A80,0x7A90, + 0x7E80,0x7E90 + }; + static u32 mca_mem_bases[]={ + 0x00C0000, + 0x00C4000, + 0x00C8000, + 0x00CC000, + 0x00D0000, + 0x00D4000, + 0x00D8000, + 0x00DC000 + }; + static char *failures[]={ + "Processor instruction", + "Processor data bus", + "Processor data bus", + "Processor data bus", + "Adapter bus", + "ROM checksum", + "Base RAM", + "Extended RAM", + "82586 internal loopback", + "82586 initialisation failure", + "Adapter list configuration error" + }; + + /* Time to play MCA games */ + + if (mc32_debug && version_printed++ == 0) + printk(KERN_DEBUG "%s", version); + + printk(KERN_INFO "%s: %s found in slot %d:", dev->name, cardname, slot); + + POS = mca_read_stored_pos(slot, 2); + + if(!(POS&1)) + { + printk(" disabled.\n"); + return -ENODEV; + } + + /* Allocate a new 'dev' if needed. */ + if (dev == NULL) { + /* + * Don't allocate the private data here, it is done later + * This makes it easier to free the memory when this driver + * is used as a module. + */ + dev = init_etherdev(0, 0); + if (dev == NULL) + return -ENOMEM; + } + + /* Fill in the 'dev' fields. */ + dev->base_addr = mca_io_bases[(POS>>1)&7]; + dev->mem_start = mca_mem_bases[(POS>>4)&7]; + + POS = mca_read_stored_pos(slot, 4); + if(!(POS&1)) + { + printk("memory window disabled.\n"); + return -ENODEV; + } + + POS = mca_read_stored_pos(slot, 5); + + i=(POS>>4)&3; + if(i==3) + { + printk("invalid memory window.\n"); + return -ENODEV; + } + + i*=16384; + i+=16384; + + dev->mem_end=dev->mem_start + i; + + dev->irq = ((POS>>2)&3)+9; + + printk("io 0x%3lX irq %d mem 0x%lX (%dK)\n", + dev->base_addr, dev->irq, dev->mem_start, i/1024); + + + /* We ought to set the cache line size here.. */ + + + /* + * Go PROM browsing + */ + + printk("%s: Address ", dev->name); + + /* Retrieve and print the ethernet address. */ + for (i = 0; i < 6; i++) + { + mca_write_pos(slot, 6, i+12); + mca_write_pos(slot, 7, 0); + + printk(" %2.2x", dev->dev_addr[i] = mca_read_pos(slot,3)); + } + + mca_write_pos(slot, 6, 0); + mca_write_pos(slot, 7, 0); + + POS = mca_read_stored_pos(slot, 4); + + if(POS&2) + printk(" : BNC port selected.\n"); + else + printk(" : AUI port selected.\n"); + + POS=inb(dev->base_addr+HOST_CTRL); + POS|=HOST_CTRL_ATTN|HOST_CTRL_RESET; + POS&=~HOST_CTRL_INTE; + outb(POS, dev->base_addr+HOST_CTRL); + /* Reset adapter */ + udelay(100); + /* Reset off */ + POS&=~(HOST_CTRL_ATTN|HOST_CTRL_RESET); + outb(POS, dev->base_addr+HOST_CTRL); + + udelay(300); + + /* + * Grab the IRQ + */ + + if(request_irq(dev->irq, &mc32_interrupt, 0, cardname, dev)) + { + printk("%s: unable to get IRQ %d.\n", + dev->name, dev->irq); + return -EAGAIN; + } + + /* Initialize the device structure. */ + if (dev->priv == NULL) { + dev->priv = kmalloc(sizeof(struct mc32_local), GFP_KERNEL); + if (dev->priv == NULL) + { + free_irq(dev->irq, dev); + return -ENOMEM; + } + } + + memset(dev->priv, 0, sizeof(struct mc32_local)); + lp = (struct mc32_local *)dev->priv; + lp->slot = slot; + + i=0; + + base = inb(dev->base_addr); + + while(base==0xFF) + { + i++; + if(i==1000) + { + printk("%s: failed to boot adapter.\n", dev->name); + free_irq(dev->irq, dev); + return -ENODEV; + } + udelay(1000); + if(inb(dev->base_addr+2)&(1<<5)) + base = inb(dev->base_addr); + } + + if(base>0) + { + if(base < 0x0C) + printk("%s: %s%s.\n", dev->name, failures[base-1], + base<0x0A?" test failure":""); + else + printk("%s: unknown failure %d.\n", dev->name, base); + free_irq(dev->irq, dev); + return -ENODEV; + } + + base=0; + for(i=0;i<4;i++) + { + int n=0; + + while(!(inb(dev->base_addr+2)&(1<<5))) + { + n++; + udelay(50); + if(n>100) + { + printk(KERN_ERR "%s: mailbox read fail (%d).\n", dev->name, i); + free_irq(dev->irq, dev); + return -ENODEV; + } + } + + base|=(inb(dev->base_addr)<<(8*i)); + } + + lp->exec_box=bus_to_virt(dev->mem_start+base); + + base=lp->exec_box->data[1]<<16|lp->exec_box->data[0]; + + lp->base = dev->mem_start+base; + + lp->rx_box=bus_to_virt(lp->base + lp->exec_box->data[2]); + lp->tx_box=bus_to_virt(lp->base + lp->exec_box->data[3]); + + lp->stats = bus_to_virt(lp->base + lp->exec_box->data[5]); + + /* + * Descriptor chains (card relative) + */ + + lp->tx_chain = lp->exec_box->data[8]; + lp->rx_chain = lp->exec_box->data[10]; + lp->tx_len = lp->exec_box->data[9]; + lp->rx_len = lp->exec_box->data[11]; + + printk("%s: %d RX buffers, %d TX buffers. Base of 0x%08X.\n", + dev->name, lp->rx_len, lp->tx_len, lp->base); + + dev->open = mc32_open; + dev->stop = mc32_close; + dev->hard_start_xmit = mc32_send_packet; + dev->get_stats = mc32_get_stats; + dev->set_multicast_list = mc32_set_multicast_list; + + lp->rx_halted = 1; + lp->tx_halted = 1; + + /* Fill in the fields of the device structure with ethernet values. */ + ether_setup(dev); + return 0; +} + + +/* + * Polled command stuff + */ + +static void mc32_ring_poll(struct device *dev) +{ + int ioaddr = dev->base_addr; + while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR)); +} + + +/* + * Send exec commands + */ + +static int mc32_command(struct device *dev, u16 cmd, void *data, int len) +{ + struct mc32_local *lp = (struct mc32_local *)dev->priv; + int ioaddr = dev->base_addr; + unsigned long flags; + + while(lp->exec_pending) + sleep_on(&lp->event); + + lp->exec_pending=1; + lp->exec_box->mbox=0; + lp->exec_box->mbox=cmd; + memcpy((void *)lp->exec_box->data, data, len); + barrier(); /* the memcpy forgot the volatile so be sure */ + + /* Send the command */ + while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR)); + outb(1<<6, ioaddr+HOST_CMD); + + save_flags(flags); + cli(); + while(lp->exec_pending!=2) + sleep_on(&lp->event); + lp->exec_pending=0; + restore_flags(flags); + + /* + * A multicast set got blocked - do it now + */ + + if(lp->mc_reload_wait) + mc32_set_multicast_list(dev); + + if(lp->exec_box->data[0]&(1<<13)) + return -1; + return 0; +} + +/* + * RX abort + */ + +static void mc32_rx_abort(struct device *dev) +{ + struct mc32_local *lp = (struct mc32_local *)dev->priv; + int ioaddr = dev->base_addr; + + while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR)); + + lp->rx_box->mbox=0; + outb(3<<3, ioaddr+HOST_CMD); /* Suspend reception */ +} + + +/* + * RX enable + */ + +static void mc32_rx_begin(struct device *dev) +{ + struct mc32_local *lp = (struct mc32_local *)dev->priv; + int ioaddr = dev->base_addr; + + while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR)); + + lp->rx_box->mbox=0; + outb(1<<3, ioaddr+HOST_CMD); /* GO */ + mc32_ring_poll(dev); + + lp->rx_halted=0; +} + +static void mc32_tx_abort(struct device *dev) +{ + struct mc32_local *lp = (struct mc32_local *)dev->priv; + int ioaddr = dev->base_addr; + + while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR)); + + lp->tx_box->mbox=0; + outb(3, ioaddr+HOST_CMD); /* Suspend */ + + /* Ring empty */ + + atomic_set(&lp->tx_count, lp->tx_len); + + /* Flush */ + if(lp->tx_skb_top!=lp->tx_skb_end) + { + int i; + if(lp->tx_skb_top<=lp->tx_skb_end) + { + for(i=lp->tx_skb_top;itx_skb_end;i++) + { + dev_kfree_skb(lp->tx_skb[i]); + lp->tx_skb[i]=NULL; + } + } + else + { + for(i=lp->tx_skb_end;itx_skb[i]); + lp->tx_skb[i]=NULL; + } + for(i=0;itx_skb_top;i++) + { + dev_kfree_skb(lp->tx_skb[i]); + lp->tx_skb[i]=NULL; + } + } + } + lp->tx_skb_top=lp->tx_skb_end=0; +} + +/* + * TX enable + */ + +static void mc32_tx_begin(struct device *dev) +{ + struct mc32_local *lp = (struct mc32_local *)dev->priv; + int ioaddr = dev->base_addr; + + while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR)); + + lp->tx_box->mbox=0; +#if 0 + outb(5, ioaddr+HOST_CMD); /* GO */ + printk("TX=>5\n"); + mc32_ring_poll(dev); + if(lp->tx_box->mbox&(1<<13)) + printk("TX begin error!\n"); +#endif + lp->tx_halted=0; +} + + +/* + * Load the rx ring + */ + +static int mc32_load_rx_ring(struct device *dev) +{ + struct mc32_local *lp = (struct mc32_local *)dev->priv; + int i; + u16 base; + volatile struct skb_header *p; + + base = lp->rx_box->data[0]; + + /* Fix me - should use card size - also fix flush ! */ + + for(i=0;irx_skb[i]=alloc_skb(1532, GFP_KERNEL); + if(lp->rx_skb[i]==NULL) + { + for(;i>=0;i--) + kfree_skb(lp->rx_skb[i]); + return -ENOBUFS; + } + lp->rx_ptr[i]=lp->rx_skb[i]->data+18; + + p=bus_to_virt(lp->base+base); + p->control=0; + p->data = virt_to_bus(lp->rx_ptr[i]); + p->status=0; + p->length = 1532; + base = p->next; + } + p->control = (1<<6); + lp->rx_box->mbox = 0; + return 0; +} + +static void mc32_flush_rx_ring(struct mc32_local *lp) +{ + int i; + for(i=0;irx_skb[i]); +} + +static void mc32_flush_tx_ring(struct mc32_local *lp) +{ + int i; + + if(lp->tx_skb_top <= lp->tx_skb_end) + { + for(i=lp->tx_skb_top;itx_skb_end;i++) + dev_kfree_skb(lp->tx_skb[i]); + } + else + { + for(i=0;itx_skb_end;i++) + dev_kfree_skb(lp->tx_skb[i]); + for(i=lp->tx_skb_top;itx_skb[i]); + } +} + +/* + * Open/initialize the board. This is called (in the current kernel) + * sometime after booting when the 'ifconfig' program is run. + */ + +static int mc32_open(struct device *dev) +{ + int ioaddr = dev->base_addr; + u16 zero_word=0; + u8 one=1; + u8 regs; + + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + + /* + * Interrupts enabled + */ + + regs=inb(ioaddr+HOST_CTRL); + regs|=HOST_CTRL_INTE; + outb(regs, ioaddr+HOST_CTRL); + + + /* + * Send the indications on command + */ + + mc32_command(dev, 4, &one, 2); + + + /* + * Send the command sequence "abort, resume" for RX and TX. + * The abort cleans up the buffer chains if needed. + */ + + mc32_rx_abort(dev); + mc32_tx_abort(dev); + + /* Set Network Address */ + mc32_command(dev, 1, dev->dev_addr, 6); + + /* Set the filters */ + mc32_set_multicast_list(dev); + + /* Issue the 82586 workaround command - this is for "busy lans", + but basically means for all lans now days - has a performance + cost but best set */ + + mc32_command(dev, 0x0D, &zero_word, 2); /* 82586 bug workaround on */ + + /* Load the ring we just initialised */ + + if(mc32_load_rx_ring(dev)) + { + mc32_close(dev); + return -ENOBUFS; + } + + /* And the resume command goes last */ + + mc32_rx_begin(dev); + mc32_tx_begin(dev); + + MOD_INC_USE_COUNT; + + return 0; +} + +static int mc32_send_packet(struct sk_buff *skb, struct device *dev) +{ + struct mc32_local *lp = (struct mc32_local *)dev->priv; + + 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; + printk(KERN_WARNING "%s: transmit timed out?\n", dev->name); + /* Try to restart the adaptor. */ + dev->tbusy=0; + dev->trans_start = jiffies; + } + + /* + * Block a timer-based transmit from overlapping. This could better be + * done with atomic_swap(1, dev->tbusy), but set_bit() works as well. + */ + if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) + { + printk(KERN_WARNING "%s: Transmitter access conflict.\n", dev->name); + dev_kfree_skb(skb); + } + else + { + unsigned long flags; + + u16 tx_head; + volatile struct skb_header *p, *np; + + save_flags(flags); + cli(); + + if(atomic_read(&lp->tx_count)==0) + { + dev->tbusy=1; + restore_flags(flags); + return 1; + } + + tx_head = lp->tx_box->data[0]; + atomic_dec(&lp->tx_count); + + /* We will need this to flush the buffer out */ + + lp->tx_skb[lp->tx_skb_end] = skb; + lp->tx_skb_end++; + lp->tx_skb_end&=(TX_RING_MAX-1); + + /* P is the last sending/sent buffer as a pointer */ + p=(struct skb_header *)bus_to_virt(lp->base+tx_head); + + /* NP is the buffer we will be loading */ + np=(struct skb_header *)bus_to_virt(lp->base+p->next); + + np->control |= (1<<6); /* EOL */ + wmb(); + + np->length = skb->len; + np->data = virt_to_bus(skb->data); + np->status = 0; + np->control = (1<<7)|(1<<6); /* EOP EOL */ + wmb(); + + p->status = 0; + p->control &= ~(1<<6); + + dev->tbusy = 0; /* Keep feeding me */ + + lp->tx_box->mbox=0; + restore_flags(flags); + } + return 0; +} + +static void mc32_update_stats(struct device *dev) +{ +} + + +static void mc32_rx_ring(struct device *dev) +{ + struct mc32_local *lp=dev->priv; + int ioaddr = dev->base_addr; + int x=0; + volatile struct skb_header *p; + u16 base; + u16 top; + + top = base = lp->rx_box->data[0]; + do + { + p=(struct skb_header *)bus_to_virt(base+lp->base); + if(!(p->status & (1<<7))) + break; + if(p->status & (1<<6)) + { + u16 length = p->length; + struct sk_buff *skb=dev_alloc_skb(length+2); + if(skb!=NULL) + { + skb_reserve(skb,2); + /*printk("Frame at %p\n", bus_to_virt(p->data)); */ + memcpy(skb_put(skb, length), + bus_to_virt(p->data), length); + skb->protocol=eth_type_trans(skb,dev); + skb->dev=dev; + lp->net_stats.rx_packets++; + lp->net_stats.rx_bytes+=skb->len; + netif_rx(skb); + } + else + lp->net_stats.rx_dropped++; + } + else + { + lp->net_stats.rx_errors++; + switch(p->status&0x0F) + { + case 1: + lp->net_stats.rx_crc_errors++;break; + case 2: + lp->net_stats.rx_fifo_errors++;break; + case 3: + lp->net_stats.rx_frame_errors++;break; + case 4: + lp->net_stats.rx_missed_errors++;break; + case 5: + lp->net_stats.rx_length_errors++;break; + } + } + p->length = 1532; + p->control &= ~(1<<6); + p->status = 0; + base = p->next; + } + while(x++<48); + + /* + * This is curious. It seems the receive stop and receive continue + * commands race against each other, even though we poll for + * command ready to be issued. The delay is hackish but is a workaround + * while I investigate in depth + */ + + while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR)); + lp->rx_box->mbox=0; + lp->rx_box->data[0] = top; + outb(1<<3, ioaddr+HOST_CMD); +} + + +/* + * The typical workload of the driver: + * Handle the network interface interrupts. + */ +static void mc32_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + struct device *dev = dev_id; + struct mc32_local *lp; + int ioaddr, status, boguscount = 0; + int rx_event = 0; + + if (dev == NULL) { + printk(KERN_WARNING "%s: irq %d for unknown device.\n", cardname, irq); + return; + } + dev->interrupt = 1; + + ioaddr = dev->base_addr; + lp = (struct mc32_local *)dev->priv; + + /* See whats cooking */ + + while((inb(ioaddr+2)&(1<<5)) && boguscount++<2000) + { + status=inb(ioaddr+HOST_CMD); + +#ifdef DEBUG_IRQ + printk("Status TX%d RX%d EX%d OV%d\n", + (status&7), (status>>3)&7, (status>>6)&1, + (status>>7)&1); +#endif + + switch(status&7) + { + case 0: + break; + case 6: /* TX fail */ + lp->net_stats.tx_errors++; + case 2: /* TX ok */ + lp->net_stats.tx_packets++; + /* Packets are sent in order - this is + basically a FIFO queue of buffers matching + the card ring */ + lp->net_stats.tx_bytes+=lp->tx_skb[lp->tx_skb_top]->len; + dev_kfree_skb(lp->tx_skb[lp->tx_skb_top]); + lp->tx_skb[lp->tx_skb_top]=NULL; + lp->tx_skb_top++; + lp->tx_skb_top&=(TX_RING_MAX-1); + atomic_inc(&lp->tx_count); + dev->tbusy=0; + mark_bh(NET_BH); + break; + case 3: /* Halt */ + case 4: /* Abort */ + lp->tx_halted=1; + wake_up(&lp->event); + break; + case 5: + lp->tx_halted=0; + wake_up(&lp->event); + break; + default: + printk("%s: strange tx ack %d\n", + dev->name, status&7); + } + status>>=3; + switch(status&7) + { + case 0: + break; + case 2: /* RX */ + rx_event=1; + break; + case 3: + case 4: + lp->rx_halted=1; + wake_up(&lp->event); + break; + case 5: + lp->rx_halted=0; + wake_up(&lp->event); + break; + case 6: + /* Out of RX buffers stat */ + /* Must restart */ + lp->net_stats.rx_dropped++; + rx_event = 1; /* To restart */ + break; + default: + printk("%s: strange rx ack %d\n", + dev->name, status&7); + + } + status>>=3; + if(status&1) + { + /* 0=no 1=yes 2=reply clearing */ + lp->exec_pending=2; + wake_up(&lp->event); + } + if(status&2) + { + /* + * Update the stats as soon as + * we have it flagged and can + * send an immediate reply (CRR set) + */ + + if(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR) + { + mc32_update_stats(dev); + outb(0, ioaddr+HOST_CMD); + } + } + } + + /* + * Process and restart the receive ring. + */ + + if(rx_event) + mc32_rx_ring(dev); + dev->interrupt = 0; + return; +} + + +/* The inverse routine to mc32_open(). */ + +static int mc32_close(struct device *dev) +{ + struct mc32_local *lp = (struct mc32_local *)dev->priv; + int ioaddr = dev->base_addr; + u8 regs; + u16 one=1; + + /* + * Send the indications on command (handy debug check) + */ + + mc32_command(dev, 4, &one, 2); + + /* Abort RX and Abort TX */ + + mc32_rx_abort(dev); + mc32_tx_abort(dev); + + /* Catch any waiting commands */ + + while(lp->exec_pending==1) + sleep_on(&lp->event); + + /* Ok the card is now stopping */ + + regs=inb(ioaddr+HOST_CTRL); + regs&=~HOST_CTRL_INTE; + outb(regs, ioaddr+HOST_CTRL); + + mc32_flush_rx_ring(lp); + mc32_flush_tx_ring(lp); + + dev->tbusy = 1; + dev->start = 0; + + /* Update the statistics here. */ + + MOD_DEC_USE_COUNT; + + return 0; + +} + +/* + * Get the current statistics. + * This may be called with the card open or closed. + */ + +static struct net_device_stats *mc32_get_stats(struct device *dev) +{ + struct mc32_local *lp = (struct mc32_local *)dev->priv; + return &lp->net_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 mc32_set_multicast_list(struct device *dev) +{ + u16 filt; + if (dev->flags&IFF_PROMISC) + { + /* Enable promiscuous mode */ + filt = 1; + mc32_command(dev, 0, &filt, 2); + } + else if((dev->flags&IFF_ALLMULTI) || dev->mc_count > 10) + { + dev->flags|=IFF_PROMISC; + filt = 1; + mc32_command(dev, 0, &filt, 2); + } + else if(dev->mc_count) + { + unsigned char block[62]; + unsigned char *bp; + struct dev_mc_list *dmc=dev->mc_list; + + int i; + + filt = 0; + block[1]=0; + block[0]=dev->mc_count; + bp=block+2; + + for(i=0;imc_count;i++) + { + memcpy(bp, dmc->dmi_addr, 6); + bp+=6; + dmc=dmc->next; + } + mc32_command(dev, 2, block, 2+6*dev->mc_count); + mc32_command(dev, 0, &filt, 2); + } + else + { + filt = 0; + mc32_command(dev, 0, &filt, 2); + } +} + +#ifdef MODULE + +static char devicename[9] = { 0, }; +static struct device this_device = { + devicename, /* will be inserted by linux/drivers/net/mc32_init.c */ + 0, 0, 0, 0, + 0, 0, /* I/O address, IRQ */ + 0, 0, 0, NULL, mc32_probe }; + +int init_module(void) +{ + int result; + + if ((result = register_netdev(&this_device)) != 0) + return result; + + return 0; +} + +void cleanup_module(void) +{ + int slot; + + /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ + unregister_netdev(&this_device); + + /* + * If we don't do this, we can't re-insmod it later. + * Release irq/dma here, when you have jumpered versions and + * allocate them in mc32_probe1(). + */ + + if (this_device.priv) + { + struct mc32_local *lp=this_device.priv; + slot = lp->slot; + mca_mark_as_unused(slot); + mca_set_adapter_name(slot, NULL); + kfree_s(this_device.priv, sizeof(struct mc32_local)); + } + free_irq(this_device.irq, &this_device); +} + +#endif /* MODULE */ diff -u --recursive --new-file v2.2.3/linux/drivers/net/3c527.h linux/drivers/net/3c527.h --- v2.2.3/linux/drivers/net/3c527.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/3c527.h Sun Mar 21 07:11:36 1999 @@ -0,0 +1,40 @@ +/* + * 3COM "EtherLink MC/32" Descriptions + */ + +/* + * Registers + */ + +#define HOST_CMD 0 + +#define HOST_STATUS 2 +#define HOST_STATUS_CRR (1<<6) +#define HOST_STATUS_CWR (1<<5) + +#define HOST_CTRL 6 +#define HOST_CTRL_ATTN (1<<7) +#define HOST_CTRL_RESET (1<<6) +#define HOST_CTRL_INTE (1<<2) + +#define HOST_RAMPAGE 8 + +struct skb_header +{ + u8 status __attribute((packed)); + u8 control __attribute((packed)); + u16 next __attribute((packed)); /* Do not change! */ + u16 length __attribute((packed)); + u32 data __attribute((packed)); +}; + +#define STATUS_MASK 0x0F +#define COMPLETED 0x80 +#define COMPLETED_OK 0x40 +#define BUFFER_BUSY 0x20 + +#define CONTROL_EOP 0x80 /* End Of Packet */ +#define CONTROL_EL 0x40 /* End of List */ + + +#define MCA_MC32_ID 0x0041 /* Our MCA ident */ \ No newline at end of file diff -u --recursive --new-file v2.2.3/linux/drivers/net/Config.in linux/drivers/net/Config.in --- v2.2.3/linux/drivers/net/Config.in Wed Jan 20 23:14:05 1999 +++ linux/drivers/net/Config.in Sun Mar 21 07:11:36 1999 @@ -32,8 +32,8 @@ fi fi if [ "$CONFIG_PPC" = "y" ]; then - bool 'MACE (Power Mac ethernet) support' CONFIG_MACE - bool 'BMAC (G3 ethernet) support' CONFIG_BMAC + tristate 'MACE (Power Mac ethernet) support' CONFIG_MACE + tristate 'BMAC (G3 ethernet) support' CONFIG_BMAC fi if [ "$CONFIG_ZORRO" = "y" ]; then tristate 'Ariadne support' CONFIG_ARIADNE @@ -53,6 +53,7 @@ tristate '3c507 support' CONFIG_EL16 if [ "$CONFIG_MCA" = "y" ]; then tristate '3c523 support' CONFIG_ELMC + tristate '3c527 support' CONFIG_ELMC_II fi fi tristate '3c509/3c579 support' CONFIG_EL3 diff -u --recursive --new-file v2.2.3/linux/drivers/net/Makefile linux/drivers/net/Makefile --- v2.2.3/linux/drivers/net/Makefile Wed Mar 10 15:29:46 1999 +++ linux/drivers/net/Makefile Sun Mar 21 07:11:36 1999 @@ -430,6 +430,14 @@ endif endif +ifeq ($(CONFIG_SUNBMAC),y) +L_OBJS += sunbmac.o +else + ifeq ($(CONFIG_SUNBMAC),m) + M_OBJS += sunbmac.o + endif +endif + ifeq ($(CONFIG_MYRI_SBUS),y) L_OBJS += myri_sbus.o else @@ -478,6 +486,14 @@ endif endif +ifeq ($(CONFIG_ELMC_II),y) +L_OBJS += 3c527.o +else + ifeq ($(CONFIG_ELMC_II),m) + M_OBJS += 3c527.o + endif +endif + ifeq ($(CONFIG_EL3),y) L_OBJS += 3c509.o else @@ -989,10 +1005,18 @@ ifeq ($(CONFIG_MACE),y) L_OBJS += mace.o +else + ifeq ($(CONFIG_MACE),m) + M_OBJS += mace.o + endif endif ifeq ($(CONFIG_BMAC),y) L_OBJS += bmac.o +else + ifeq ($(CONFIG_BMAC),m) + M_OBJS += bmac.o + endif endif ifeq ($(CONFIG_VENDOR_SANGOMA),y) diff -u --recursive --new-file v2.2.3/linux/drivers/net/Space.c linux/drivers/net/Space.c --- v2.2.3/linux/drivers/net/Space.c Tue Feb 23 15:21:33 1999 +++ linux/drivers/net/Space.c Mon Mar 15 16:11:30 1999 @@ -87,6 +87,7 @@ extern int sparc_lance_probe(struct device *); extern int happy_meal_probe(struct device *); extern int qec_probe(struct device *); +extern int bigmac_probe(struct device *); extern int myri_sbus_probe(struct device *); extern int sgiseeq_probe(struct device *); extern int atarilance_probe(struct device *); @@ -246,6 +247,9 @@ #endif #ifdef CONFIG_SUNQE {qec_probe, 0}, +#endif +#ifdef CONFIG_SUNBMAC + {bigmac_probe, 0}, #endif #ifdef CONFIG_MYRI_SBUS {myri_sbus_probe, 0}, diff -u --recursive --new-file v2.2.3/linux/drivers/net/a2065.c linux/drivers/net/a2065.c --- v2.2.3/linux/drivers/net/a2065.c Wed Mar 10 15:29:46 1999 +++ linux/drivers/net/a2065.c Sun Mar 21 07:22:00 1999 @@ -135,6 +135,7 @@ struct Linux_SBus_DMA *ledma; /* if set this points to ledma and arch=4m */ int burst_sizes; /* ledma SBus burst sizes */ #endif + struct timer_list multicast_timer; }; #define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\ @@ -527,6 +528,7 @@ dev->start = 0; dev->tbusy = 1; + del_timer(&lp->multicast_timer); /* Stop the card */ ll->rap = LE_CSR0; @@ -706,12 +708,20 @@ volatile struct lance_init_block *ib = lp->init_block; volatile struct lance_regs *ll = lp->ll; - while (dev->tbusy) - schedule(); + if (!dev->start) + return; + + if (dev->tbusy) { + mod_timer(&lp->multicast_timer, jiffies + 2); + return; + } set_bit (0, (void *) &dev->tbusy); - while (lp->tx_old != lp->tx_new) - schedule(); + if (lp->tx_old != lp->tx_new) { + mod_timer(&lp->multicast_timer, jiffies + 4); + dev->tbusy = 0; + return; + } ll->rap = LE_CSR0; ll->rdp = LE_C0_STOP; @@ -726,6 +736,7 @@ load_csrs (lp); init_restart_lance (lp); dev->tbusy = 0; + mark_bh(NET_BH); } @@ -795,6 +806,11 @@ dev->dma = 0; ether_setup(dev); + init_timer(&priv->multicast_timer); + priv->multicast_timer.data = (unsigned long) dev; + priv->multicast_timer.function = + (void (*)(unsigned long)) &lance_set_multicast; + zorro_config_board(key, 0); return(0); } diff -u --recursive --new-file v2.2.3/linux/drivers/net/bsd_comp.c linux/drivers/net/bsd_comp.c --- v2.2.3/linux/drivers/net/bsd_comp.c Sat Nov 29 10:33:19 1997 +++ linux/drivers/net/bsd_comp.c Wed Mar 10 16:51:35 1999 @@ -70,7 +70,6 @@ #include #include #include -#include /* to get the struct task_struct */ #include /* used in new tty drivers */ #include /* used in new tty drivers */ diff -u --recursive --new-file v2.2.3/linux/drivers/net/cs89x0.c linux/drivers/net/cs89x0.c --- v2.2.3/linux/drivers/net/cs89x0.c Sun Nov 8 14:03:00 1998 +++ linux/drivers/net/cs89x0.c Wed Mar 10 16:51:35 1999 @@ -991,22 +991,26 @@ 0, 0, 0, 0, 0, NULL, NULL }; -int io=0; -int irq=0; -#endif -#ifdef MODULE -int debug=1; -char *media="auto"; -char *duplex="f"; +static int io=0; +static int irq=0; +static int debug=0; +static char media[8]; +static int duplex=-1; + +MODULE_PARM(io, "i"); +MODULE_PARM(irq, "i"); +MODULE_PARM(debug, "i"); +MODULE_PARM(media, "s"); +MODULE_PARM(duplex, "i"); + +EXPORT_NO_SYMBOLS; /* * media=t - specify media type or media=2 or media=aui or medai=auto -* duplex=f - specify forced half/full/autonegotiate duplex - or duplex=h - or duplex=auto +* duplex=0 - specify forced half/full/autonegotiate duplex * debug=# - debug level @@ -1044,12 +1048,14 @@ /* boy, they'd better get these right */ if (!strcmp(media, "rj45")) lp->adapter_cnf = A_CNF_MEDIA_10B_T | A_CNF_10B_T; - if (!strcmp(media, "aui")) + else if (!strcmp(media, "aui")) lp->adapter_cnf = A_CNF_MEDIA_AUI | A_CNF_AUI; - if (!strcmp(media, "bnc")) + else if (!strcmp(media, "bnc")) lp->adapter_cnf = A_CNF_MEDIA_10B_2 | A_CNF_10B_2; + else + lp->adapter_cnf = A_CNF_MEDIA_10B_T | A_CNF_10B_T; - if (!strcmp(duplex, "auto")) + if (duplex==-1) lp->auto_neg_cnf = AUTO_NEG_ENABLE; if (io == 0) { diff -u --recursive --new-file v2.2.3/linux/drivers/net/ibmtr.c linux/drivers/net/ibmtr.c --- v2.2.3/linux/drivers/net/ibmtr.c Mon Jan 25 17:44:34 1999 +++ linux/drivers/net/ibmtr.c Tue Mar 16 14:21:51 1999 @@ -70,6 +70,9 @@ * Changes by Joel Sloan (jjs@c-me.com) : * + disable verbose debug messages by default - to enable verbose * debugging, edit the IBMTR_DEBUG_MESSAGES define below + * + * Changes by Tim Hockin (thockin@isunix.it.ilstu.edu) : + * + added spinlocks for SMP sanity (10 March 1999) */ /* change the define of IBMTR_DEBUG_MESSAGES to a nonzero value @@ -144,6 +147,7 @@ #include #include +#include #include #include @@ -754,6 +758,9 @@ { struct tok_info *ti=(struct tok_info *)dev->priv; + /* init the spinlock */ + ti->lock = (spinlock_t) SPIN_LOCK_UNLOCKED; + if (ti->open_status==CLOSED) tok_init_card(dev); if (ti->open_status==IN_PROGRESS) sleep_on(&ti->wait_for_reset); @@ -806,6 +813,7 @@ DPRINTK("Int from tok_driver, dev : %p\n",dev); #endif ti = (struct tok_info *) dev->priv; + spin_lock(&(ti->lock)); /* Disable interrupts till processing is finished */ dev->interrupt=1; @@ -832,6 +840,7 @@ if (status == 0xFF) { DPRINTK("PCMCIA card removed.\n"); + spin_unlock(&(ti->lock)); dev->interrupt = 0; return; } @@ -840,6 +849,7 @@ if ( readb (ti->mmio + ACA_OFFSET + ACA_RW + ISRP_EVEN) == 0xFF) { DPRINTK("PCMCIA card removed.\n"); + spin_unlock(&(ti->lock)); dev->interrupt = 0; return; } @@ -1167,6 +1177,7 @@ DPRINTK("Unexpected interrupt from tr adapter\n"); } + spin_unlock(&(ti->lock)); } static void initial_tok_int(struct device *dev) @@ -1598,12 +1609,19 @@ if (test_and_set_bit(0,(void *)&dev->tbusy)!=0) DPRINTK("Transmitter access conflict\n"); else { + int flags; + + /* lock against other CPUs */ + spin_lock_irqsave(&(ti->lock), flags); + /* Save skb; we'll need it when the adapter asks for the data */ ti->current_skb=skb; writeb(XMIT_UI_FRAME, ti->srb + offsetof(struct srb_xmit, command)); writew(ti->exsap_station_id, ti->srb +offsetof(struct srb_xmit, station_id)); writeb(CMD_IN_SRB, (ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD)); + spin_unlock_irqrestore(&(ti->lock), flags); + dev->trans_start=jiffies; } diff -u --recursive --new-file v2.2.3/linux/drivers/net/ibmtr.h linux/drivers/net/ibmtr.h --- v2.2.3/linux/drivers/net/ibmtr.h Tue Jul 28 14:21:08 1998 +++ linux/drivers/net/ibmtr.h Tue Mar 16 14:21:52 1999 @@ -217,6 +217,7 @@ unsigned char ring_speed; __u32 func_addr; unsigned int retry_count; + spinlock_t lock; /* SMP protection */ }; /* token ring adapter commands */ diff -u --recursive --new-file v2.2.3/linux/drivers/net/mace.c linux/drivers/net/mace.c --- v2.2.3/linux/drivers/net/mace.c Mon Oct 5 13:13:39 1998 +++ linux/drivers/net/mace.c Wed Mar 10 21:48:46 1999 @@ -4,6 +4,12 @@ * * Copyright (C) 1996 Paul Mackerras. */ + +#ifdef MODULE +#include +#include +#endif + #include #include #include @@ -16,6 +22,10 @@ #include #include "mace.h" +#ifdef MODULE +static struct device *mace_devs = NULL; +#endif + #define N_RX_RING 8 #define N_TX_RING 6 #define MAX_TX_ACTIVE 1 @@ -71,6 +81,9 @@ static void mace_rxdma_intr(int irq, void *dev_id, struct pt_regs *regs); static void mace_set_timeout(struct device *dev); static void mace_tx_timeout(unsigned long data); +static inline void dbdma_reset(volatile struct dbdma_regs *dma); +static inline void mace_clean_rings(struct mace_data *mp); +static void __mace_set_address(struct device *dev, void *addr); /* * If we can't get a skbuff when we need it, we use this area for DMA. @@ -78,7 +91,7 @@ static unsigned char dummy_buf[RX_BUFLEN+2]; /* Bit-reverse one byte of an ethernet hardware address. */ -static int +static inline int bitrev(int b) { int d = 0, i; @@ -144,7 +157,8 @@ dev->dev_addr[j] = rev? bitrev(addr[j]): addr[j]; printk("%c%.2x", (j? ':': ' '), dev->dev_addr[j]); } - printk("\n"); + printk(", chip revision %d.%d\n", + in_8(&mp->mace->chipid_hi), in_8(&mp->mace->chipid_lo)); mp = (struct mace_data *) dev->priv; mp->maccc = ENXMT | ENRCV; @@ -193,6 +207,21 @@ return 0; } +static void dbdma_reset(volatile struct dbdma_regs *dma) +{ + int i; + + out_le32(&dma->control, (WAKE|FLUSH|PAUSE|RUN) << 16); + + /* + * Yes this looks peculiar, but apparently it needs to be this + * way on some machines. + */ + for (i = 200; i > 0; --i) + if (ld_le32(&dma->control) & RUN) + udelay(1); +} + static void mace_reset(struct device *dev) { struct mace_data *mp = (struct mace_data *) dev->priv; @@ -203,14 +232,14 @@ i = 200; while (--i) { out_8(&mb->biucc, SWRST); - if (mb->biucc & SWRST) { - udelay(20); + if (in_8(&mb->biucc) & SWRST) { + udelay(10); continue; } break; } if (!i) { - printk("mace: cannot reset chip!\n"); + printk(KERN_ERR "mace: cannot reset chip!\n"); return; } @@ -218,19 +247,14 @@ i = in_8(&mb->ir); out_8(&mb->maccc, 0); /* turn off tx, rx */ - mb->biucc = XMTSP_64; - mb->utr = RTRD; - mb->fifocc = RCVFW_32 | XMTFW_16 | XMTFWU | RCVFWU | XMTBRST; - mb->xmtfc = AUTO_PAD_XMIT; /* auto-pad short frames */ - mb->rcvfc = 0; + out_8(&mb->biucc, XMTSP_64); + out_8(&mb->utr, RTRD); + out_8(&mb->fifocc, RCVFW_32 | XMTFW_16 | XMTFWU | RCVFWU | XMTBRST); + out_8(&mb->xmtfc, AUTO_PAD_XMIT); /* auto-pad short frames */ + out_8(&mb->rcvfc, 0); /* load up the hardware address */ - out_8(&mb->iac, ADDRCHG | PHYADDR); - while ((in_8(&mb->iac) & ADDRCHG) != 0) - ; - for (i = 0; i < 6; ++i) { - out_8(&mb->padr, dev->dev_addr[i]); - } + __mace_set_address(dev, dev->dev_addr); /* clear the multicast filter */ out_8(&mb->iac, ADDRCHG | LOGADDR); @@ -245,23 +269,30 @@ out_8(&mb->plscc, PORTSEL_GPSI + ENPLSIO); } -static int mace_set_address(struct device *dev, void *addr) +static void __mace_set_address(struct device *dev, void *addr) { + volatile struct mace *mb = ((struct mace_data *) dev->priv)->mace; unsigned char *p = addr; - struct mace_data *mp = (struct mace_data *) dev->priv; - volatile struct mace *mb = mp->mace; int i; - unsigned long flags; - - save_flags(flags); cli(); /* load up the hardware address */ out_8(&mb->iac, ADDRCHG | PHYADDR); while ((in_8(&mb->iac) & ADDRCHG) != 0) ; - for (i = 0; i < 6; ++i) { + for (i = 0; i < 6; ++i) out_8(&mb->padr, dev->dev_addr[i] = p[i]); - } +} + +static int mace_set_address(struct device *dev, void *addr) +{ + struct mace_data *mp = (struct mace_data *) dev->priv; + volatile struct mace *mb = mp->mace; + unsigned long flags; + + save_flags(flags); cli(); + + __mace_set_address(dev, addr); + out_8(&mb->iac, 0); /* note: setting ADDRCHG clears ENRCV */ out_8(&mb->maccc, mp->maccc); @@ -285,6 +316,7 @@ mace_reset(dev); /* initialize list of sk_buffs for receiving and set up recv dma */ + mace_clean_rings(mp); memset((char *)mp->rx_cmds, 0, N_RX_RING * sizeof(struct dbdma_cmd)); cp = mp->rx_cmds; for (i = 0; i < N_RX_RING - 1; ++i) { @@ -335,25 +367,17 @@ out_8(&mb->maccc, mp->maccc); /* enable all interrupts except receive interrupts */ out_8(&mb->imr, RCVINT); + +#ifdef MOD_INC_USE_COUNT + MOD_INC_USE_COUNT; +#endif return 0; } -static int mace_close(struct device *dev) +static inline void mace_clean_rings(struct mace_data *mp) { - struct mace_data *mp = (struct mace_data *) dev->priv; - volatile struct mace *mb = mp->mace; - volatile struct dbdma_regs *rd = mp->rx_dma; - volatile struct dbdma_regs *td = mp->tx_dma; int i; - /* disable rx and tx */ - mb->maccc = 0; - mb->imr = 0xff; /* disable all intrs */ - - /* disable rx and tx dma */ - st_le32(&rd->control, (RUN|PAUSE|FLUSH|WAKE) << 16); /* clear run bit */ - st_le32(&td->control, (RUN|PAUSE|FLUSH|WAKE) << 16); /* clear run bit */ - /* free some skb's */ for (i = 0; i < N_RX_RING; ++i) { if (mp->rx_bufs[i] != 0) { @@ -366,6 +390,28 @@ if (++i >= N_TX_RING) i = 0; } +} + +static int mace_close(struct device *dev) +{ + struct mace_data *mp = (struct mace_data *) dev->priv; + volatile struct mace *mb = mp->mace; + volatile struct dbdma_regs *rd = mp->rx_dma; + volatile struct dbdma_regs *td = mp->tx_dma; + + /* disable rx and tx */ + out_8(&mb->maccc, 0); + out_8(&mb->imr, 0xff); /* disable all intrs */ + + /* disable rx and tx dma */ + st_le32(&rd->control, (RUN|PAUSE|FLUSH|WAKE) << 16); /* clear run bit */ + st_le32(&td->control, (RUN|PAUSE|FLUSH|WAKE) << 16); /* clear run bit */ + + mace_clean_rings(mp); + +#ifdef MOD_DEC_USE_COUNT + MOD_DEC_USE_COUNT; +#endif return 0; } @@ -517,10 +563,10 @@ if (intr & MPCO) mp->stats.rx_missed_errors += 256; - mp->stats.rx_missed_errors += mb->mpc; /* reading clears it */ + mp->stats.rx_missed_errors += in_8(&mb->mpc); /* reading clears it */ if (intr & RNTPCO) mp->stats.rx_length_errors += 256; - mp->stats.rx_length_errors += mb->rntpc; /* reading clears it */ + mp->stats.rx_length_errors += in_8(&mb->rntpc); /* reading clears it */ if (intr & CERR) ++mp->stats.tx_heartbeat_errors; if (intr & BABBLE) @@ -547,7 +593,7 @@ mace_handle_misc_intrs(mp, intr); i = mp->tx_empty; - while (mb->pr & XMTSV) { + while (in_8(&mb->pr) & XMTSV) { del_timer(&mp->tx_timeout); mp->timeout_active = 0; /* @@ -555,13 +601,13 @@ * word. This appears to unlatch any error indication from * the DMA controller. */ - intr = mb->ir; + intr = in_8(&mb->ir); if (intr != 0) mace_handle_misc_intrs(mp, intr); if (mp->tx_bad_runt) { fs = in_8(&mb->xmtfs); mp->tx_bad_runt = 0; - mb->xmtfc = AUTO_PAD_XMIT; + out_8(&mb->xmtfc, AUTO_PAD_XMIT); continue; } dstat = ld_le32(&td->status); @@ -571,7 +617,7 @@ * xcount is the number of complete frames which have been * written to the fifo but for which status has not been read. */ - xcount = (mb->fifofc >> XMTFC_SH) & XMTFC_MASK; + xcount = (in_8(&mb->fifofc) >> XMTFC_SH) & XMTFC_MASK; if (xcount == 0 || (dstat & DEAD)) { /* * If a packet was aborted before the DMA controller has @@ -586,11 +632,15 @@ */ out_8(&mb->xmtfc, DXMTFCS); } - fs = mb->xmtfs; + fs = in_8(&mb->xmtfs); if ((fs & XMTSV) == 0) { printk(KERN_ERR "mace: xmtfs not valid! (fs=%x xc=%d ds=%x)\n", fs, xcount, dstat); - return; + mace_reset(dev); + /* + * XXX mace likes to hang the machine after a xmtfs error. + * This is hard to reproduce, reseting *may* help + */ } cp = mp->tx_cmds + NCMDS_TX * i; stat = ld_le16(&cp->xfer_status); @@ -600,7 +650,7 @@ * the transmit FIFO. */ udelay(1); - x = (mb->fifofc >> XMTFC_SH) & XMTFC_MASK; + x = (in_8(&mb->fifofc) >> XMTFC_SH) & XMTFC_MASK; if (x != 0) { /* there were two bytes with an end-of-packet indication */ mp->tx_bad_runt = 1; @@ -612,10 +662,10 @@ * We flush the transmit FIFO just in case (by setting the * XMTFWU bit with the transmitter disabled). */ - out_8(&mb->maccc, mb->maccc & ~ENXMT); - out_8(&mb->fifocc, mb->fifocc | XMTFWU); + out_8(&mb->maccc, in_8(&mb->maccc) & ~ENXMT); + out_8(&mb->fifocc, in_8(&mb->fifocc) | XMTFWU); udelay(1); - out_8(&mb->maccc, mb->maccc | ENXMT); + out_8(&mb->maccc, in_8(&mb->maccc) | ENXMT); out_8(&mb->xmtfc, AUTO_PAD_XMIT); } } @@ -632,7 +682,7 @@ ++mp->stats.tx_carrier_errors; if (fs & (UFLO|LCOL|RTRY)) ++mp->stats.tx_aborted_errors; - } else + } else ++mp->stats.tx_packets; dev_kfree_skb(mp->tx_bufs[i]); --mp->tx_active; @@ -686,19 +736,19 @@ goto out; /* update various counters */ - mace_handle_misc_intrs(mp, mb->ir); + mace_handle_misc_intrs(mp, in_8(&mb->ir)); cp = mp->tx_cmds + NCMDS_TX * mp->tx_empty; /* turn off both tx and rx and reset the chip */ out_8(&mb->maccc, 0); - out_le32(&td->control, (RUN|PAUSE|FLUSH|WAKE) << 16); printk(KERN_ERR "mace: transmit timeout - resetting\n"); + dbdma_reset(td); mace_reset(dev); /* restart rx dma */ cp = bus_to_virt(ld_le32(&rd->cmdptr)); - out_le32(&rd->control, (RUN|PAUSE|FLUSH|WAKE) << 16); + dbdma_reset(rd); out_le16(&cp->xfer_status, 0); out_le32(&rd->cmdptr, virt_to_bus(cp)); out_le32(&rd->control, (RUN << 16) | RUN); @@ -786,8 +836,8 @@ ++mp->stats.rx_crc_errors; } else { /* Mace feature AUTO_STRIP_RCV is on by default, dropping the - * FCS on frames with 802.3 headers. This means that Ethernet - * frames have 8 extra octets at the end, while 802.3 frames + * FCS on frames with 802.3 headers. This means that Ethernet + * frames have 8 extra octets at the end, while 802.3 frames * have only 4. We need to correctly account for this. */ if (*(unsigned short *)(data+12) < 1536) /* 802.3 header */ nb -= 4; @@ -846,3 +896,35 @@ mp->rx_fill = i; } } + +#ifdef MODULE + +#if LINUX_VERSION_CODE > 0x20118 +MODULE_AUTHOR("Paul Mackerras"); +MODULE_DESCRIPTION("PowerMac MACE driver."); +#endif + +int init_module(void) +{ + int res; + + if(mace_devs != NULL) + return -EBUSY; + res = mace_probe(NULL); + return res; +} + +void cleanup_module(void) +{ + struct mace_data *mp = (struct mace_data *) mace_devs->priv; + unregister_netdev(mace_devs); + + free_irq(mace_devs->irq, mace_interrupt); + free_irq(mp->tx_dma_intr, mace_txdma_intr); + free_irq(mp->rx_dma_intr, mace_rxdma_intr); + + kfree(mace_devs); + mace_devs = NULL; +} + +#endif diff -u --recursive --new-file v2.2.3/linux/drivers/net/myri_sbus.c linux/drivers/net/myri_sbus.c --- v2.2.3/linux/drivers/net/myri_sbus.c Tue Jul 28 14:21:08 1998 +++ linux/drivers/net/myri_sbus.c Mon Mar 15 16:11:30 1999 @@ -278,7 +278,7 @@ mp->rx_skbs[i] = skb; skb->dev = dev; skb_put(skb, RX_ALLOC_SIZE); - rxd[i].myri_scatters[0].addr = (u32) ((unsigned long)skb->data); + rxd[i].myri_scatters[0].addr = sbus_dvma_addr(skb->data); rxd[i].myri_scatters[0].len = RX_ALLOC_SIZE; rxd[i].ctx = i; rxd[i].num_sg = 1; @@ -340,12 +340,6 @@ dev_kfree_skb(skb); mp->tx_skbs[entry] = NULL; mp->enet_stats.tx_packets++; - -#ifdef NEED_DMA_SYNCHRONIZATION - mmu_sync_dma(((u32)((unsigned long)skb->data)), - skb->len, mp->myri_sbus_dev->my_bus); -#endif - entry = NEXT_TX(entry); } mp->tx_old = entry; @@ -437,8 +431,7 @@ drops++; DRX(("DROP ")); mp->enet_stats.rx_dropped++; - rxd->myri_scatters[0].addr = - (u32) ((unsigned long)skb->data); + rxd->myri_scatters[0].addr = sbus_dvma_addr(skb->data); rxd->myri_scatters[0].len = RX_ALLOC_SIZE; rxd->ctx = index; rxd->num_sg = 1; @@ -447,7 +440,7 @@ } #ifdef NEED_DMA_SYNCHRONIZATION - mmu_sync_dma(((u32)((unsigned long)skb->data)), + mmu_sync_dma(sbus_dvma_addr(skb->data), skb->len, mp->myri_sbus_dev->my_bus); #endif @@ -464,8 +457,7 @@ mp->rx_skbs[index] = new_skb; new_skb->dev = dev; skb_put(new_skb, RX_ALLOC_SIZE); - rxd->myri_scatters[0].addr = - (u32) ((unsigned long)new_skb->data); + rxd->myri_scatters[0].addr = sbus_dvma_addr(new_skb->data); rxd->myri_scatters[0].len = RX_ALLOC_SIZE; rxd->ctx = index; rxd->num_sg = 1; @@ -489,8 +481,7 @@ /* Reuse original ring buffer. */ DRX(("reuse ")); - rxd->myri_scatters[0].addr = - (u32) ((unsigned long)skb->data); + rxd->myri_scatters[0].addr = sbus_dvma_addr(skb->data); rxd->myri_scatters[0].len = RX_ALLOC_SIZE; rxd->ctx = index; rxd->num_sg = 1; @@ -600,6 +591,12 @@ return 1; } + +#ifdef NEED_DMA_SYNCHRONIZATION + mmu_sync_dma(sbus_dvma_addr(skb->data), + skb->len, mp->myri_sbus_dev->my_bus); +#endif + /* This is just to prevent multiple PIO reads for TX_BUFFS_AVAIL. */ head = sq->head; tail = sq->tail; @@ -628,8 +625,7 @@ txd = &sq->myri_txd[entry]; mp->tx_skbs[entry] = skb; - txd->myri_gathers[0].addr = - (unsigned int) ((unsigned long)skb->data); + txd->myri_gathers[0].addr = sbus_dvma_addr(skb->data); txd->myri_gathers[0].len = len; txd->num_sg = 1; txd->chan = KERNEL_CHANNEL; diff -u --recursive --new-file v2.2.3/linux/drivers/net/ne2.c linux/drivers/net/ne2.c --- v2.2.3/linux/drivers/net/ne2.c Fri Jan 8 22:36:07 1999 +++ linux/drivers/net/ne2.c Wed Mar 10 16:51:35 1999 @@ -75,7 +75,6 @@ #include #include #include -#include #include #include diff -u --recursive --new-file v2.2.3/linux/drivers/net/ppp.c linux/drivers/net/ppp.c --- v2.2.3/linux/drivers/net/ppp.c Tue Feb 23 15:21:33 1999 +++ linux/drivers/net/ppp.c Wed Mar 10 16:51:35 1999 @@ -61,7 +61,6 @@ #include #include #include -#include /* to get the struct task_struct */ #include /* used in new tty drivers */ #include /* used in new tty drivers */ #include diff -u --recursive --new-file v2.2.3/linux/drivers/net/ppp_deflate.c linux/drivers/net/ppp_deflate.c --- v2.2.3/linux/drivers/net/ppp_deflate.c Wed Apr 1 20:11:52 1998 +++ linux/drivers/net/ppp_deflate.c Wed Mar 10 16:51:35 1999 @@ -43,7 +43,6 @@ #include #include #include -#include /* to get the struct task_struct */ #include /* used in new tty drivers */ #include /* used in new tty drivers */ diff -u --recursive --new-file v2.2.3/linux/drivers/net/shaper.c linux/drivers/net/shaper.c --- v2.2.3/linux/drivers/net/shaper.c Wed Mar 10 15:29:46 1999 +++ linux/drivers/net/shaper.c Wed Mar 10 16:51:35 1999 @@ -66,7 +66,6 @@ #include #include #include -#include #include #include #include diff -u --recursive --new-file v2.2.3/linux/drivers/net/smc9194.c linux/drivers/net/smc9194.c --- v2.2.3/linux/drivers/net/smc9194.c Thu Feb 12 20:56:09 1998 +++ linux/drivers/net/smc9194.c Wed Mar 10 16:51:35 1999 @@ -65,7 +65,6 @@ #include #include #include -#include #include #include #include diff -u --recursive --new-file v2.2.3/linux/drivers/net/sunbmac.c linux/drivers/net/sunbmac.c --- v2.2.3/linux/drivers/net/sunbmac.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sunbmac.c Mon Mar 15 16:11:30 1999 @@ -0,0 +1,1557 @@ +/* sunbmac.c: Driver for Sparc BigMAC 100baseT ethernet adapters. + * + * Copyright (C) 1997, 1998 David S. Miller (davem@caip.rutgers.edu) + */ + +static char *version = + "sunbmac.c:v1.1 8/Dec/98 David S. Miller (davem@caipfs.rutgers.edu)\n"; + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "sunbmac.h" + +#undef DEBUG_PROBE +#undef DEBUG_TX +#undef DEBUG_IRQ + +#ifdef DEBUG_PROBE +#define DP(x) printk x +#else +#define DP(x) +#endif + +#ifdef DEBUG_TX +#define DTX(x) printk x +#else +#define DTX(x) +#endif + +#ifdef DEBUG_IRQ +#define DIRQ(x) printk x +#else +#define DIRQ(x) +#endif + +#ifdef MODULE +static struct bigmac *root_bigmac_dev = NULL; +#endif + +#define DEFAULT_JAMSIZE 4 /* Toe jam */ + +#define QEC_RESET_TRIES 200 + +static inline int qec_global_reset(struct qe_globreg *gregs) +{ + int tries = QEC_RESET_TRIES; + + gregs->ctrl = GLOB_CTRL_RESET; + while(--tries) { + if(gregs->ctrl & GLOB_CTRL_RESET) { + udelay(20); + continue; + } + break; + } + if(tries) + return 0; + printk("BigMAC: Cannot reset the QEC.\n"); + return -1; +} + +static void qec_init(struct bigmac *bp) +{ + struct qe_globreg *gregs = bp->gregs; + struct linux_sbus_device *qec_sdev = bp->qec_sbus_dev; + unsigned char bsizes = bp->bigmac_bursts; + unsigned int regval; + + /* 64byte bursts do not work at the moment, do + * not even try to enable them. -DaveM + */ + if(bsizes & DMA_BURST32) + regval = GLOB_CTRL_B32; + else + regval = GLOB_CTRL_B16; + gregs->ctrl = regval | GLOB_CTRL_BMODE; + + gregs->psize = GLOB_PSIZE_2048; + + /* All of memsize is given to bigmac. */ + gregs->msize = qec_sdev->reg_addrs[1].reg_size; + + /* Half to the transmitter, half to the receiver. */ + gregs->rsize = gregs->tsize = qec_sdev->reg_addrs[1].reg_size >> 1; +} + +/* XXX auto negotiation on these things might not be pleasant... */ + +#define TX_RESET_TRIES 32 +#define RX_RESET_TRIES 32 + +static inline void bigmac_tx_reset(struct BIG_MAC_regs *bregs) +{ + int tries = TX_RESET_TRIES; + + bregs->tx_cfg = 0; + + /* The fifo threshold bit is read-only and does + * not clear. -DaveM + */ + while((bregs->tx_cfg & ~(BIGMAC_TXCFG_FIFO)) != 0 && + --tries != 0) + udelay(20); + + if(!tries) { + printk("BIGMAC: Transmitter will not reset.\n"); + printk("BIGMAC: tx_cfg is %08x\n", bregs->tx_cfg); + } +} + +static inline void bigmac_rx_reset(struct BIG_MAC_regs *bregs) +{ + int tries = RX_RESET_TRIES; + + bregs->rx_cfg = 0; + while((bregs->rx_cfg) && --tries) + udelay(20); + + if(!tries) { + printk("BIGMAC: Receiver will not reset.\n"); + printk("BIGMAC: rx_cfg is %08x\n", bregs->rx_cfg); + } +} + +/* Reset the transmitter and receiver. */ +static void bigmac_stop(struct bigmac *bp) +{ + bigmac_tx_reset(bp->bregs); + bigmac_rx_reset(bp->bregs); +} + +static void bigmac_get_counters(struct bigmac *bp, struct BIG_MAC_regs *bregs) +{ + struct enet_statistics *stats = &bp->enet_stats; + + stats->rx_crc_errors += bregs->rcrce_ctr; + bregs->rcrce_ctr = 0; + + stats->rx_frame_errors += bregs->unale_ctr; + bregs->unale_ctr = 0; + + stats->rx_length_errors += bregs->gle_ctr; + bregs->gle_ctr = 0; + + stats->tx_aborted_errors += bregs->ex_ctr; + + stats->collisions += (bregs->ex_ctr + bregs->lt_ctr); + bregs->ex_ctr = bregs->lt_ctr = 0; +} + +static inline void bigmac_clean_rings(struct bigmac *bp) +{ + int i; + + for(i = 0; i < RX_RING_SIZE; i++) { + if(bp->rx_skbs[i] != NULL) { + dev_kfree_skb(bp->rx_skbs[i]); + bp->rx_skbs[i] = NULL; + } + } + + for(i = 0; i < TX_RING_SIZE; i++) { + if(bp->tx_skbs[i] != NULL) { + dev_kfree_skb(bp->tx_skbs[i]); + bp->tx_skbs[i] = NULL; + } + } +} + +static void bigmac_init_rings(struct bigmac *bp, int from_irq) +{ + struct bmac_init_block *bb = bp->bmac_block; + struct device *dev = bp->dev; + int i, gfp_flags = GFP_KERNEL; + + if(from_irq || in_interrupt()) + gfp_flags = GFP_ATOMIC; + + bp->rx_new = bp->rx_old = bp->tx_new = bp->tx_old = 0; + + /* Free any skippy bufs left around in the rings. */ + bigmac_clean_rings(bp); + + /* Now get new skippy bufs for the receive ring. */ + for(i = 0; i < RX_RING_SIZE; i++) { + struct sk_buff *skb; + + skb = big_mac_alloc_skb(RX_BUF_ALLOC_SIZE, gfp_flags); + if(!skb) + continue; + + bp->rx_skbs[i] = skb; + skb->dev = dev; + + /* Because we reserve afterwards. */ + skb_put(skb, ETH_FRAME_LEN); + skb_reserve(skb, 34); + + bb->be_rxd[i].rx_addr = sbus_dvma_addr(skb->data); + bb->be_rxd[i].rx_flags = + (RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH)); + } + + for(i = 0; i < TX_RING_SIZE; i++) + bb->be_txd[i].tx_flags = bb->be_txd[i].tx_addr = 0; +} + +#ifndef __sparc_v9__ +static void sun4c_bigmac_init_rings(struct bigmac *bp) +{ + struct bmac_init_block *bb = bp->bmac_block; + __u32 bbufs_dvma = bp->s4c_buf_dvma; + int i; + + bp->rx_new = bp->rx_old = bp->tx_new = bp->tx_old = 0; + + for(i = 0; i < RX_RING_SIZE; i++) { + bb->be_rxd[i].rx_addr = bbufs_dvma + bbuf_offset(rx_buf, i); + bb->be_rxd[i].rx_flags = + (RXD_OWN | (SUN4C_RX_BUFF_SIZE & RXD_LENGTH)); + } + + for(i = 0; i < TX_RING_SIZE; i++) + bb->be_txd[i].tx_flags = bb->be_txd[i].tx_addr = 0; +} +#endif + +#define MGMT_CLKON (MGMT_PAL_INT_MDIO|MGMT_PAL_EXT_MDIO|MGMT_PAL_OENAB|MGMT_PAL_DCLOCK) +#define MGMT_CLKOFF (MGMT_PAL_INT_MDIO|MGMT_PAL_EXT_MDIO|MGMT_PAL_OENAB) + +static inline void idle_transceiver(struct bmac_tcvr *tregs) +{ + volatile unsigned int garbage; + int i = 20; + + while(i--) { + tregs->mgmt_pal = MGMT_CLKOFF; + garbage = tregs->mgmt_pal; + tregs->mgmt_pal = MGMT_CLKON; + garbage = tregs->mgmt_pal; + } +} + +static void write_tcvr_bit(struct bigmac *bp, struct bmac_tcvr *tregs, int bit) +{ + volatile unsigned int garbage; + + if(bp->tcvr_type == internal) { + bit = (bit & 1) << 3; + tregs->mgmt_pal = bit | (MGMT_PAL_OENAB | MGMT_PAL_EXT_MDIO); + garbage = tregs->mgmt_pal; + tregs->mgmt_pal = bit | (MGMT_PAL_OENAB | + MGMT_PAL_EXT_MDIO | + MGMT_PAL_DCLOCK); + garbage = tregs->mgmt_pal; + } else if(bp->tcvr_type == external) { + bit = (bit & 1) << 2; + tregs->mgmt_pal = bit | (MGMT_PAL_INT_MDIO | MGMT_PAL_OENAB); + garbage = tregs->mgmt_pal; + tregs->mgmt_pal = bit | (MGMT_PAL_INT_MDIO | + MGMT_PAL_OENAB | + MGMT_PAL_DCLOCK); + garbage = tregs->mgmt_pal; + } else { + printk("write_tcvr_bit: No transceiver type known!\n"); + } +} + +static int read_tcvr_bit(struct bigmac *bp, struct bmac_tcvr *tregs) +{ + volatile unsigned int garbage; + int retval = 0; + + if(bp->tcvr_type == internal) { + tregs->mgmt_pal = MGMT_PAL_EXT_MDIO; + garbage = tregs->mgmt_pal; + tregs->mgmt_pal = MGMT_PAL_EXT_MDIO | MGMT_PAL_DCLOCK; + garbage = tregs->mgmt_pal; + retval = (tregs->mgmt_pal & MGMT_PAL_INT_MDIO) >> 3; + } else if(bp->tcvr_type == external) { + tregs->mgmt_pal = MGMT_PAL_INT_MDIO; + garbage = tregs->mgmt_pal; + tregs->mgmt_pal = MGMT_PAL_INT_MDIO | MGMT_PAL_DCLOCK; + garbage = tregs->mgmt_pal; + retval = (tregs->mgmt_pal & MGMT_PAL_EXT_MDIO) >> 2; + } else { + printk("read_tcvr_bit: No transceiver type known!\n"); + } + return retval; +} + +static int read_tcvr_bit2(struct bigmac *bp, struct bmac_tcvr *tregs) +{ + volatile unsigned int garbage; + int retval = 0; + + if(bp->tcvr_type == internal) { + tregs->mgmt_pal = MGMT_PAL_EXT_MDIO; + garbage = tregs->mgmt_pal; + retval = (tregs->mgmt_pal & MGMT_PAL_INT_MDIO) >> 3; + tregs->mgmt_pal = (MGMT_PAL_EXT_MDIO | MGMT_PAL_DCLOCK); + garbage = tregs->mgmt_pal; + } else if(bp->tcvr_type == external) { + tregs->mgmt_pal = MGMT_PAL_INT_MDIO; + garbage = tregs->mgmt_pal; + retval = (tregs->mgmt_pal & MGMT_PAL_EXT_MDIO) >> 2; + tregs->mgmt_pal = (MGMT_PAL_INT_MDIO | MGMT_PAL_DCLOCK); + garbage = tregs->mgmt_pal; + } else { + printk("read_tcvr_bit2: No transceiver type known!\n"); + } + return retval; +} + +static inline void put_tcvr_byte(struct bigmac *bp, + struct bmac_tcvr *tregs, + unsigned int byte) +{ + int shift = 4; + + do { + write_tcvr_bit(bp, tregs, ((byte >> shift) & 1)); + shift -= 1; + } while (shift >= 0); +} + +static void bigmac_tcvr_write(struct bigmac *bp, struct bmac_tcvr *tregs, + int reg, unsigned short val) +{ + int shift; + + reg &= 0xff; + val &= 0xffff; + switch(bp->tcvr_type) { + case internal: + case external: + break; + + default: + printk("bigmac_tcvr_read: Whoops, no known transceiver type.\n"); + return; + }; + + idle_transceiver(tregs); + write_tcvr_bit(bp, tregs, 0); + write_tcvr_bit(bp, tregs, 1); + write_tcvr_bit(bp, tregs, 0); + write_tcvr_bit(bp, tregs, 1); + + put_tcvr_byte(bp, tregs, + ((bp->tcvr_type == internal) ? + BIGMAC_PHY_INTERNAL : BIGMAC_PHY_EXTERNAL)); + + put_tcvr_byte(bp, tregs, reg); + + write_tcvr_bit(bp, tregs, 1); + write_tcvr_bit(bp, tregs, 0); + + shift = 15; + do { + write_tcvr_bit(bp, tregs, (val >> shift) & 1); + shift -= 1; + } while(shift >= 0); +} + +static unsigned short bigmac_tcvr_read(struct bigmac *bp, + struct bmac_tcvr *tregs, + int reg) +{ + unsigned short retval = 0; + + reg &= 0xff; + switch(bp->tcvr_type) { + case internal: + case external: + break; + + default: + printk("bigmac_tcvr_read: Whoops, no known transceiver type.\n"); + return 0xffff; + }; + + idle_transceiver(tregs); + write_tcvr_bit(bp, tregs, 0); + write_tcvr_bit(bp, tregs, 1); + write_tcvr_bit(bp, tregs, 1); + write_tcvr_bit(bp, tregs, 0); + + put_tcvr_byte(bp, tregs, + ((bp->tcvr_type == internal) ? + BIGMAC_PHY_INTERNAL : BIGMAC_PHY_EXTERNAL)); + + put_tcvr_byte(bp, tregs, reg); + + if(bp->tcvr_type == external) { + int shift = 15; + + (void) read_tcvr_bit2(bp, tregs); + (void) read_tcvr_bit2(bp, tregs); + + do { + int tmp; + + tmp = read_tcvr_bit2(bp, tregs); + retval |= ((tmp & 1) << shift); + shift -= 1; + } while(shift >= 0); + + (void) read_tcvr_bit2(bp, tregs); + (void) read_tcvr_bit2(bp, tregs); + (void) read_tcvr_bit2(bp, tregs); + } else { + int shift = 15; + + (void) read_tcvr_bit(bp, tregs); + (void) read_tcvr_bit(bp, tregs); + + do { + int tmp; + + tmp = read_tcvr_bit(bp, tregs); + retval |= ((tmp & 1) << shift); + shift -= 1; + } while(shift >= 0); + + (void) read_tcvr_bit(bp, tregs); + (void) read_tcvr_bit(bp, tregs); + (void) read_tcvr_bit(bp, tregs); + } + return retval; +} + +static void bigmac_tcvr_init(struct bigmac *bp) +{ + volatile unsigned int garbage; + struct bmac_tcvr *tregs = bp->tregs; + + idle_transceiver(tregs); + tregs->mgmt_pal = (MGMT_PAL_INT_MDIO | + MGMT_PAL_EXT_MDIO | + MGMT_PAL_DCLOCK); + garbage = tregs->mgmt_pal; + + /* Only the bit for the present transceiver (internal or + * external) will stick, set them both and see what stays. + */ + tregs->mgmt_pal = (MGMT_PAL_INT_MDIO | + MGMT_PAL_EXT_MDIO); + garbage = tregs->mgmt_pal; + udelay(20); + + if(tregs->mgmt_pal & MGMT_PAL_EXT_MDIO) { + bp->tcvr_type = external; + tregs->tcvr_pal = ~(TCVR_PAL_EXTLBACK | + TCVR_PAL_MSENSE | + TCVR_PAL_LTENABLE); + garbage = tregs->tcvr_pal; + } else if(tregs->mgmt_pal & MGMT_PAL_INT_MDIO) { + bp->tcvr_type = internal; + tregs->tcvr_pal = ~(TCVR_PAL_SERIAL | + TCVR_PAL_EXTLBACK | + TCVR_PAL_MSENSE | + TCVR_PAL_LTENABLE); + garbage = tregs->tcvr_pal; + } else { + printk("BIGMAC: AIEEE, neither internal nor " + "external MDIO available!\n"); + printk("BIGMAC: mgmt_pal[%08x] tcvr_pal[%08x]\n", + tregs->mgmt_pal, tregs->tcvr_pal); + } +} + +static int bigmac_init(struct bigmac *, int); + +static int try_next_permutation(struct bigmac *bp, struct bmac_tcvr *tregs) +{ + if(bp->sw_bmcr & BMCR_SPEED100) { + int timeout; + + /* Reset the PHY. */ + bp->sw_bmcr = (BMCR_ISOLATE | BMCR_PDOWN | BMCR_LOOPBACK); + bigmac_tcvr_write(bp, tregs, BIGMAC_BMCR, bp->sw_bmcr); + bp->sw_bmcr = (BMCR_RESET); + bigmac_tcvr_write(bp, tregs, BIGMAC_BMCR, bp->sw_bmcr); + + timeout = 64; + while(--timeout) { + bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMCR); + if((bp->sw_bmcr & BMCR_RESET) == 0) + break; + udelay(20); + } + if(timeout == 0) + printk("%s: PHY reset failed.\n", bp->dev->name); + + bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMCR); + + /* Now we try 10baseT. */ + bp->sw_bmcr &= ~(BMCR_SPEED100); + bigmac_tcvr_write(bp, tregs, BIGMAC_BMCR, bp->sw_bmcr); + return 0; + } + + /* We've tried them all. */ + return -1; +} + +static void bigmac_timer(unsigned long data) +{ + struct bigmac *bp = (struct bigmac *) data; + struct bmac_tcvr *tregs = bp->tregs; + int restart_timer = 0; + + bp->timer_ticks++; + if(bp->timer_state == ltrywait) { + bp->sw_bmsr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMSR); + bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMCR); + if(bp->sw_bmsr & BMSR_LSTATUS) { + printk("%s: Link is now up at %s.\n", + bp->dev->name, + (bp->sw_bmcr & BMCR_SPEED100) ? + "100baseT" : "10baseT"); + bp->timer_state = asleep; + restart_timer = 0; + } else { + if(bp->timer_ticks >= 4) { + int ret; + + ret = try_next_permutation(bp, tregs); + if(ret == -1) { + printk("%s: Link down, cable problem?\n", + bp->dev->name); + ret = bigmac_init(bp, 0); + if(ret) { + printk("%s: Error, cannot re-init the " + "BigMAC.\n", bp->dev->name); + } + return; + } + bp->timer_ticks = 0; + restart_timer = 1; + } else { + restart_timer = 1; + } + } + } else { + /* Can't happens.... */ + printk("%s: Aieee, link timer is asleep but we got one anyways!\n", + bp->dev->name); + restart_timer = 0; + bp->timer_ticks = 0; + bp->timer_state = asleep; /* foo on you */ + } + + if(restart_timer != 0) { + bp->bigmac_timer.expires = jiffies + ((12 * HZ)/10); /* 1.2 sec. */ + add_timer(&bp->bigmac_timer); + } +} + +/* Well, really we just force the chip into 100baseT then + * 10baseT, each time checking for a link status. + */ +static void bigmac_begin_auto_negotiation(struct bigmac *bp) +{ + struct bmac_tcvr *tregs = bp->tregs; + int timeout; + + /* Grab new software copies of PHY registers. */ + bp->sw_bmsr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMSR); + bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMCR); + + /* Reset the PHY. */ + bp->sw_bmcr = (BMCR_ISOLATE | BMCR_PDOWN | BMCR_LOOPBACK); + bigmac_tcvr_write(bp, tregs, BIGMAC_BMCR, bp->sw_bmcr); + bp->sw_bmcr = (BMCR_RESET); + bigmac_tcvr_write(bp, tregs, BIGMAC_BMCR, bp->sw_bmcr); + + timeout = 64; + while(--timeout) { + bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMCR); + if((bp->sw_bmcr & BMCR_RESET) == 0) + break; + udelay(20); + } + if(timeout == 0) + printk("%s: PHY reset failed.\n", bp->dev->name); + + bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMCR); + + /* First we try 100baseT. */ + bp->sw_bmcr |= BMCR_SPEED100; + bigmac_tcvr_write(bp, tregs, BIGMAC_BMCR, bp->sw_bmcr); + + bp->timer_state = ltrywait; + bp->timer_ticks = 0; + bp->bigmac_timer.expires = jiffies + (12 * HZ) / 10; + bp->bigmac_timer.data = (unsigned long) bp; + bp->bigmac_timer.function = &bigmac_timer; + add_timer(&bp->bigmac_timer); +} + +static int bigmac_init(struct bigmac *bp, int from_irq) +{ + struct qe_globreg *gregs = bp->gregs; + struct qe_creg *cregs = bp->creg; + struct BIG_MAC_regs *bregs = bp->bregs; + unsigned char *e = &bp->dev->dev_addr[0]; + + /* Latch current counters into statistics. */ + bigmac_get_counters(bp, bregs); + + /* Reset QEC. */ + qec_global_reset(gregs); + + /* Init QEC. */ + qec_init(bp); + + /* Alloc and reset the tx/rx descriptor chains. */ +#ifndef __sparc_v9__ + if(sparc_cpu_model == sun4c) + sun4c_bigmac_init_rings(bp); + else +#endif + bigmac_init_rings(bp, from_irq); + + /* Initialize the PHY. */ + bigmac_tcvr_init(bp); + + /* Stop transmitter and receiver. */ + bigmac_stop(bp); + + /* Set hardware ethernet address. */ + bregs->mac_addr2 = ((e[4] << 8) | e[5]); + bregs->mac_addr1 = ((e[2] << 8) | e[3]); + bregs->mac_addr0 = ((e[0] << 8) | e[1]); + + /* Clear the hash table until mc upload occurs. */ + bregs->htable3 = 0; + bregs->htable2 = 0; + bregs->htable1 = 0; + bregs->htable0 = 0; + + /* Enable Big Mac hash table filter. */ + bregs->rx_cfg = (BIGMAC_RXCFG_HENABLE | BIGMAC_RXCFG_FIFO); + + udelay(20); + + /* Ok, configure the Big Mac transmitter. */ + bregs->tx_cfg = BIGMAC_TXCFG_FIFO; + + /* The HME docs recommend to use the 10LSB of our MAC here. */ + bregs->rand_seed = ((e[5] | e[4] << 8) & 0x3ff); + + /* Enable the output drivers no matter what. */ + bregs->xif_cfg = (BIGMAC_XCFG_ODENABLE | BIGMAC_XCFG_RESV); + + /* Tell the QEC where the ring descriptors are. */ + cregs->rxds = bp->bblock_dvma + bib_offset(be_rxd, 0); + cregs->txds = bp->bblock_dvma + bib_offset(be_txd, 0); + + /* Setup the FIFO pointers into QEC local memory. */ + cregs->rxwbufptr = cregs->rxrbufptr = 0; + cregs->txwbufptr = cregs->txrbufptr = gregs->rsize; + + /* Tell bigmac what interrupts we don't want to hear about. */ + bregs->imask = (BIGMAC_IMASK_GOTFRAME | BIGMAC_IMASK_SENTFRAME); + + /* Enable the various other irq's. */ + cregs->rimask = 0; + cregs->timask = 0; + cregs->qmask = 0; + cregs->bmask = 0; + + /* Set jam size to a reasonable default. */ + bregs->jsize = DEFAULT_JAMSIZE; + + /* Clear collision counter. */ + cregs->ccnt = 0; + + /* Enable transmitter and receiver. */ + bregs->tx_cfg |= BIGMAC_TXCFG_ENABLE; + bregs->rx_cfg |= BIGMAC_RXCFG_ENABLE; + + /* Ok, start detecting link speed/duplex. */ + bigmac_begin_auto_negotiation(bp); + + /* Success. */ + return 0; +} + +/* Error interrupts get sent here. */ +static void bigmac_is_medium_rare(struct bigmac *bp, + unsigned int qec_status, + unsigned int bmac_status) +{ + printk("bigmac_is_medium_rare: "); + if(qec_status & (GLOB_STAT_ER | GLOB_STAT_BM)) { + if(qec_status & GLOB_STAT_ER) + printk("QEC_ERROR, "); + if(qec_status & GLOB_STAT_BM) + printk("QEC_BMAC_ERROR, "); + } + if(bmac_status & CREG_STAT_ERRORS) { + if(bmac_status & CREG_STAT_BERROR) + printk("BMAC_ERROR, "); + if(bmac_status & CREG_STAT_TXDERROR) + printk("TXD_ERROR, "); + if(bmac_status & CREG_STAT_TXLERR) + printk("TX_LATE_ERROR, "); + if(bmac_status & CREG_STAT_TXPERR) + printk("TX_PARITY_ERROR, "); + if(bmac_status & CREG_STAT_TXSERR) + printk("TX_SBUS_ERROR, "); + + if(bmac_status & CREG_STAT_RXDROP) + printk("RX_DROP_ERROR, "); + + if(bmac_status & CREG_STAT_RXSMALL) + printk("RX_SMALL_ERROR, "); + if(bmac_status & CREG_STAT_RXLERR) + printk("RX_LATE_ERROR, "); + if(bmac_status & CREG_STAT_RXPERR) + printk("RX_PARITY_ERROR, "); + if(bmac_status & CREG_STAT_RXSERR) + printk("RX_SBUS_ERROR, "); + } + + printk(" RESET\n"); + bigmac_init(bp, 1); +} + +/* BigMAC transmit complete service routines. */ +static inline void bigmac_tx(struct bigmac *bp) +{ + struct be_txd *txbase = &bp->bmac_block->be_txd[0]; + struct be_txd *this; + int elem = bp->tx_old; + + DTX(("bigmac_tx: tx_old[%d] ", elem)); + while(elem != bp->tx_new) { + struct sk_buff *skb; + + this = &txbase[elem]; + + DTX(("this(%p) [flags(%08x)addr(%08x)]", + this, this->tx_flags, this->tx_addr)); + + if(this->tx_flags & TXD_OWN) + break; + skb = bp->tx_skbs[elem]; + DTX(("skb(%p) ", skb)); + bp->tx_skbs[elem] = NULL; + dev_kfree_skb(skb); + + bp->enet_stats.tx_packets++; + elem = NEXT_TX(elem); + } + DTX((" DONE, tx_old=%d\n", elem)); + bp->tx_old = elem; +} + +#ifndef __sparc_v9__ +static inline void sun4c_bigmac_tx(struct bigmac *bp) +{ + struct be_txd *txbase = &bp->bmac_block->be_txd[0]; + struct be_txd *this; + int elem = bp->tx_old; + + while(elem != bp->tx_new) { + this = &txbase[elem]; + if(this->tx_flags & TXD_OWN) + break; + bp->enet_stats.tx_packets++; + elem = NEXT_TX(elem); + } + bp->tx_old = elem; +} +#endif + +/* BigMAC receive complete service routines. */ +static inline void bigmac_rx(struct bigmac *bp) +{ + struct be_rxd *rxbase = &bp->bmac_block->be_rxd[0]; + struct be_rxd *this; + int elem = bp->rx_new, drops = 0; + + this = &rxbase[elem]; + while(!(this->rx_flags & RXD_OWN)) { + struct sk_buff *skb; + unsigned int flags = this->rx_flags; + int len = (flags & RXD_LENGTH); /* FCS not included */ + + /* Check for errors. */ + if(len < ETH_ZLEN) { + bp->enet_stats.rx_errors++; + bp->enet_stats.rx_length_errors++; + + drop_it: + /* Return it to the BigMAC. */ + bp->enet_stats.rx_dropped++; + this->rx_addr = sbus_dvma_addr(bp->rx_skbs[elem]->data); + this->rx_flags = + (RXD_OWN | (RX_BUF_ALLOC_SIZE & RXD_LENGTH)); + goto next; + } + skb = bp->rx_skbs[elem]; +#ifdef NEED_DMA_SYNCHRONIZATION +#ifdef __sparc_v9__ + if ((unsigned long) (skb->data + skb->len) >= MAX_DMA_ADDRESS) { + printk("sunbmac: Bogus DMA buffer address " + "[%016lx]\n", ((unsigned long) skb->data)); + panic("DMA address too large, tell DaveM"); + } +#endif + mmu_sync_dma(sbus_dvma_addr(skb->data), + skb->len, bp->bigmac_sbus_dev->my_bus); +#endif + if(len > RX_COPY_THRESHOLD) { + struct sk_buff *new_skb; + + /* Now refill the entry, if we can. */ + new_skb = big_mac_alloc_skb(RX_BUF_ALLOC_SIZE, GFP_ATOMIC); + if(!new_skb) { + drops++; + goto drop_it; + } + bp->rx_skbs[elem] = new_skb; + new_skb->dev = bp->dev; + skb_put(new_skb, ETH_FRAME_LEN); + skb_reserve(new_skb, 34); + rxbase[elem].rx_addr = sbus_dvma_addr(new_skb->data); + rxbase[elem].rx_flags = + (RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH)); + + /* Trim the original skb for the netif. */ + skb_trim(skb, len); + } else { + struct sk_buff *copy_skb = dev_alloc_skb(len + 2); + + if(!copy_skb) { + drops++; + goto drop_it; + } + copy_skb->dev = bp->dev; + skb_reserve(copy_skb, 2); + skb_put(copy_skb, len); + eth_copy_and_sum(copy_skb, (unsigned char *)skb->data, len, 0); + + /* Reuse otiginal ring buffer. */ + rxbase[elem].rx_addr = sbus_dvma_addr(skb->data); + rxbase[elem].rx_flags = + (RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH)); + + skb = copy_skb; + } + + /* No checksums done by the BigMAC ;-( */ + skb->protocol = eth_type_trans(skb, bp->dev); + netif_rx(skb); + bp->enet_stats.rx_packets++; + next: + elem = NEXT_RX(elem); + this = &rxbase[elem]; + } + bp->rx_new = elem; + if(drops) + printk("%s: Memory squeeze, deferring packet.\n", bp->dev->name); +} + +#ifndef __sparc_v9__ +static inline void sun4c_bigmac_rx(struct bigmac *bp) +{ + struct be_rxd *rxbase = &bp->bmac_block->be_rxd[0]; + struct be_rxd *this; + struct bigmac_buffers *bbufs = bp->sun4c_buffers; + __u32 bbufs_dvma = bp->s4c_buf_dvma; + int elem = bp->rx_new, drops = 0; + + this = &rxbase[elem]; + while(!(this->rx_flags & RXD_OWN)) { + struct sk_buff *skb; + unsigned char *this_bbuf = + bbufs->rx_buf[elem & (SUN4C_RX_RING_SIZE - 1)]; + __u32 this_bbuf_dvma = bbufs_dvma + + bbuf_offset(rx_buf, (elem & (SUN4C_RX_RING_SIZE - 1))); + struct be_rxd *end_rxd = + &rxbase[(elem+SUN4C_RX_RING_SIZE)&(RX_RING_SIZE-1)]; + unsigned int flags = this->rx_flags; + int len = (flags & RXD_LENGTH) - 4; /* FCS not included */ + + /* Check for errors. */ + if(len < ETH_ZLEN) { + bp->enet_stats.rx_errors++; + bp->enet_stats.rx_length_errors++; + bp->enet_stats.rx_dropped++; + } else { + skb = dev_alloc_skb(len + 2); + if(skb == 0) { + drops++; + bp->enet_stats.rx_dropped++; + } else { + skb->dev = bp->dev; + skb_reserve(skb, 2); + skb_put(skb, len); + eth_copy_and_sum(skb, (unsigned char *)this_bbuf, + len, 0); + skb->protocol = eth_type_trans(skb, bp->dev); + netif_rx(skb); + bp->enet_stats.rx_packets++; + } + } + end_rxd->rx_addr = this_bbuf_dvma; + end_rxd->rx_flags = (RXD_OWN | (SUN4C_RX_BUFF_SIZE & RXD_LENGTH)); + + elem = NEXT_RX(elem); + this = &rxbase[elem]; + } + bp->rx_new = elem; + if(drops) + printk("%s: Memory squeeze, deferring packet.\n", bp->dev->name); +} +#endif + +static void bigmac_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct bigmac *bp = (struct bigmac *) dev_id; + unsigned int qec_status, bmac_status; + + DIRQ(("bigmac_interrupt: ")); + + /* Latch status registers now. */ + bmac_status = bp->creg->stat; + qec_status = bp->gregs->stat; + + bp->dev->interrupt = 1; + + DIRQ(("qec_status=%08x bmac_status=%08x\n", qec_status, bmac_status)); + if((qec_status & (GLOB_STAT_ER | GLOB_STAT_BM)) || + (bmac_status & CREG_STAT_ERRORS)) + bigmac_is_medium_rare(bp, qec_status, bmac_status); + + if(bmac_status & CREG_STAT_TXIRQ) + bigmac_tx(bp); + + if(bmac_status & CREG_STAT_RXIRQ) + bigmac_rx(bp); + + if(bp->dev->tbusy && (TX_BUFFS_AVAIL(bp) >= 0)) { + bp->dev->tbusy = 0; + mark_bh(NET_BH); + } + + bp->dev->interrupt = 0; +} + +#ifndef __sparc_v9__ +static void sun4c_bigmac_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct bigmac *bp = (struct bigmac *) dev_id; + unsigned int qec_status, bmac_status; + + /* Latch status registers now. */ + bmac_status = bp->creg->stat; + qec_status = bp->gregs->stat; + + bp->dev->interrupt = 1; + + if(qec_status & (GLOB_STAT_ER | GLOB_STAT_BM) || + (bmac_status & CREG_STAT_ERRORS)) + bigmac_is_medium_rare(bp, qec_status, bmac_status); + + if(bmac_status & CREG_STAT_TXIRQ) + sun4c_bigmac_tx(bp); + + if(bmac_status & CREG_STAT_RXIRQ) + sun4c_bigmac_rx(bp); + + if(bp->dev->tbusy && (SUN4C_TX_BUFFS_AVAIL(bp) >= 0)) { + bp->dev->tbusy = 0; + mark_bh(NET_BH); + } + + bp->dev->interrupt = 0; +} +#endif + +static int bigmac_open(struct device *dev) +{ + struct bigmac *bp = (struct bigmac *) dev->priv; + int res; + +#ifndef __sparc_v9__ + if(sparc_cpu_model == sun4c) { + if(request_irq(dev->irq, &sun4c_bigmac_interrupt, + SA_SHIRQ, "BIG MAC", (void *) bp)) { + printk("BIGMAC: Can't order irq %d to go.\n", dev->irq); + return -EAGAIN; + } + } else +#endif + if(request_irq(dev->irq, &bigmac_interrupt, + SA_SHIRQ, "BIG MAC", (void *) bp)) { + printk("BIGMAC: Can't order irq %d to go.\n", dev->irq); + return -EAGAIN; + } + init_timer(&bp->bigmac_timer); + res = bigmac_init(bp, 0); + if(!res) { + MOD_INC_USE_COUNT; + } + return res; +} + +static int bigmac_close(struct device *dev) +{ + struct bigmac *bp = (struct bigmac *) dev->priv; + + del_timer(&bp->bigmac_timer); + bp->timer_state = asleep; + bp->timer_ticks = 0; + + bigmac_stop(bp); + bigmac_clean_rings(bp); + free_irq(dev->irq, (void *)bp); + MOD_DEC_USE_COUNT; + return 0; +} + +/* Put a packet on the wire. */ +static int bigmac_start_xmit(struct sk_buff *skb, struct device *dev) +{ + struct bigmac *bp = (struct bigmac *) dev->priv; + int len, entry; + + if(dev->tbusy) { + int tickssofar = jiffies - dev->trans_start; + + if (tickssofar < 40) { + return 1; + } else { + printk ("%s: transmit timed out, resetting\n", dev->name); + bp->enet_stats.tx_errors++; + bigmac_init(bp, 0); + dev->tbusy = 0; + dev->trans_start = jiffies; + dev_kfree_skb(skb); + return 0; + } + } + + if(test_and_set_bit(0, (void *) &dev->tbusy) != 0) { + printk("%s: Transmitter access conflict.\n", dev->name); + return 1; + } + + if(!TX_BUFFS_AVAIL(bp)) + return 1; + +#ifdef NEED_DMA_SYNCHRONIZATION +#ifdef __sparc_v9__ + if ((unsigned long) (skb->data + skb->len) >= MAX_DMA_ADDRESS) { + struct sk_buff *new_skb = skb_copy(skb, GFP_DMA | GFP_ATOMIC); + if(!new_skb) + return 1; + dev_kfree_skb(skb); + skb = new_skb; + } +#endif + mmu_sync_dma(sbus_dvma_addr(skb->data), + skb->len, bp->bigmac_sbus_dev->my_bus); +#endif + len = skb->len; + entry = bp->tx_new; + DTX(("bigmac_start_xmit: len(%d) entry(%d)\n", len, entry)); + + /* Avoid a race... */ + bp->bmac_block->be_txd[entry].tx_flags = TXD_UPDATE; + + bp->tx_skbs[entry] = skb; + bp->bmac_block->be_txd[entry].tx_addr = sbus_dvma_addr(skb->data); + bp->bmac_block->be_txd[entry].tx_flags = + (TXD_OWN | TXD_SOP | TXD_EOP | (len & TXD_LENGTH)); + dev->trans_start = jiffies; + bp->tx_new = NEXT_TX(entry); + + /* Get it going. */ + bp->creg->ctrl = CREG_CTRL_TWAKEUP; + + if(TX_BUFFS_AVAIL(bp)) + dev->tbusy = 0; + + return 0; +} + +#ifndef __sparc_v9__ +static int sun4c_bigmac_start_xmit(struct sk_buff *skb, struct device *dev) +{ + struct bigmac *bp = (struct bigmac *) dev->priv; + struct bigmac_buffers *bbufs = bp->sun4c_buffers; + __u32 txbuf_dvma, bbufs_dvma = bp->s4c_buf_dvma; + unsigned char *txbuf; + int len, entry; + + if(dev->tbusy) { + int tickssofar = jiffies - dev->trans_start; + + if (tickssofar < 40) { + return 1; + } else { + printk ("%s: transmit timed out, resetting\n", dev->name); + bp->enet_stats.tx_errors++; + bigmac_init(bp, 0); + dev->tbusy = 0; + dev->trans_start = jiffies; + return 0; + } + } + + if(test_and_set_bit(0, (void *) &dev->tbusy) != 0) { + printk("%s: Transmitter access conflict.\n", dev->name); + return 1; + } + + if(!SUN4C_TX_BUFFS_AVAIL(bp)) + return 1; + + len = skb->len; + entry = bp->tx_new; + + txbuf = &bbufs->tx_buf[entry][0]; + txbuf_dvma = bbufs_dvma + bbuf_offset(tx_buf, entry); + memcpy(txbuf, skb->data, len); + + /* Avoid a race... */ + bp->bmac_block->be_txd[entry].tx_flags = TXD_UPDATE; + + bp->bmac_block->be_txd[entry].tx_addr = txbuf_dvma; + bp->bmac_block->be_txd[entry].tx_flags = + (TXD_OWN | TXD_SOP | TXD_EOP | (len & TXD_LENGTH)); + bp->tx_new = NEXT_TX(entry); + + /* Get it going. */ + dev->trans_start = jiffies; + bp->creg->ctrl = CREG_CTRL_TWAKEUP; + + dev_kfree_skb(skb); + + if(SUN4C_TX_BUFFS_AVAIL(bp)) + dev->tbusy = 0; + + return 0; +} +#endif + +static struct enet_statistics *bigmac_get_stats(struct device *dev) +{ + struct bigmac *bp = (struct bigmac *) dev->priv; + + bigmac_get_counters(bp, bp->bregs); + return &bp->enet_stats; +} + +#define CRC_POLYNOMIAL_BE 0x04c11db7UL /* Ethernet CRC, big endian */ +#define CRC_POLYNOMIAL_LE 0xedb88320UL /* Ethernet CRC, little endian */ + +static void bigmac_set_multicast(struct device *dev) +{ + struct bigmac *bp = (struct bigmac *) dev->priv; + struct BIG_MAC_regs *bregs = bp->bregs; + struct dev_mc_list *dmi = dev->mc_list; + char *addrs; + int i, j, bit, byte; + u32 crc, poly = CRC_POLYNOMIAL_LE; + + /* Disable the receiver. The bit self-clears when + * the operation is complete. + */ + bregs->rx_cfg &= ~(BIGMAC_RXCFG_ENABLE); + while((bregs->rx_cfg & BIGMAC_RXCFG_ENABLE) != 0) + udelay(20); + + if((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 64)) { + bregs->htable0 = 0xffff; + bregs->htable1 = 0xffff; + bregs->htable2 = 0xffff; + bregs->htable3 = 0xffff; + } else if(dev->flags & IFF_PROMISC) { + bregs->rx_cfg |= BIGMAC_RXCFG_PMISC; + } else { + u16 hash_table[4]; + + for(i = 0; i < 4; i++) + hash_table[i] = 0; + + for(i = 0; i < dev->mc_count; i++) { + addrs = dmi->dmi_addr; + dmi = dmi->next; + + if(!(*addrs & 1)) + continue; + + crc = 0xffffffffU; + for(byte = 0; byte < 6; byte++) { + for(bit = *addrs++, j = 0; j < 8; j++, bit >>= 1) { + int test; + + test = ((bit ^ crc) & 0x01); + crc >>= 1; + if(test) + crc = crc ^ poly; + } + } + crc >>= 26; + hash_table[crc >> 4] |= 1 << (crc & 0xf); + } + bregs->htable0 = hash_table[0]; + bregs->htable1 = hash_table[1]; + bregs->htable2 = hash_table[2]; + bregs->htable3 = hash_table[3]; + } + + /* Re-enable the receiver. */ + bregs->rx_cfg |= BIGMAC_RXCFG_ENABLE; +} + +__initfunc(static int bigmac_ether_init(struct device *dev, struct linux_sbus_device *qec_sdev)) +{ + static unsigned version_printed = 0; + struct bigmac *bp = 0; + unsigned char bsizes, bsizes_more; + int i, j, num_qranges, res = ENOMEM; + struct linux_prom_ranges qranges[8]; + + /* Get a new device struct for this interface. */ + dev = init_etherdev(0, sizeof(struct bigmac)); + + if(version_printed++ == 0) + printk(version); + + /* Report what we have found to the user. */ + printk("%s: BigMAC 100baseT Ethernet ", dev->name); + dev->base_addr = (long) qec_sdev; + for(i = 0; i < 6; i++) + printk("%2.2x%c", dev->dev_addr[i] = idprom->id_ethaddr[i], + i == 5 ? ' ' : ':'); + printk("\n"); + + /* Setup softc, with backpointers to QEC and BigMAC SBUS device structs. */ + bp = (struct bigmac *) dev->priv; + bp->qec_sbus_dev = qec_sdev; + bp->bigmac_sbus_dev = qec_sdev->child; + + /* All further failures we find return this. */ + res = ENODEV; + + /* Verify the registers we expect, are actually there. */ + if((bp->bigmac_sbus_dev->num_registers != 3) || + (bp->qec_sbus_dev->num_registers != 2)) { + printk("BIGMAC: Device does not have 2 and 3 regs, it has %d and %d.\n", + bp->qec_sbus_dev->num_registers, + bp->bigmac_sbus_dev->num_registers); + printk("BIGMAC: Would you like that for here or to go?\n"); + goto fail_and_cleanup; + } + + /* Fun with QEC ranges... */ + if(bp->bigmac_sbus_dev->ranges_applied == 0) { + i = prom_getproperty(bp->qec_sbus_dev->prom_node, "ranges", + (char *)&qranges[0], sizeof(qranges)); + num_qranges = (i / sizeof(struct linux_prom_ranges)); + + /* Now, apply all the ranges for the BigMAC. */ + for(j = 0; j < bp->bigmac_sbus_dev->num_registers; j++) { + int k; + + for(k = 0; k < num_qranges; k++) + if(bp->bigmac_sbus_dev->reg_addrs[j].which_io == + qranges[k].ot_child_space) + break; + if(k >= num_qranges) { + printk("BigMAC: Aieee, bogus QEC range for space %08x\n", + bp->bigmac_sbus_dev->reg_addrs[j].which_io); + goto fail_and_cleanup; + } + bp->bigmac_sbus_dev->reg_addrs[j].which_io = qranges[k].ot_parent_space; + bp->bigmac_sbus_dev->reg_addrs[j].phys_addr += qranges[k].ot_parent_base; + } + + /* Next, apply SBUS ranges on top of what we just changed. */ + prom_apply_sbus_ranges(bp->bigmac_sbus_dev->my_bus, + &bp->bigmac_sbus_dev->reg_addrs[0], + bp->bigmac_sbus_dev->num_registers, + bp->bigmac_sbus_dev); + } + + /* Apply SBUS ranges for the QEC parent. */ + prom_apply_sbus_ranges(bp->qec_sbus_dev->my_bus, + &bp->qec_sbus_dev->reg_addrs[0], + bp->qec_sbus_dev->num_registers, + bp->qec_sbus_dev); + + /* Map in QEC global control registers. */ + bp->gregs = sparc_alloc_io(bp->qec_sbus_dev->reg_addrs[0].phys_addr, + 0, + sizeof(struct qe_globreg), + "BigMAC QEC Global Regs", + bp->qec_sbus_dev->reg_addrs[0].which_io, + 0); + if(!bp->gregs) { + printk("BIGMAC: Cannot map QEC global registers.\n"); + goto fail_and_cleanup; + } + + /* Make sure QEC is in BigMAC mode. */ + if((bp->gregs->ctrl & 0xf0000000) != GLOB_CTRL_BMODE) { + printk("BigMAC: AIEEE, QEC is not in BigMAC mode!\n"); + goto fail_and_cleanup; + } + + /* Reset the QEC. */ + if(qec_global_reset(bp->gregs)) + goto fail_and_cleanup; + + /* Get supported SBUS burst sizes. */ + bsizes = prom_getintdefault(bp->qec_sbus_dev->prom_node, + "burst-sizes", + 0xff); + + bsizes_more = prom_getintdefault(bp->qec_sbus_dev->my_bus->prom_node, + "burst-sizes", + 0xff); + + bsizes &= 0xff; + if(bsizes_more != 0xff) + bsizes &= bsizes_more; + if(bsizes == 0xff || (bsizes & DMA_BURST16) == 0 || + (bsizes & DMA_BURST32) == 0) + bsizes = (DMA_BURST32 - 1); + bp->bigmac_bursts = bsizes; + + /* Perform QEC initialization. */ + qec_init(bp); + + /* Map in the BigMAC channel registers. */ + bp->creg = sparc_alloc_io(bp->bigmac_sbus_dev->reg_addrs[0].phys_addr, + 0, + sizeof(struct qe_creg), + "BigMAC QEC Channel Regs", + bp->bigmac_sbus_dev->reg_addrs[0].which_io, + 0); + if(!bp->creg) { + printk("BIGMAC: Cannot map QEC channel registers.\n"); + goto fail_and_cleanup; + } + + /* Map in the BigMAC control registers. */ + bp->bregs = sparc_alloc_io(bp->bigmac_sbus_dev->reg_addrs[1].phys_addr, + 0, + sizeof(struct BIG_MAC_regs), + "BigMAC Primary Regs", + bp->bigmac_sbus_dev->reg_addrs[1].which_io, + 0); + if(!bp->bregs) { + printk("BIGMAC: Cannot map BigMAC primary registers.\n"); + goto fail_and_cleanup; + } + + /* Map in the BigMAC transceiver registers, this is how you poke at + * the BigMAC's PHY. + */ + bp->tregs = sparc_alloc_io(bp->bigmac_sbus_dev->reg_addrs[2].phys_addr, + 0, + sizeof(struct bmac_tcvr), + "BigMAC Transceiver Regs", + bp->bigmac_sbus_dev->reg_addrs[2].which_io, + 0); + if(!bp->tregs) { + printk("BIGMAC: Cannot map BigMAC transceiver registers.\n"); + goto fail_and_cleanup; + } + + /* Stop the BigMAC. */ + bigmac_stop(bp); + + /* Allocate transmit/receive descriptor DVMA block. */ + bp->bmac_block = (struct bmac_init_block *) + sparc_dvma_malloc(PAGE_SIZE, "BigMAC Init Block", + &bp->bblock_dvma); + + /* Get the board revision of this BigMAC. */ + bp->board_rev = prom_getintdefault(bp->bigmac_sbus_dev->prom_node, + "board-version", 1); + + /* If on sun4c, we use a static buffer pool, on sun4m we DMA directly + * in and out of sk_buffs instead for speed and one copy to userspace. + */ +#ifndef __sparc_v9__ + if(sparc_cpu_model == sun4c) + bp->sun4c_buffers = (struct bigmac_buffers *) + sparc_dvma_malloc(sizeof(struct bigmac_buffers), + "BigMAC Bufs", + &bp->s4c_buf_dvma); + else +#endif + bp->sun4c_buffers = 0; + + /* Init auto-negotiation timer state. */ + init_timer(&bp->bigmac_timer); + bp->timer_state = asleep; + bp->timer_ticks = 0; + + /* Backlink to generic net device struct. */ + bp->dev = dev; + + /* Set links to our BigMAC open and close routines. */ + dev->open = &bigmac_open; + dev->stop = &bigmac_close; + + /* Choose transmit routine based upon buffering scheme. */ +#ifndef __sparc_v9__ + if(sparc_cpu_model == sun4c) + dev->hard_start_xmit = &sun4c_bigmac_start_xmit; + else +#endif + dev->hard_start_xmit = &bigmac_start_xmit; + + /* Set links to BigMAC statistic and multi-cast loading code. */ + dev->get_stats = &bigmac_get_stats; + dev->set_multicast_list = &bigmac_set_multicast; + + /* Finish net device registration. */ + dev->irq = bp->bigmac_sbus_dev->irqs[0]; + dev->dma = 0; + ether_setup(dev); + +#ifdef MODULE + /* Put us into the list of instances attached for later module unloading. */ + bp->next_module = root_bigmac_dev; + root_bigmac_dev = bp; +#endif + return 0; + +fail_and_cleanup: + /* Something went wrong, undo whatever we did so far. */ + if(bp) { + /* Free register mappings if any. */ + if(bp->gregs) + sparc_free_io(bp->gregs, sizeof(struct qe_globreg)); + if(bp->creg) + sparc_free_io(bp->creg, sizeof(struct qe_creg)); + if(bp->bregs) + sparc_free_io(bp->bregs, sizeof(struct BIG_MAC_regs)); + if(bp->tregs) + sparc_free_io(bp->tregs, sizeof(struct bmac_tcvr)); + + /* XXX todo, bmac_block and sun4c_buffers */ + + /* Free the BigMAC softc. */ + kfree(bp); + dev->priv = 0; + } + return res; /* Return error code. */ +} + +__initfunc(int bigmac_probe(struct device *dev)) +{ + struct linux_sbus *bus; + struct linux_sbus_device *sdev = 0; + static int called = 0; + int cards = 0, v; + + if(called) + return ENODEV; + called++; + + for_each_sbus(bus) { + for_each_sbusdev(sdev, bus) { + if(cards) dev = NULL; + + /* QEC can be the parent of either QuadEthernet or + * a BigMAC. We want the latter. + */ + if(!strcmp(sdev->prom_name, "qec") && sdev->child && + !strcmp(sdev->child->prom_name, "be")) { + cards++; + if((v = bigmac_ether_init(dev, sdev))) + return v; + } + } + } + if(!cards) + return ENODEV; + return 0; +} + +#ifdef MODULE + +int +init_module(void) +{ + root_bigmac_dev = NULL; + return bigmac_probe(NULL); +} + +void +cleanup_module(void) +{ + /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ + while (root_bigmac_dev) { + struct bigmac *bp = root_bigmac_dev; + struct bigmac *bp_nxt = root_bigmac_dev->next_module; + + sparc_free_io(bp->gregs, sizeof(struct qe_globreg)); + sparc_free_io(bp->creg, sizeof(struct qe_creg)); + sparc_free_io(bp->bregs, sizeof(struct BIG_MAC_regs)); + sparc_free_io(bp->tregs, sizeof(struct bmac_tcvr)); + + /* XXX todo, bmac_block and sun4c_buffers */ + + unregister_netdev(bp->dev); + kfree(bp->dev); + root_bigmac_dev = bp_nxt; + } +} + +#endif /* MODULE */ diff -u --recursive --new-file v2.2.3/linux/drivers/net/sunbmac.h linux/drivers/net/sunbmac.h --- v2.2.3/linux/drivers/net/sunbmac.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/sunbmac.h Mon Mar 15 16:11:30 1999 @@ -0,0 +1,381 @@ +/* sunbmac.h: Defines for the Sun "Big MAC" 100baseT ethernet cards. + * + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + */ + +#ifndef _SUNBMAC_H +#define _SUNBMAC_H + +/* QEC global registers. */ +struct qe_globreg { + volatile unsigned int ctrl; /* Control */ + volatile unsigned int stat; /* Status */ + volatile unsigned int psize; /* Packet Size */ + volatile unsigned int msize; /* Local-mem size (64K) */ + volatile unsigned int rsize; /* Receive partition size */ + volatile unsigned int tsize; /* Transmit partition size */ +}; + +#define GLOB_CTRL_MMODE 0x40000000 /* MACE qec mode */ +#define GLOB_CTRL_BMODE 0x10000000 /* BigMAC qec mode */ +#define GLOB_CTRL_EPAR 0x00000020 /* Enable parity */ +#define GLOB_CTRL_ACNTRL 0x00000018 /* SBUS arbitration control */ +#define GLOB_CTRL_B64 0x00000004 /* 64 byte dvma bursts */ +#define GLOB_CTRL_B32 0x00000002 /* 32 byte dvma bursts */ +#define GLOB_CTRL_B16 0x00000000 /* 16 byte dvma bursts */ +#define GLOB_CTRL_RESET 0x00000001 /* Reset the QEC */ + +#define GLOB_STAT_TX 0x00000008 /* BigMAC Transmit IRQ */ +#define GLOB_STAT_RX 0x00000004 /* BigMAC Receive IRQ */ +#define GLOB_STAT_BM 0x00000002 /* BigMAC Global IRQ */ +#define GLOB_STAT_ER 0x00000001 /* BigMAC Error IRQ */ + +#define GLOB_PSIZE_2048 0x00 /* 2k packet size */ +#define GLOB_PSIZE_4096 0x01 /* 4k packet size */ +#define GLOB_PSIZE_6144 0x10 /* 6k packet size */ +#define GLOB_PSIZE_8192 0x11 /* 8k packet size */ + +/* QEC BigMAC channel registers. */ +struct qe_creg { + volatile unsigned int ctrl; /* Control */ + volatile unsigned int stat; /* Status */ + volatile unsigned int rxds; /* RX descriptor ring ptr */ + volatile unsigned int txds; /* TX descriptor ring ptr */ + volatile unsigned int rimask; /* RX Interrupt Mask */ + volatile unsigned int timask; /* TX Interrupt Mask */ + volatile unsigned int qmask; /* QEC Error Interrupt Mask */ + volatile unsigned int bmask; /* BigMAC Error Interrupt Mask */ + volatile unsigned int rxwbufptr; /* Local memory rx write ptr */ + volatile unsigned int rxrbufptr; /* Local memory rx read ptr */ + volatile unsigned int txwbufptr; /* Local memory tx write ptr */ + volatile unsigned int txrbufptr; /* Local memory tx read ptr */ + volatile unsigned int ccnt; /* Collision Counter */ +}; + +#define CREG_CTRL_TWAKEUP 0x00000001 /* Transmitter Wakeup, 'go'. */ + +#define CREG_STAT_BERROR 0x80000000 /* BigMAC error */ +#define CREG_STAT_TXIRQ 0x00200000 /* Transmit Interrupt */ +#define CREG_STAT_TXDERROR 0x00080000 /* TX Descriptor is bogus */ +#define CREG_STAT_TXLERR 0x00040000 /* Late Transmit Error */ +#define CREG_STAT_TXPERR 0x00020000 /* Transmit Parity Error */ +#define CREG_STAT_TXSERR 0x00010000 /* Transmit SBUS error ack */ +#define CREG_STAT_RXIRQ 0x00000020 /* Receive Interrupt */ +#define CREG_STAT_RXDROP 0x00000010 /* Dropped a RX'd packet */ +#define CREG_STAT_RXSMALL 0x00000008 /* Receive buffer too small */ +#define CREG_STAT_RXLERR 0x00000004 /* Receive Late Error */ +#define CREG_STAT_RXPERR 0x00000002 /* Receive Parity Error */ +#define CREG_STAT_RXSERR 0x00000001 /* Receive SBUS Error ACK */ + +#define CREG_STAT_ERRORS (CREG_STAT_BERROR|CREG_STAT_TXDERROR|CREG_STAT_TXLERR| \ + CREG_STAT_TXPERR|CREG_STAT_TXSERR|CREG_STAT_RXDROP| \ + CREG_STAT_RXSMALL|CREG_STAT_RXLERR|CREG_STAT_RXPERR| \ + CREG_STAT_RXSERR) + +#define CREG_QMASK_TXDERROR 0x00080000 /* TXD error */ +#define CREG_QMASK_TXLERR 0x00040000 /* TX late error */ +#define CREG_QMASK_TXPERR 0x00020000 /* TX parity error */ +#define CREG_QMASK_TXSERR 0x00010000 /* TX sbus error ack */ +#define CREG_QMASK_RXDROP 0x00000010 /* RX drop */ +#define CREG_QMASK_RXBERROR 0x00000008 /* RX buffer error */ +#define CREG_QMASK_RXLEERR 0x00000004 /* RX late error */ +#define CREG_QMASK_RXPERR 0x00000002 /* RX parity error */ +#define CREG_QMASK_RXSERR 0x00000001 /* RX sbus error ack */ + +struct BIG_MAC_regs { + volatile unsigned int xif_cfg; /* XIF config register */ + volatile unsigned int _unused[63]; /* Reserved... */ + volatile unsigned int status; /* Status register, clear on read */ + volatile unsigned int imask; /* Interrupt mask register */ + volatile unsigned int _unused2[64]; /* Reserved... */ + volatile unsigned int tx_swreset; /* Transmitter software reset */ + volatile unsigned int tx_cfg; /* Transmitter config register */ + volatile unsigned int ipkt_gap1; /* Inter-packet gap 1 */ + volatile unsigned int ipkt_gap2; /* Inter-packet gap 2 */ + volatile unsigned int attempt_limit; /* Transmit attempt limit */ + volatile unsigned int stime; /* Transmit slot time */ + volatile unsigned int preamble_len; /* Size of transmit preamble */ + volatile unsigned int preamble_pattern; /* Pattern for transmit preamble */ + volatile unsigned int tx_sframe_delim; /* Transmit delimiter */ + volatile unsigned int jsize; /* Toe jam... */ + volatile unsigned int tx_pkt_max; /* Transmit max pkt size */ + volatile unsigned int tx_pkt_min; /* Transmit min pkt size */ + volatile unsigned int peak_attempt; /* Count of transmit peak attempts */ + volatile unsigned int dt_ctr; /* Transmit defer timer */ + volatile unsigned int nc_ctr; /* Transmit normal-collision counter */ + volatile unsigned int fc_ctr; /* Transmit first-collision counter */ + volatile unsigned int ex_ctr; /* Transmit excess-collision counter */ + volatile unsigned int lt_ctr; /* Transmit late-collision counter */ + volatile unsigned int rand_seed; /* Transmit random number seed */ + volatile unsigned int tx_smachine; /* Transmit state machine */ + volatile unsigned int _unused3[44]; /* Reserved */ + volatile unsigned int rx_swreset; /* Receiver software reset */ + volatile unsigned int rx_cfg; /* Receiver config register */ + volatile unsigned int rx_pkt_max; /* Receive max pkt size */ + volatile unsigned int rx_pkt_min; /* Receive min pkt size */ + volatile unsigned int mac_addr2; /* Ether address register 2 */ + volatile unsigned int mac_addr1; /* Ether address register 1 */ + volatile unsigned int mac_addr0; /* Ether address register 0 */ + volatile unsigned int fr_ctr; /* Receive frame receive counter */ + volatile unsigned int gle_ctr; /* Receive giant-length error counter */ + volatile unsigned int unale_ctr; /* Receive unaligned error counter */ + volatile unsigned int rcrce_ctr; /* Receive CRC error counter */ + volatile unsigned int rx_smachine; /* Receiver state machine */ + volatile unsigned int rx_cvalid; /* Receiver code violation */ + volatile unsigned int _unused4; /* Reserved... */ + volatile unsigned int htable3; /* Hash table 3 */ + volatile unsigned int htable2; /* Hash table 2 */ + volatile unsigned int htable1; /* Hash table 1 */ + volatile unsigned int htable0; /* Hash table 0 */ + volatile unsigned int afilter2; /* Address filter 2 */ + volatile unsigned int afilter1; /* Address filter 1 */ + volatile unsigned int afilter0; /* Address filter 0 */ + volatile unsigned int afilter_mask; /* Address filter mask */ +}; + +/* BigMac XIF config register. */ +#define BIGMAC_XCFG_ODENABLE 0x00000001 /* Output driver enable */ +#define BIGMAC_XCFG_RESV 0x00000002 /* Reserved, write always as 1 */ +#define BIGMAC_XCFG_MLBACK 0x00000004 /* Loopback-mode MII enable */ +#define BIGMAC_XCFG_SMODE 0x00000008 /* Enable serial mode */ + +/* BigMAC status register. */ +#define BIGMAC_STAT_GOTFRAME 0x00000001 /* Received a frame */ +#define BIGMAC_STAT_RCNTEXP 0x00000002 /* Receive frame counter expired */ +#define BIGMAC_STAT_ACNTEXP 0x00000004 /* Align-error counter expired */ +#define BIGMAC_STAT_CCNTEXP 0x00000008 /* CRC-error counter expired */ +#define BIGMAC_STAT_LCNTEXP 0x00000010 /* Length-error counter expired */ +#define BIGMAC_STAT_RFIFOVF 0x00000020 /* Receive FIFO overflow */ +#define BIGMAC_STAT_CVCNTEXP 0x00000040 /* Code-violation counter expired */ +#define BIGMAC_STAT_SENTFRAME 0x00000100 /* Transmitted a frame */ +#define BIGMAC_STAT_TFIFO_UND 0x00000200 /* Transmit FIFO underrun */ +#define BIGMAC_STAT_MAXPKTERR 0x00000400 /* Max-packet size error */ +#define BIGMAC_STAT_NCNTEXP 0x00000800 /* Normal-collision counter expired */ +#define BIGMAC_STAT_ECNTEXP 0x00001000 /* Excess-collision counter expired */ +#define BIGMAC_STAT_LCCNTEXP 0x00002000 /* Late-collision counter expired */ +#define BIGMAC_STAT_FCNTEXP 0x00004000 /* First-collision counter expired */ +#define BIGMAC_STAT_DTIMEXP 0x00008000 /* Defer-timer expired */ + +/* BigMAC interrupt mask register. */ +#define BIGMAC_IMASK_GOTFRAME 0x00000001 /* Received a frame */ +#define BIGMAC_IMASK_RCNTEXP 0x00000002 /* Receive frame counter expired */ +#define BIGMAC_IMASK_ACNTEXP 0x00000004 /* Align-error counter expired */ +#define BIGMAC_IMASK_CCNTEXP 0x00000008 /* CRC-error counter expired */ +#define BIGMAC_IMASK_LCNTEXP 0x00000010 /* Length-error counter expired */ +#define BIGMAC_IMASK_RFIFOVF 0x00000020 /* Receive FIFO overflow */ +#define BIGMAC_IMASK_CVCNTEXP 0x00000040 /* Code-violation counter expired */ +#define BIGMAC_IMASK_SENTFRAME 0x00000100 /* Transmitted a frame */ +#define BIGMAC_IMASK_TFIFO_UND 0x00000200 /* Transmit FIFO underrun */ +#define BIGMAC_IMASK_MAXPKTERR 0x00000400 /* Max-packet size error */ +#define BIGMAC_IMASK_NCNTEXP 0x00000800 /* Normal-collision counter expired */ +#define BIGMAC_IMASK_ECNTEXP 0x00001000 /* Excess-collision counter expired */ +#define BIGMAC_IMASK_LCCNTEXP 0x00002000 /* Late-collision counter expired */ +#define BIGMAC_IMASK_FCNTEXP 0x00004000 /* First-collision counter expired */ +#define BIGMAC_IMASK_DTIMEXP 0x00008000 /* Defer-timer expired */ + +/* BigMac transmit config register. */ +#define BIGMAC_TXCFG_ENABLE 0x00000001 /* Enable the transmitter */ +#define BIGMAC_TXCFG_FIFO 0x00000010 /* Default tx fthresh... */ +#define BIGMAC_TXCFG_SMODE 0x00000020 /* Enable slow transmit mode */ +#define BIGMAC_TXCFG_CIGN 0x00000040 /* Ignore transmit collisions */ +#define BIGMAC_TXCFG_FCSOFF 0x00000080 /* Do not emit FCS */ +#define BIGMAC_TXCFG_DBACKOFF 0x00000100 /* Disable backoff */ +#define BIGMAC_TXCFG_FULLDPLX 0x00000200 /* Enable full-duplex */ + +/* BigMac receive config register. */ +#define BIGMAC_RXCFG_ENABLE 0x00000001 /* Enable the receiver */ +#define BIGMAC_RXCFG_FIFO 0x0000000e /* Default rx fthresh... */ +#define BIGMAC_RXCFG_PSTRIP 0x00000020 /* Pad byte strip enable */ +#define BIGMAC_RXCFG_PMISC 0x00000040 /* Enable promiscous mode */ +#define BIGMAC_RXCFG_DERR 0x00000080 /* Disable error checking */ +#define BIGMAC_RXCFG_DCRCS 0x00000100 /* Disable CRC stripping */ +#define BIGMAC_RXCFG_ME 0x00000200 /* Receive packets addressed to me */ +#define BIGMAC_RXCFG_PGRP 0x00000400 /* Enable promisc group mode */ +#define BIGMAC_RXCFG_HENABLE 0x00000800 /* Enable the hash filter */ +#define BIGMAC_RXCFG_AENABLE 0x00001000 /* Enable the address filter */ + +/* The BigMAC PHY transceiver. Not nearly as sophisticated as the happy meal + * one. But it does have the "bit banger", oh baby. + */ +struct bmac_tcvr { + unsigned int tcvr_pal; + unsigned int mgmt_pal; +}; + +/* Frame commands. */ +#define FRAME_WRITE 0x50020000 +#define FRAME_READ 0x60020000 + +/* Tranceiver registers. */ +#define TCVR_PAL_SERIAL 0x00000001 /* Enable serial mode */ +#define TCVR_PAL_EXTLBACK 0x00000002 /* Enable external loopback */ +#define TCVR_PAL_MSENSE 0x00000004 /* Media sense */ +#define TCVR_PAL_LTENABLE 0x00000008 /* Link test enable */ +#define TCVR_PAL_LTSTATUS 0x00000010 /* Link test status (P1 only) */ + +/* Management PAL. */ +#define MGMT_PAL_DCLOCK 0x00000001 /* Data clock */ +#define MGMT_PAL_OENAB 0x00000002 /* Output enabler */ +#define MGMT_PAL_MDIO 0x00000004 /* MDIO Data/attached */ +#define MGMT_PAL_TIMEO 0x00000008 /* Transmit enable timeout error */ +#define MGMT_PAL_EXT_MDIO MGMT_PAL_MDIO +#define MGMT_PAL_INT_MDIO MGMT_PAL_TIMEO + +/* Here are some PHY addresses. */ +#define BIGMAC_PHY_EXTERNAL 0 /* External transceiver */ +#define BIGMAC_PHY_INTERNAL 1 /* Internal transceiver */ + +/* PHY registers */ +#define BIGMAC_BMCR 0x00 /* Basic mode control register */ +#define BIGMAC_BMSR 0x01 /* Basic mode status register */ + +/* BMCR bits */ +#define BMCR_ISOLATE 0x0400 /* Disconnect DP83840 from MII */ +#define BMCR_PDOWN 0x0800 /* Powerdown the DP83840 */ +#define BMCR_ANENABLE 0x1000 /* Enable auto negotiation */ +#define BMCR_SPEED100 0x2000 /* Select 100Mbps */ +#define BMCR_LOOPBACK 0x4000 /* TXD loopback bits */ +#define BMCR_RESET 0x8000 /* Reset the DP83840 */ + +/* BMSR bits */ +#define BMSR_ERCAP 0x0001 /* Ext-reg capability */ +#define BMSR_JCD 0x0002 /* Jabber detected */ +#define BMSR_LSTATUS 0x0004 /* Link status */ + +/* Ring descriptors and such, same as Quad Ethernet. */ +struct be_rxd { + unsigned int rx_flags; + unsigned int rx_addr; +}; + +#define RXD_OWN 0x80000000 /* Ownership. */ +#define RXD_UPDATE 0x10000000 /* Being Updated? */ +#define RXD_LENGTH 0x000007ff /* Packet Length. */ + +struct be_txd { + unsigned int tx_flags; + unsigned int tx_addr; +}; + +#define TXD_OWN 0x80000000 /* Ownership. */ +#define TXD_SOP 0x40000000 /* Start Of Packet */ +#define TXD_EOP 0x20000000 /* End Of Packet */ +#define TXD_UPDATE 0x10000000 /* Being Updated? */ +#define TXD_LENGTH 0x000007ff /* Packet Length. */ + +#define TX_RING_MAXSIZE 256 +#define RX_RING_MAXSIZE 256 + +#define TX_RING_SIZE 256 +#define RX_RING_SIZE 256 + +#define NEXT_RX(num) (((num) + 1) & (RX_RING_SIZE - 1)) +#define NEXT_TX(num) (((num) + 1) & (TX_RING_SIZE - 1)) +#define PREV_RX(num) (((num) - 1) & (RX_RING_SIZE - 1)) +#define PREV_TX(num) (((num) - 1) & (TX_RING_SIZE - 1)) + +#define TX_BUFFS_AVAIL(bp) \ + (((bp)->tx_old <= (bp)->tx_new) ? \ + (bp)->tx_old + (TX_RING_SIZE - 1) - (bp)->tx_new : \ + (bp)->tx_old - (bp)->tx_new - 1) + + +#define SUN4C_TX_BUFFS_AVAIL(bp) \ + (((bp)->tx_old <= (bp)->tx_new) ? \ + (bp)->tx_old + (SUN4C_TX_RING_SIZE - 1) - (bp)->tx_new : \ + (bp)->tx_old - (bp)->tx_new - (TX_RING_SIZE - SUN4C_TX_RING_SIZE)) + + +#define RX_COPY_THRESHOLD 128 +#define RX_BUF_ALLOC_SIZE (ETH_FRAME_LEN + (64 * 3)) + +struct bmac_init_block { + struct be_rxd be_rxd[RX_RING_MAXSIZE]; + struct be_txd be_txd[TX_RING_MAXSIZE]; +}; + +#define bib_offset(mem, elem) \ +((__u32)((unsigned long)(&(((struct bmac_init_block *)0)->mem[elem])))) + +#define SUN4C_PKT_BUF_SZ 1546 +#define SUN4C_RX_BUFF_SIZE SUN4C_PKT_BUF_SZ +#define SUN4C_TX_BUFF_SIZE SUN4C_PKT_BUF_SZ + +#define SUN4C_RX_RING_SIZE 16 +#define SUN4C_TX_RING_SIZE 16 + +struct bigmac_buffers { + char tx_buf[SUN4C_TX_RING_SIZE][SUN4C_TX_BUFF_SIZE]; + char pad[2]; /* Align rx_buf for copy_and_sum(). */ + char rx_buf[SUN4C_RX_RING_SIZE][SUN4C_RX_BUFF_SIZE]; +}; + +#define bbuf_offset(mem, elem) \ +((__u32)((unsigned long)(&(((struct bigmac_buffers *)0)->mem[elem][0])))) + +/* Now software state stuff. */ +enum bigmac_transceiver { + external = 0, + internal = 1, + none = 2, +}; + +/* Timer state engine. */ +enum bigmac_timer_state { + ltrywait = 1, /* Forcing try of all modes, from fastest to slowest. */ + asleep = 2, /* Timer inactive. */ +}; + +struct bigmac { + struct qe_globreg *gregs; /* QEC Global Registers */ + struct qe_creg *creg; /* QEC BigMAC Channel Registers */ + struct BIG_MAC_regs *bregs; /* BigMAC Registers */ + struct bmac_tcvr *tregs; /* BigMAC Transceiver */ + struct bmac_init_block *bmac_block; /* RX and TX descriptors */ + __u32 bblock_dvma; /* RX and TX descriptors */ + + struct sk_buff *rx_skbs[RX_RING_SIZE]; + struct sk_buff *tx_skbs[TX_RING_SIZE]; + + int rx_new, tx_new, rx_old, tx_old; + + struct bigmac_buffers *sun4c_buffers; + __u32 s4c_buf_dvma; + + int board_rev; /* BigMAC board revision. */ + + enum bigmac_transceiver tcvr_type; + unsigned int bigmac_bursts; + unsigned int paddr; + unsigned short sw_bmsr; /* SW copy of PHY BMSR */ + unsigned short sw_bmcr; /* SW copy of PHY BMCR */ + struct timer_list bigmac_timer; + enum bigmac_timer_state timer_state; + unsigned int timer_ticks; + + struct enet_statistics enet_stats; + struct linux_sbus_device *qec_sbus_dev; + struct linux_sbus_device *bigmac_sbus_dev; + struct device *dev; + struct bigmac *next_module; +}; + +/* We use this to acquire receive skb's that we can DMA directly into. */ +#define ALIGNED_RX_SKB_ADDR(addr) \ + ((((unsigned long)(addr) + (64 - 1)) & ~(64 - 1)) - (unsigned long)(addr)) + +static inline struct sk_buff *big_mac_alloc_skb(unsigned int length, int gfp_flags) +{ + struct sk_buff *skb; + + skb = alloc_skb(length + 64, gfp_flags); + if(skb) { + int offset = ALIGNED_RX_SKB_ADDR(skb->data); + + if(offset) + skb_reserve(skb, offset); + } + return skb; +} + +#endif /* !(_SUNBMAC_H) */ diff -u --recursive --new-file v2.2.3/linux/drivers/net/sunhme.c linux/drivers/net/sunhme.c --- v2.2.3/linux/drivers/net/sunhme.c Thu Nov 19 09:56:28 1998 +++ linux/drivers/net/sunhme.c Mon Mar 15 16:11:30 1999 @@ -2,11 +2,11 @@ * auto carrier detecting ethernet driver. Also known as the * "Happy Meal Ethernet" found on SunSwift SBUS cards. * - * Copyright (C) 1996 David S. Miller (davem@caipfs.rutgers.edu) + * Copyright (C) 1996, 1998 David S. Miller (davem@caipfs.rutgers.edu) */ static char *version = - "sunhme.c:v1.2 10/Oct/96 David S. Miller (davem@caipfs.rutgers.edu)\n"; + "sunhme.c:v1.10 27/Jan/99 David S. Miller (davem@caipfs.rutgers.edu)\n"; #include @@ -35,12 +35,13 @@ #include #include #include -#include #include #include #ifndef __sparc_v9__ #include #endif +#include +#include #include #include @@ -57,6 +58,11 @@ static struct happy_meal *root_happy_dev = NULL; #endif +static struct quattro *qfe_sbus_list = NULL; +#ifdef CONFIG_PCI +static struct quattro *qfe_pci_list = NULL; +#endif + #undef HMEDEBUG #undef SXDEBUG #undef RXDEBUG @@ -363,6 +369,15 @@ { hp->sw_bmcr = happy_meal_tcvr_read(hp, tregs, DP83840_BMCR); + /* Downgrade from full to half duplex. Only possible + * via ethtool. + */ + if(hp->sw_bmcr & BMCR_FULLDPLX) { + hp->sw_bmcr &= ~(BMCR_FULLDPLX); + happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, hp->sw_bmcr); + return 0; + } + /* Downgrade from 100 to 10. */ if(hp->sw_bmcr & BMCR_SPEED100) { hp->sw_bmcr &= ~(BMCR_SPEED100); @@ -1049,7 +1064,7 @@ for(i = 0; i < RX_RING_SIZE; i++) { struct sk_buff *skb; - skb = happy_meal_alloc_skb(RX_BUF_ALLOC_SIZE, gfp_flags); + skb = happy_meal_alloc_skb(RX_BUF_ALLOC_SIZE, gfp_flags | GFP_DMA); if(!skb) continue; hp->rx_skbs[i] = skb; @@ -1077,7 +1092,7 @@ } else #endif { - hb->happy_meal_rxd[i].rx_addr = (u32)((unsigned long) skb->data); + hb->happy_meal_rxd[i].rx_addr = sbus_dvma_addr(skb->data); hb->happy_meal_rxd[i].rx_flags = (RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16)); } @@ -1115,7 +1130,8 @@ #endif static void happy_meal_begin_auto_negotiation(struct happy_meal *hp, - struct hmeal_tcvregs *tregs) + struct hmeal_tcvregs *tregs, + struct ethtool_cmd *ep) { int timeout; @@ -1124,93 +1140,108 @@ hp->sw_bmcr = happy_meal_tcvr_read(hp, tregs, DP83840_BMCR); hp->sw_physid1 = happy_meal_tcvr_read(hp, tregs, DP83840_PHYSID1); hp->sw_physid2 = happy_meal_tcvr_read(hp, tregs, DP83840_PHYSID2); - hp->sw_advertise = happy_meal_tcvr_read(hp, tregs, DP83840_ADVERTISE); /* XXX Check BMSR_ANEGCAPABLE, should not be necessary though. */ - /* Advertise everything we can support. */ - if(hp->sw_bmsr & BMSR_10HALF) - hp->sw_advertise |= (ADVERTISE_10HALF); - else - hp->sw_advertise &= ~(ADVERTISE_10HALF); + hp->sw_advertise = happy_meal_tcvr_read(hp, tregs, DP83840_ADVERTISE); + if(ep == NULL || ep->autoneg == AUTONEG_ENABLE) { + /* Advertise everything we can support. */ + if(hp->sw_bmsr & BMSR_10HALF) + hp->sw_advertise |= (ADVERTISE_10HALF); + else + hp->sw_advertise &= ~(ADVERTISE_10HALF); - if(hp->sw_bmsr & BMSR_10FULL) - hp->sw_advertise |= (ADVERTISE_10FULL); - else - hp->sw_advertise &= ~(ADVERTISE_10FULL); - if(hp->sw_bmsr & BMSR_100HALF) - hp->sw_advertise |= (ADVERTISE_100HALF); - else - hp->sw_advertise &= ~(ADVERTISE_100HALF); - if(hp->sw_bmsr & BMSR_100FULL) - hp->sw_advertise |= (ADVERTISE_100FULL); - else - hp->sw_advertise &= ~(ADVERTISE_100FULL); + if(hp->sw_bmsr & BMSR_10FULL) + hp->sw_advertise |= (ADVERTISE_10FULL); + else + hp->sw_advertise &= ~(ADVERTISE_10FULL); + if(hp->sw_bmsr & BMSR_100HALF) + hp->sw_advertise |= (ADVERTISE_100HALF); + else + hp->sw_advertise &= ~(ADVERTISE_100HALF); + if(hp->sw_bmsr & BMSR_100FULL) + hp->sw_advertise |= (ADVERTISE_100FULL); + else + hp->sw_advertise &= ~(ADVERTISE_100FULL); + happy_meal_tcvr_write(hp, tregs, DP83840_ADVERTISE, hp->sw_advertise); - /* XXX Currently no Happy Meal cards I know off support 100BaseT4, - * XXX and this is because the DP83840 does not support it, changes - * XXX would need to be made to the tx/rx logic in the driver as well - * XXX so I completely skip checking for it in the BMSR for now. - */ + /* XXX Currently no Happy Meal cards I know off support 100BaseT4, + * XXX and this is because the DP83840 does not support it, changes + * XXX would need to be made to the tx/rx logic in the driver as well + * XXX so I completely skip checking for it in the BMSR for now. + */ #ifdef AUTO_SWITCH_DEBUG - ASD(("%s: Advertising [ ", hp->dev->name)); - if(hp->sw_advertise & ADVERTISE_10HALF) - ASD(("10H ")); - if(hp->sw_advertise & ADVERTISE_10FULL) - ASD(("10F ")); - if(hp->sw_advertise & ADVERTISE_100HALF) - ASD(("100H ")); - if(hp->sw_advertise & ADVERTISE_100FULL) - ASD(("100F ")); + ASD(("%s: Advertising [ ", hp->dev->name)); + if(hp->sw_advertise & ADVERTISE_10HALF) + ASD(("10H ")); + if(hp->sw_advertise & ADVERTISE_10FULL) + ASD(("10F ")); + if(hp->sw_advertise & ADVERTISE_100HALF) + ASD(("100H ")); + if(hp->sw_advertise & ADVERTISE_100FULL) + ASD(("100F ")); #endif - happy_meal_tcvr_write(hp, tregs, DP83840_ADVERTISE, hp->sw_advertise); + /* Enable Auto-Negotiation, this is usually on already... */ + hp->sw_bmcr |= BMCR_ANENABLE; + happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, hp->sw_bmcr); - /* Enable Auto-Negotiation, this is usually on already... */ - hp->sw_bmcr |= BMCR_ANENABLE; - happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, hp->sw_bmcr); + /* Restart it to make sure it is going. */ + hp->sw_bmcr |= BMCR_ANRESTART; + happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, hp->sw_bmcr); - /* Restart it to make sure it is going. */ - hp->sw_bmcr |= BMCR_ANRESTART; - happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, hp->sw_bmcr); + /* BMCR_ANRESTART self clears when the process has begun. */ - /* BMCR_ANRESTART self clears when the process has begun. */ + timeout = 64; /* More than enough. */ + while(--timeout) { + hp->sw_bmcr = happy_meal_tcvr_read(hp, tregs, DP83840_BMCR); + if(!(hp->sw_bmcr & BMCR_ANRESTART)) + break; /* got it. */ + udelay(10); + } + if(!timeout) { + printk("%s: Happy Meal would not start auto negotiation " + "BMCR=0x%04x\n", hp->dev->name, hp->sw_bmcr); + printk("%s: Performing force link detection.\n", + hp->dev->name); + goto force_link; + } else { + hp->timer_state = arbwait; + } + } else { +force_link: + /* Force the link up, trying first a particular mode. + * Either we are here at the request of ethtool or + * because the Happy Meal would not start to autoneg. + */ - timeout = 64; /* More than enough. */ - while(--timeout) { - hp->sw_bmcr = happy_meal_tcvr_read(hp, tregs, DP83840_BMCR); - if(!(hp->sw_bmcr & BMCR_ANRESTART)) - break; /* got it. */ - udelay(10); - } - if(!timeout) { - printk("%s: Happy Meal would not start auto negotiation BMCR=0x%04x\n", - hp->dev->name, hp->sw_bmcr); - printk("%s: Performing force link detection.\n", hp->dev->name); - - /* Disable auto-negotiation in BMCR, enable FULL duplex and 100mb/s, - * setup the timer state machine, and fire it off. - * - * XXX Should probably reset the DP83840 first - * XXX as this is a gross fatal error... + /* Disable auto-negotiation in BMCR, enable the duplex and + * speed setting, init the timer state machine, and fire it off. */ - hp->sw_bmcr = BMCR_SPEED100; + if(ep == NULL || ep->autoneg == AUTONEG_ENABLE) { + hp->sw_bmcr = BMCR_SPEED100; + } else { + if(ep->speed == SPEED_100) + hp->sw_bmcr = BMCR_SPEED100; + else + hp->sw_bmcr = 0; + if(ep->duplex == DUPLEX_FULL) + hp->sw_bmcr |= BMCR_FULLDPLX; + } happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, hp->sw_bmcr); /* OK, seems we need do disable the transceiver for the first * tick to make sure we get an accurate link state at the * second tick. */ - hp->sw_csconfig = happy_meal_tcvr_read(hp, tregs, DP83840_CSCONFIG); - printk("%s: CSCONFIG [%04x], disabling transceiver\n", hp->dev->name, - hp->sw_csconfig); + hp->sw_csconfig = happy_meal_tcvr_read(hp, tregs, + DP83840_CSCONFIG); hp->sw_csconfig &= ~(CSCONFIG_TCVDISAB); - happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG, hp->sw_csconfig); + happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG, + hp->sw_csconfig); hp->timer_state = ltrywait; - } else { - hp->timer_state = arbwait; } hp->timer_ticks = 0; @@ -1220,6 +1251,9 @@ add_timer(&hp->happy_timer); } +#define CRC_POLYNOMIAL_BE 0x04c11db7UL /* Ethernet CRC, big endian */ +#define CRC_POLYNOMIAL_LE 0xedb88320UL /* Ethernet CRC, little endian */ + static int happy_meal_init(struct happy_meal *hp, int from_irq) { struct hmeal_gregs *gregs = hp->gregs; @@ -1227,9 +1261,12 @@ struct hmeal_erxregs *erxregs = hp->erxregs; struct hmeal_bigmacregs *bregs = hp->bigmacregs; struct hmeal_tcvregs *tregs = hp->tcvregs; - unsigned long regtmp; + unsigned long regtmp, rxcfg; unsigned char *e = &hp->dev->dev_addr[0]; + /* If auto-negotiation timer is running, kill it. */ + del_timer(&hp->happy_timer); + HMD(("happy_meal_init: happy_flags[%08x] ", hp->happy_flags)); if(!(hp->happy_flags & HFLAG_INIT)) { @@ -1322,12 +1359,54 @@ hme_write32(hp, &bregs->mac_addr1, ((e[2] << 8) | e[3])); hme_write32(hp, &bregs->mac_addr0, ((e[0] << 8) | e[1])); - /* Ick, figure out how to properly program the hash table later... */ HMD(("htable, ")); - hme_write32(hp, &bregs->htable3, 0); - hme_write32(hp, &bregs->htable2, 0); - hme_write32(hp, &bregs->htable1, 0); - hme_write32(hp, &bregs->htable0, 0); + if((hp->dev->flags & IFF_ALLMULTI) || + (hp->dev->mc_count > 64)) { + hme_write32(hp, &bregs->htable0, 0xffff); + hme_write32(hp, &bregs->htable1, 0xffff); + hme_write32(hp, &bregs->htable2, 0xffff); + hme_write32(hp, &bregs->htable3, 0xffff); + } else if((hp->dev->flags & IFF_PROMISC) == 0) { + u16 hash_table[4]; + struct dev_mc_list *dmi = hp->dev->mc_list; + char *addrs; + int i, j, bit, byte; + u32 crc, poly = CRC_POLYNOMIAL_LE; + + for(i = 0; i < 4; i++) + hash_table[i] = 0; + + for(i = 0; i < hp->dev->mc_count; i++) { + addrs = dmi->dmi_addr; + dmi = dmi->next; + + if(!(*addrs & 1)) + continue; + + crc = 0xffffffffU; + for(byte = 0; byte < 6; byte++) { + for(bit = *addrs++, j = 0; j < 8; j++, bit >>= 1) { + int test; + + test = ((bit ^ crc) & 0x01); + crc >>= 1; + if(test) + crc = crc ^ poly; + } + } + crc >>= 26; + hash_table[crc >> 4] |= 1 << (crc & 0xf); + } + hme_write32(hp, &bregs->htable0, hash_table[0]); + hme_write32(hp, &bregs->htable1, hash_table[1]); + hme_write32(hp, &bregs->htable2, hash_table[2]); + hme_write32(hp, &bregs->htable3, hash_table[3]); + } else { + hme_write32(hp, &bregs->htable3, 0); + hme_write32(hp, &bregs->htable2, 0); + hme_write32(hp, &bregs->htable1, 0); + hme_write32(hp, &bregs->htable0, 0); + } /* Set the RX and TX ring ptrs. */ HMD(("ring ptrs rxr[%08x] txr[%08x]\n", @@ -1343,9 +1422,22 @@ hme_read32(hp, &gregs->cfg))); #ifdef __sparc_v9__ + /* XXX Can sun4d do these too? */ if(hp->happy_bursts & DMA_BURST64) { + u32 gcfg = GREG_CFG_BURST64; + + /* I have no idea if I should set the extended + * transfer mode bit for Cheerio, so for now I + * do not. -DaveM + */ + if((hp->happy_flags & HFLAG_PCI) == 0) { + mmu_set_sbus64(hp->happy_sbus_dev, + hp->happy_bursts); + gcfg |= GREG_CFG_64BIT; + } + HMD(("64>")); - hme_write32(hp, &gregs->cfg, GREG_CFG_BURST64); + hme_write32(hp, &gregs->cfg, gcfg); } else #endif if(hp->happy_bursts & DMA_BURST32) { @@ -1396,7 +1488,10 @@ /* Enable Big Mac hash table filter. */ HMD(("happy_meal_init: enable hash rx_cfg_old[%08x], ", hme_read32(hp, &bregs->rx_cfg))); - hme_write32(hp, &bregs->rx_cfg, BIGMAC_RXCFG_HENABLE); + rxcfg = BIGMAC_RXCFG_HENABLE; + if(hp->dev->flags & IFF_PROMISC) + rxcfg |= BIGMAC_RXCFG_PMISC; + hme_write32(hp, &bregs->rx_cfg, rxcfg); /* Let the bits settle in the chip. */ udelay(10); @@ -1433,7 +1528,7 @@ hme_read32(hp, &bregs->rx_cfg) | BIGMAC_RXCFG_ENABLE); /* Get the autonegotiation started, and the watch timer ticking. */ - happy_meal_begin_auto_negotiation(hp, tregs); + happy_meal_begin_auto_negotiation(hp, tregs, NULL); /* Success. */ return 0; @@ -1505,7 +1600,7 @@ /* Only print messages for non-counter related interrupts. */ if(status & (GREG_STAT_RFIFOVF | GREG_STAT_STSTERR | GREG_STAT_TFIFO_UND | - GREG_STAT_MAXPKTERR | GREG_STAT_NORXD | GREG_STAT_RXERR | + GREG_STAT_MAXPKTERR | GREG_STAT_RXERR | GREG_STAT_RXPERR | GREG_STAT_RXTERR | GREG_STAT_EOPERR | GREG_STAT_MIFIRQ | GREG_STAT_TXEACK | GREG_STAT_TXLERR | GREG_STAT_TXPERR | GREG_STAT_TXTERR | GREG_STAT_SLVERR | @@ -1541,13 +1636,14 @@ } if(status & GREG_STAT_NORXD) { - /* AIEEE, out of receive descriptors. Check out our drop - * processing in happy_meal_rx to see how we try very hard - * to avoid this situation. + /* This is harmless, it just means the system is + * quite loaded and the incomming packet rate was + * faster than the interrupt handler could keep up + * with. */ - printk("%s: Happy Meal out of receive descriptors, aieee!\n", + printk(KERN_INFO "%s: Happy Meal out of receive " + "descriptors, packet dropped.\n", hp->dev->name); - reset = 1; } if(status & (GREG_STAT_RXERR|GREG_STAT_RXPERR|GREG_STAT_RXTERR)) { @@ -1662,13 +1758,6 @@ hp->tx_skbs[elem] = NULL; hp->net_stats.tx_bytes+=skb->len; -#ifdef NEED_DMA_SYNCHRONIZATION -#ifdef CONFIG_PCI - if(!(hp->happy_flags & HFLAG_PCI)) -#endif - mmu_sync_dma(kva_to_hva(hp, skb->data), - skb->len, hp->happy_sbus_dev->my_bus); -#endif dev_kfree_skb(skb); hp->net_stats.tx_packets++; @@ -1797,7 +1886,8 @@ struct sk_buff *new_skb; /* Now refill the entry, if we can. */ - new_skb = happy_meal_alloc_skb(RX_BUF_ALLOC_SIZE, GFP_ATOMIC); + new_skb = happy_meal_alloc_skb(RX_BUF_ALLOC_SIZE, + (GFP_DMA|GFP_ATOMIC)); if(!new_skb) { drops++; goto drop_it; @@ -1814,7 +1904,7 @@ /* Trim the original skb for the netif. */ skb_trim(skb, len); } else { - struct sk_buff *copy_skb = dev_alloc_skb(len+2); + struct sk_buff *copy_skb = dev_alloc_skb(len + 2); if(!copy_skb) { drops++; @@ -1908,7 +1998,8 @@ struct sk_buff *new_skb; /* Now refill the entry, if we can. */ - new_skb = happy_meal_alloc_skb(RX_BUF_ALLOC_SIZE, GFP_ATOMIC); + new_skb = happy_meal_alloc_skb(RX_BUF_ALLOC_SIZE, + (GFP_DMA|GFP_ATOMIC)); if(!new_skb) { drops++; goto drop_it; @@ -1925,7 +2016,7 @@ /* Trim the original skb for the netif. */ skb_trim(skb, len); } else { - struct sk_buff *copy_skb = dev_alloc_skb(len+2); + struct sk_buff *copy_skb = dev_alloc_skb(len + 2); if(!copy_skb) { drops++; @@ -2083,7 +2174,8 @@ struct sk_buff *new_skb; /* Now refill the entry, if we can. */ - new_skb = happy_meal_alloc_skb(RX_BUF_ALLOC_SIZE, GFP_ATOMIC); + new_skb = happy_meal_alloc_skb(RX_BUF_ALLOC_SIZE, + (GFP_DMA | GFP_ATOMIC)); if(!new_skb) { drops++; goto drop_it; @@ -2103,7 +2195,7 @@ /* Trim the original skb for the netif. */ skb_trim(skb, len); } else { - struct sk_buff *copy_skb = dev_alloc_skb(len+2); + struct sk_buff *copy_skb = dev_alloc_skb(len + 2); if(!copy_skb) { drops++; @@ -2328,12 +2420,39 @@ } #endif +static void quattro_sbus_interrupt(int irq, void *cookie, struct pt_regs *ptregs) +{ + struct quattro *qp = (struct quattro *)cookie; + int i; + + for(i = 0; i < 4; i++) { + struct device *hdev = qp->happy_meals[i]; + struct happy_meal *hp = (struct happy_meal *) hdev->priv; + volatile u32 *sreg = qp->irq_status[i]; + + if(sreg && + (hme_read32(hp, sreg) & (GREG_STAT_ERRORS | + GREG_STAT_MIFIRQ | + GREG_STAT_TXALL | + GREG_STAT_RXTOHOST)) != 0) + qp->handler(irq, hdev, ptregs); + } +} + static int happy_meal_open(struct device *dev) { struct happy_meal *hp = (struct happy_meal *) dev->priv; int res; HMD(("happy_meal_open: ")); + + /* On SBUS Quattro QFE cards, all hme interrupts are concentrated + * into a single source which we register handling at probe time. + */ + if((hp->happy_flags & (HFLAG_QUATTRO|HFLAG_PCI)) == HFLAG_QUATTRO) { + hp->qfe_parent->irq_status[hp->qfe_ent] = &hp->gregs->stat; + goto after_request_irq; + } #ifndef __sparc_v9__ if(sparc_cpu_model == sun4c) { if(request_irq(dev->irq, &sun4c_happy_meal_interrupt, @@ -2370,8 +2489,8 @@ __irq_itoa(dev->irq)); return -EAGAIN; } - HMD(("Init happy timer\n")); - init_timer(&hp->happy_timer); + +after_request_irq: HMD(("to happy_meal_init\n")); res = happy_meal_init(hp, 0); if(!res) { @@ -2390,7 +2509,17 @@ /* If auto-negotiation timer is running, kill it. */ del_timer(&hp->happy_timer); - free_irq(dev->irq, (void *)dev); + /* On Quattro QFE cards, all hme interrupts are concentrated + * into a single source which we register handling at probe + * time and never unregister. + */ + if((hp->happy_flags & (HFLAG_QUATTRO|HFLAG_PCI)) != HFLAG_QUATTRO) { + free_irq(dev->irq, (void *)dev); + } else { + /* Zap the status register pointer. */ + hp->qfe_parent->irq_status[hp->qfe_ent] = NULL; + } + MOD_DEC_USE_COUNT; return 0; } @@ -2429,10 +2558,23 @@ tx_add_log(hp, TXLOG_ACTION_TXMIT|TXLOG_ACTION_NBUFS, 0); return 1; } +#ifdef __sparc_v9__ + if ((unsigned long)(skb->data + skb->len) >= MAX_DMA_ADDRESS) { + struct sk_buff *new_skb = skb_copy(skb, GFP_DMA | GFP_ATOMIC); + if (!new_skb) + return 1; + dev_kfree_skb(skb); + skb = new_skb; + } +#endif len = skb->len; entry = hp->tx_new; SXD(("SX", len, entry)); +#ifdef NEED_DMA_SYNCHRONIZATION + mmu_sync_dma(kva_to_hva(hp, skb->data), + skb->len, hp->happy_sbus_dev->my_bus); +#endif hp->tx_skbs[entry] = skb; hp->happy_block->happy_meal_txd[entry].tx_addr = kva_to_hva(hp, skb->data); hp->happy_block->happy_meal_txd[entry].tx_flags = @@ -2622,9 +2764,6 @@ return &hp->net_stats; } -#define CRC_POLYNOMIAL_BE 0x04c11db7UL /* Ethernet CRC, big endian */ -#define CRC_POLYNOMIAL_LE 0xedb88320UL /* Ethernet CRC, little endian */ - static void happy_meal_set_multicast(struct device *dev) { struct happy_meal *hp = (struct happy_meal *) dev->priv; @@ -2634,10 +2773,6 @@ int i, j, bit, byte; u32 crc, poly = CRC_POLYNOMIAL_LE; - /* Let the transmits drain. */ - while(dev->tbusy) - schedule(); - /* Lock out others. */ set_bit(0, (void *) &dev->tbusy); @@ -2686,13 +2821,262 @@ dev->tbusy = 0; } +/* Ethtool support... */ +static int happy_meal_ioctl(struct device *dev, + struct ifreq *rq, int cmd) +{ + struct happy_meal *hp = (struct happy_meal *) dev->priv; + struct ethtool_cmd *ep_user = (struct ethtool_cmd *) rq->ifr_data; + struct ethtool_cmd ecmd; + + if(cmd != SIOCETHTOOL) + return -EOPNOTSUPP; + if(copy_from_user(&ecmd, ep_user, sizeof(ecmd))) + return -EFAULT; + + if(ecmd.cmd == SPARC_ETH_GSET) { + ecmd.supported = + (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | + SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII); + + /* XXX hardcoded stuff for now */ + ecmd.port = PORT_TP; /* XXX no MII support */ + ecmd.transceiver = XCVR_INTERNAL; /* XXX no external xcvr support */ + ecmd.phy_address = 0; /* XXX fixed PHYAD */ + + /* Record PHY settings. */ + hp->sw_bmcr = happy_meal_tcvr_read(hp, hp->tcvregs, DP83840_BMCR); + hp->sw_lpa = happy_meal_tcvr_read(hp, hp->tcvregs, DP83840_LPA); + if(hp->sw_bmcr & BMCR_ANENABLE) { + ecmd.autoneg = AUTONEG_ENABLE; + ecmd.speed = + (hp->sw_lpa & (LPA_100HALF | LPA_100FULL)) ? + SPEED_100 : SPEED_10; + if(ecmd.speed == SPEED_100) + ecmd.duplex = + (hp->sw_lpa & (LPA_100FULL)) ? + DUPLEX_FULL : DUPLEX_HALF; + else + ecmd.duplex = + (hp->sw_lpa & (LPA_10FULL)) ? + DUPLEX_FULL : DUPLEX_HALF; + } else { + ecmd.autoneg = AUTONEG_DISABLE; + ecmd.speed = + (hp->sw_bmcr & BMCR_SPEED100) ? + SPEED_100 : SPEED_10; + ecmd.duplex = + (hp->sw_bmcr & BMCR_FULLDPLX) ? + DUPLEX_FULL : DUPLEX_HALF; + } + if(copy_to_user(ep_user, &ecmd, sizeof(ecmd))) + return -EFAULT; + return 0; + } else if(ecmd.cmd == SPARC_ETH_SSET) { + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + + /* Verify the settings we care about. */ + if(ecmd.autoneg != AUTONEG_ENABLE && + ecmd.autoneg != AUTONEG_DISABLE) + return -EINVAL; + if(ecmd.autoneg == AUTONEG_DISABLE && + ((ecmd.speed != SPEED_100 && + ecmd.speed != SPEED_10) || + (ecmd.duplex != DUPLEX_HALF && + ecmd.duplex != DUPLEX_FULL))) + return -EINVAL; + + /* Ok, do it to it. */ + del_timer(&hp->happy_timer); + happy_meal_begin_auto_negotiation(hp, + hp->tcvregs, + &ecmd); + + return 0; + } else + return -EOPNOTSUPP; +} + +void __init quattro_get_ranges(struct quattro *qp) +{ + int err; + + err = prom_getproperty(qp->quattro_sbus_dev->prom_node, + "ranges", + (char *)&qp->ranges[0], + sizeof(qp->ranges)); + if(err == 0 || err == -1) { + qp->nranges = 0; + return; + } + qp->nranges = (err / sizeof(struct linux_prom_ranges)); +} + +static void __init quattro_apply_ranges(struct quattro *qp, struct happy_meal *hp) +{ + struct linux_sbus_device *sdev = hp->happy_sbus_dev; + int rng; + + for(rng = 0; rng < qp->nranges; rng++) { + struct linux_prom_ranges *rngp = &qp->ranges[rng]; + int reg; + + for(reg = 0; reg < 5; reg++) { + if(sdev->reg_addrs[reg].which_io == + rngp->ot_child_space) + break; + } + if(reg == 5) + continue; + + sdev->reg_addrs[reg].which_io = rngp->ot_parent_space; + sdev->reg_addrs[reg].phys_addr += rngp->ot_parent_base; + } +} + +/* Given a happy meal sbus device, find it's quattro parent. + * If none exist, allocate and return a new one. + * + * Return NULL on failure. + */ +static struct quattro * __init quattro_sbus_find(struct linux_sbus_device *goal_sdev) +{ + struct linux_sbus *sbus; + struct linux_sbus_device *sdev; + struct quattro *qp; + + for(qp = qfe_sbus_list; qp != NULL; qp = qp->next) { + for(sdev = qp->quattro_sbus_dev->child; + sdev != NULL; + sdev = sdev->next) { + if(sdev == goal_sdev) + return qp; + } + } + for_each_sbus(sbus) { + for_each_sbusdev(sdev, sbus) { + if(sdev->child != NULL) { + struct linux_sbus_device *p; + + for(p = sdev->child; p != NULL; p = p->next) + if(p == goal_sdev) + goto found; + } + } + } + + /* Cannot find quattro parent, fail. */ + return NULL; + +found: + qp = kmalloc(sizeof(struct quattro), GFP_KERNEL); + if(qp != NULL) { + int i; + + for(i = 0; i < 4; i++) { + qp->irq_status[i] = NULL; + qp->happy_meals[i] = NULL; + } + qp->handler = NULL; + qp->quattro_sbus_dev = sdev; +#ifdef CONFIG_PCI + qp->quattro_pci_dev = NULL; +#endif + qp->next = qfe_sbus_list; + qfe_sbus_list = qp; + quattro_get_ranges(qp); + } + return qp; +} + +#ifdef CONFIG_PCI +static struct quattro * __init quattro_pci_find(struct pci_dev *pdev) +{ + struct pci_dev *bdev = pdev->bus->self; + struct quattro *qp; + + if (!bdev) return NULL; + for(qp = qfe_pci_list; qp != NULL; qp = qp->next) { + if(qp->quattro_pci_dev == bdev) + return qp; + } + qp = kmalloc(sizeof(struct quattro), GFP_KERNEL); + if(qp != NULL) { + int i; + + for(i = 0; i < 4; i++) { + qp->irq_status[i] = NULL; + qp->happy_meals[i] = NULL; + } + qp->handler = NULL; + qp->quattro_sbus_dev = NULL; + qp->quattro_pci_dev = bdev; + qp->next = qfe_pci_list; + qfe_pci_list = qp; + + /* No range tricks necessary on PCI. */ + qp->nranges = 0; + } + return qp; +} +#endif + +/* After all quattro cards have been probed, we call these functions + * to register the IRQ handlers. + */ +static void __init quattro_sbus_register_irqs(void) +{ + struct quattro *qp; + + for(qp = qfe_sbus_list; qp != NULL; qp = qp->next) { + int err; + + /* Set the handler. */ +#ifndef __sparc_v9__ + if(sparc_cpu_model == sun4c) + qp->handler = sun4c_happy_meal_interrupt; + else if(sparc_cpu_model == sun4c) + qp->handler = sun4d_happy_meal_interrupt; + else +#endif +#ifdef CONFIG_PCI + if(qp->quattro_pci_dev != NULL) + panic("QFE: PCI qfe in sbus_register_irqs!"); + else +#endif + qp->handler = happy_meal_interrupt; + + err = request_irq(qp->quattro_sbus_dev->irqs[0], + quattro_sbus_interrupt, + SA_SHIRQ, "Quattro", + qp); + if(err != 0) { + printk("Quattro: Fatal IRQ registery error %d.\n", err); + panic("QFE request irq"); + } + } +} + static unsigned hme_version_printed = 0; -static inline int happy_meal_ether_init(struct device *dev, struct linux_sbus_device *sdev) +static int __init happy_meal_ether_init(struct device *dev, struct linux_sbus_device *sdev, int is_qfe) { + struct quattro *qp = NULL; struct happy_meal *hp; - int i; + int i, qfe_slot = -1; + if(is_qfe) { + qp = quattro_sbus_find(sdev); + if(qp == NULL) + return ENODEV; + for(qfe_slot = 0; qfe_slot < 4; qfe_slot++) + if(qp->happy_meals[qfe_slot] == NULL) + break; + if(qfe_slot == 4) + return ENODEV; + } if(dev == NULL) { dev = init_etherdev(0, sizeof(struct happy_meal)); } else { @@ -2703,9 +3087,16 @@ if(hme_version_printed++ == 0) printk(version); - printk("%s: HAPPY MEAL (SBUS) 10/100baseT Ethernet ", dev->name); + if(qfe_slot != -1) + printk("%s: Quattro HME slot %d (SBUS) 10/100baseT Ethernet", + dev->name, qfe_slot); + else + printk("%s: HAPPY MEAL (SBUS) 10/100baseT Ethernet ", + dev->name); dev->base_addr = (long) sdev; + + /* XXX Check for local-mac-address property on Quattro... -DaveM */ for(i = 0; i < 6; i++) printk("%2.2x%c", dev->dev_addr[i] = idprom->id_ethaddr[i], @@ -2727,6 +3118,13 @@ return ENODEV; } + if(qp != NULL) { + hp->qfe_parent = qp; + hp->qfe_ent = qfe_slot; + qp->happy_meals[qfe_slot] = dev; + quattro_apply_ranges(qp, hp); + } + prom_apply_sbus_ranges(sdev->my_bus, &sdev->reg_addrs[0], sdev->num_registers, sdev); hp->gregs = sparc_alloc_io(sdev->reg_addrs[0].phys_addr, 0, @@ -2784,6 +3182,9 @@ else if(hp->hm_revision != 0xa0) hp->happy_flags = HFLAG_NOT_A0; + if(qp != NULL) + hp->happy_flags |= HFLAG_QUATTRO; + /* Get the supported DVMA burst sizes from our Happy SBUS. */ hp->happy_bursts = prom_getintdefault(hp->happy_sbus_dev->my_bus->prom_node, "burst-sizes", 0x00); @@ -2817,6 +3218,8 @@ */ happy_meal_set_initial_advertisement(hp); + init_timer(&hp->happy_timer); + hp->dev = dev; dev->open = &happy_meal_open; dev->stop = &happy_meal_close; @@ -2830,6 +3233,7 @@ dev->hard_start_xmit = &happy_meal_start_xmit; dev->get_stats = &happy_meal_get_stats; dev->set_multicast_list = &happy_meal_set_multicast; + dev->do_ioctl = &happy_meal_ioctl; dev->irq = sdev->irqs[0]; dev->dma = 0; @@ -2846,14 +3250,35 @@ } #ifdef CONFIG_PCI -__initfunc(int happy_meal_pci_init(struct device *dev, struct pci_dev *pdev)) +static int __init happy_meal_pci_init(struct device *dev, struct pci_dev *pdev) { + struct quattro *qp = NULL; struct pcidev_cookie *pcp; struct happy_meal *hp; unsigned long hpreg_base; unsigned short pci_command; - int i, node; + int i, node, qfe_slot = -1; + char prom_name[64]; + /* Now make sure pci_dev cookie is there. */ + pcp = pdev->sysdata; + if(pcp == NULL || pcp->prom_node == -1) { + printk("happymeal(PCI): Some PCI device info missing\n"); + return ENODEV; + } + node = pcp->prom_node; + + prom_getstring(node, "name", prom_name, sizeof(prom_name)); + if (!strcmp(prom_name, "SUNW,qfe") || !strcmp(prom_name, "qfe")) { + qp = quattro_pci_find(pdev); + if(qp == NULL) + return ENODEV; + for(qfe_slot = 0; qfe_slot < 4; qfe_slot++) + if(qp->happy_meals[qfe_slot] == NULL) + break; + if(qfe_slot == 4) + return ENODEV; + } if(dev == NULL) { dev = init_etherdev(0, sizeof(struct happy_meal)); } else { @@ -2864,15 +3289,28 @@ if(hme_version_printed++ == 0) printk(version); - printk("%s: HAPPY MEAL (PCI/CheerIO) 10/100BaseT Ethernet ", dev->name); + if (!qfe_slot) { + prom_name[0] = 0; + if (!strncmp(dev->name, "eth", 3)) { + int i = simple_strtoul(dev->name + 3, NULL, 10); + sprintf(prom_name, "-%d", i + 3); + } + printk("%s%s: Quattro HME (PCI/CheerIO) 10/100baseT Ethernet ", dev->name, prom_name); + if (qp->quattro_pci_dev->vendor == PCI_VENDOR_ID_DEC && + qp->quattro_pci_dev->device == PCI_DEVICE_ID_DEC_21153) + printk("DEC 21153 PCI Bridge\n"); + else + printk("unknown bridge %04x.%04x\n", + qp->quattro_pci_dev->vendor, qp->quattro_pci_dev->device); + } + if(qfe_slot != -1) + printk("%s: Quattro HME slot %d (PCI/CheerIO) 10/100baseT Ethernet ", + dev->name, qfe_slot); + else + printk("%s: HAPPY MEAL (PCI/CheerIO) 10/100BaseT Ethernet ", + dev->name); dev->base_addr = (long) pdev; - for(i = 0; i < 6; i++) - printk("%2.2x%c", - dev->dev_addr[i] = idprom->id_ethaddr[i], - i == 5 ? ' ' : ':'); - - printk("\n"); hp = (struct happy_meal *)dev->priv; memset(hp, 0, sizeof(*hp)); @@ -2880,6 +3318,12 @@ hp->happy_sbus_dev = NULL; hp->happy_pci_dev = pdev; + if(qp != NULL) { + hp->qfe_parent = qp; + hp->qfe_ent = qfe_slot; + qp->happy_meals[qfe_slot] = dev; + } + hpreg_base = pdev->base_address[0]; if((hpreg_base & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) { printk("happymeal(PCI): Cannot find proper PCI device base address.\n"); @@ -2887,13 +3331,14 @@ } hpreg_base &= PCI_BASE_ADDRESS_MEM_MASK; - /* Now make sure pci_dev cookie is there. */ - pcp = pdev->sysdata; - if(pcp == NULL || pcp->prom_node == -1) { - printk("happymeal(PCI): Some PCI device info missing\n"); - return ENODEV; - } - node = pcp->prom_node; + if (qfe_slot != -1 && prom_getproplen(node, "local-mac-address") == 6) + prom_getproperty(node, "local-mac-address", dev->dev_addr, 6); + else + memcpy(dev->dev_addr, idprom->id_ethaddr, 6); + for(i = 0; i < 6; i++) + printk("%2.2x%c", dev->dev_addr[i], i == 5 ? ' ' : ':'); + + printk("\n"); /* Layout registers. */ hp->gregs = (struct hmeal_gregs *) (hpreg_base + 0x0000); @@ -2912,6 +3357,9 @@ else if(hp->hm_revision != 0xa0) hp->happy_flags = HFLAG_NOT_A0; + if(qp != NULL) + hp->happy_flags |= HFLAG_QUATTRO; + /* And of course, indicate this is PCI. */ hp->happy_flags |= HFLAG_PCI; @@ -2937,29 +3385,32 @@ hp->timer_ticks = 0; happy_meal_set_initial_advertisement(hp); + init_timer(&hp->happy_timer); + hp->dev = dev; dev->open = &happy_meal_open; dev->stop = &happy_meal_close; dev->hard_start_xmit = &pci_happy_meal_start_xmit; dev->get_stats = &happy_meal_get_stats; dev->set_multicast_list = &happy_meal_set_multicast; + dev->do_ioctl = &happy_meal_ioctl; dev->irq = pdev->irq; dev->dma = 0; ether_setup(dev); /* If we don't do this, nothing works. */ - pcibios_read_config_word(pdev->bus->number, - pdev->devfn, - PCI_COMMAND, &pci_command); + pci_read_config_word(pdev, PCI_COMMAND, &pci_command); pci_command |= PCI_COMMAND_MASTER; - pcibios_write_config_word(pdev->bus->number, - pdev->devfn, - PCI_COMMAND, pci_command); - - /* Set the latency timer as well, PROM leaves it at zero. */ - pcibios_write_config_byte(pdev->bus->number, - pdev->devfn, - PCI_LATENCY_TIMER, 240); + pci_write_config_word(pdev, PCI_COMMAND, pci_command); + + /* Set the latency timer and cache line size as well, + * PROM leaves it at zero. + */ + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 128); +#ifdef __sparc_v9__ + /* NOTE: Cache line size is in 32-bit word units. */ + pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 0x10); +#endif #ifdef MODULE /* We are home free at this point, link us in to the happy @@ -2973,7 +3424,7 @@ } #endif -__initfunc(int happy_meal_probe(struct device *dev)) +int __init happy_meal_probe(struct device *dev) { struct linux_sbus *bus; struct linux_sbus_device *sdev = 0; @@ -2990,11 +3441,18 @@ dev = NULL; if(!strcmp(sdev->prom_name, "SUNW,hme")) { cards++; - if((v = happy_meal_ether_init(dev, sdev))) + if((v = happy_meal_ether_init(dev, sdev, 0))) + return v; + } else if(!strcmp(sdev->prom_name, "qfe") || + !strcmp(sdev->prom_name, "SUNW,qfe")) { + cards++; + if((v = happy_meal_ether_init(dev, sdev, 1))) return v; } } } + if(cards != 0) + quattro_sbus_register_irqs(); #ifdef CONFIG_PCI if(pci_present()) { struct pci_dev *pdev; diff -u --recursive --new-file v2.2.3/linux/drivers/net/sunhme.h linux/drivers/net/sunhme.h --- v2.2.3/linux/drivers/net/sunhme.h Thu Nov 19 09:56:28 1998 +++ linux/drivers/net/sunhme.h Mon Mar 15 16:11:30 1999 @@ -507,6 +507,8 @@ asleep = 3, /* Time inactive. */ }; +struct quattro; + /* Happy happy, joy joy! */ struct happy_meal { struct hmeal_gregs *gregs; /* Happy meal global registers */ @@ -559,6 +561,8 @@ struct pci_dev *happy_pci_dev; #endif struct device *dev; /* Backpointer */ + struct quattro *qfe_parent; /* For Quattro cards */ + int qfe_ent; /* Which instance on quattro */ struct happy_meal *next_module; }; @@ -575,10 +579,28 @@ #define HFLAG_INIT 0x00000200 /* Init called at least once */ #define HFLAG_LINKUP 0x00000400 /* 1 = Link is up */ #define HFLAG_PCI 0x00000800 /* PCI based Happy Meal */ +#define HFLAG_QUATTRO 0x00001000 /* On QFE/Quattro card */ #define HFLAG_20_21 (HFLAG_POLLENABLE | HFLAG_FENABLE) #define HFLAG_NOT_A0 (HFLAG_POLLENABLE | HFLAG_FENABLE | HFLAG_LANCE | HFLAG_RXCV) +/* Support for QFE/Quattro cards. */ +struct quattro { + volatile u32 *irq_status[4]; + struct device *happy_meals[4]; + void (*handler)(int, void *, struct pt_regs *); + + struct linux_sbus_device *quattro_sbus_dev; +#ifdef CONFIG_PCI + struct pci_dev *quattro_pci_dev; +#endif + struct quattro *next; + + /* PROM ranges, if any. */ + struct linux_prom_ranges ranges[8]; + int nranges; +}; + /* We use this to acquire receive skb's that we can DMA directly into. */ #define ALIGNED_RX_SKB_ADDR(addr) \ ((((unsigned long)(addr) + (64 - 1)) & ~(64 - 1)) - (unsigned long)(addr)) @@ -606,7 +628,16 @@ return (u32) virt_to_bus((volatile void *)addr); else #endif - return (u32) ((unsigned long)addr); + { +#ifdef __sparc_v9__ + if (((unsigned long) addr) >= MAX_DMA_ADDRESS) { + printk("sunhme: Bogus DMA buffer address " + "[%016lx]\n", ((unsigned long) addr)); + panic("DMA address too large, tell DaveM"); + } +#endif + return sbus_dvma_addr(addr); + } } extern inline unsigned int hme_read32(struct happy_meal *hp, diff -u --recursive --new-file v2.2.3/linux/drivers/net/sunlance.c linux/drivers/net/sunlance.c --- v2.2.3/linux/drivers/net/sunlance.c Sat Sep 5 16:46:40 1998 +++ linux/drivers/net/sunlance.c Sun Mar 21 07:22:00 1999 @@ -1,4 +1,4 @@ -/* $Id: sunlance.c,v 1.81 1998/08/10 09:08:23 jj Exp $ +/* $Id: sunlance.c,v 1.85 1999/03/21 05:22:05 davem Exp $ * lance.c: Linux/Sparc/Lance driver * * Written 1995, 1996 by Miguel de Icaza @@ -56,12 +56,16 @@ * * 1.11: * 12/27/97: Added sun4d support. (jj@sunsite.mff.cuni.cz) + * + * 1.12: + * 11/3/99: Fixed SMP race in lance_start_xmit found by davem. + * Anton Blanchard (anton@progsoc.uts.edu.au) */ #undef DEBUG_DRIVER static char *version = - "sunlance.c:v1.11 27/Dec/97 Miguel de Icaza (miguel@nuclecu.unam.mx)\n"; + "sunlance.c:v1.12 11/Mar/99 Miguel de Icaza (miguel@nuclecu.unam.mx)\n"; static char *lancestr = "LANCE"; static char *lancedma = "LANCE DMA"; @@ -252,6 +256,7 @@ struct device *dev; /* Backpointer */ struct lance_private *next_module; struct linux_sbus *sbus; + struct timer_list multicast_timer; }; #define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\ @@ -326,8 +331,6 @@ lp->rx_new = lp->tx_new = 0; lp->rx_old = lp->tx_old = 0; - ib->mode = 0; - /* Copy the ethernet address to the lance init block * Note that on the sparc you need to swap the ethernet address. * Note also we want the CPU ptr of the init_block here. @@ -384,10 +387,6 @@ ib->tx_ptr = leptr; if (ZERO) printk ("TX ptr: %8.8x\n", leptr); - - /* Clear the multicast filter */ - ib->filter [0] = 0; - ib->filter [1] = 0; } static int init_restart_lance (struct lance_private *lp) @@ -668,6 +667,7 @@ { struct lance_private *lp = (struct lance_private *)dev->priv; volatile struct lance_regs *ll = lp->ll; + volatile struct lance_init_block *ib = lp->init_block; int status = 0; last_dev = dev; @@ -686,6 +686,16 @@ if (lp->ledma) lp->ledma->regs->dma_test = ((__u32) lp->init_block_dvma) & 0xff000000; + /* Set mode and clear multicast filter only at device open, + so that lance_init_ring() called at any error will not + forget multicast filters. + + BTW it is common bug in all lance drivers! --ANK + */ + ib->mode = 0; + ib->filter [0] = 0; + ib->filter [1] = 0; + lance_init_ring (dev); load_csrs (lp); @@ -742,6 +752,7 @@ dev->start = 0; dev->tbusy = 1; + del_timer(&lp->multicast_timer); /* Stop the card */ ll->rap = LE_CSR0; @@ -791,27 +802,19 @@ volatile unsigned long flush; unsigned long flags; int entry, skblen, len; - int status = 0; - static int outs; - /* Transmitter timeout, serious problems */ - if (dev->tbusy) { + if (test_and_set_bit (0, (void *) &dev->tbusy) != 0) { int tickssofar = jiffies - dev->trans_start; - - if (tickssofar < 100) { - status = -1; - } else { - printk ("%s: transmit timed out, status %04x, reset\n", - dev->name, ll->rdp); - lance_reset (dev); - } - return status; - } - /* Block a timer-based transmit from overlapping. */ - if (test_and_set_bit (0, (void *) &dev->tbusy) != 0) { - printk ("Transmitter access conflict.\n"); - return -1; + if (tickssofar < 100) + return 1; + + printk ("%s: transmit timed out, status %04x, reset\n", + dev->name, ll->rdp); + lp->stats.tx_errors++; + lance_reset (dev); + + return 1; } skblen = skb->len; @@ -820,13 +823,13 @@ if (!TX_BUFFS_AVAIL) { restore_flags(flags); - return -1; + return 1; } len = (skblen <= ETH_ZLEN) ? ETH_ZLEN : skblen; - + lp->stats.tx_bytes += len; - + entry = lp->tx_new & TX_RING_MOD_MASK; ib->btx_ring [entry].length = (-len) | 0xf000; ib->btx_ring [entry].misc = 0; @@ -842,7 +845,6 @@ ib->btx_ring [entry].tmd1_bits = (LE_T1_POK|LE_T1_OWN); lp->tx_new = (lp->tx_new+1) & TX_RING_MOD_MASK; - outs++; /* Kick the lance: transmit now */ ll->rdp = LE_C0_INEA | LE_C0_TDMD; dev->trans_start = jiffies; @@ -857,7 +859,7 @@ flush = ll->rdp; restore_flags(flags); - return status; + return 0; } static struct net_device_stats *lance_get_stats (struct device *dev) @@ -879,7 +881,7 @@ u32 crc, poly = CRC_POLYNOMIAL_LE; /* set all multicast bits */ - if (dev->flags & IFF_ALLMULTI){ + if (dev->flags & IFF_ALLMULTI) { ib->filter [0] = 0xffffffff; ib->filter [1] = 0xffffffff; return; @@ -889,31 +891,29 @@ ib->filter [1] = 0; /* Add addresses */ - for (i = 0; i < dev->mc_count; i++){ + for (i = 0; i < dev->mc_count; i++) { addrs = dmi->dmi_addr; dmi = dmi->next; /* multicast address? */ if (!(*addrs & 1)) continue; - + crc = 0xffffffff; - for (byte = 0; byte < 6; byte++) + for (byte = 0; byte < 6; byte++) { for (bit = *addrs++, j = 0; j < 8; j++, bit >>= 1) { int test; test = ((bit ^ crc) & 0x01); crc >>= 1; - if (test) { + if (test) crc = crc ^ poly; - } } - + } crc = crc >> 26; mcast_table [crc >> 4] |= 1 << (crc & 0xf); } - return; } static void lance_set_multicast (struct device *dev) @@ -922,12 +922,33 @@ volatile struct lance_init_block *ib = lp->init_block; volatile struct lance_regs *ll = lp->ll; - while (dev->tbusy) - schedule(); + if (!dev->start) + return; + + if (dev->tbusy) { + mod_timer(&lp->multicast_timer, jiffies + 2); + return; + } + /* This CANNOT be correct. Chip is running + and dev->tbusy may change any moment. + It is useless to set it. + + Generally, usage of dev->tbusy in this driver is completely + wrong. + + I protected calls to this function + with start_bh_atomic, so that set_multicast_list + and hard_start_xmit are serialized now by top level. --ANK + + The same is true about a2065. + */ set_bit (0, (void *) &dev->tbusy); - while (lp->tx_old != lp->tx_new) - schedule(); + if (lp->tx_old != lp->tx_new) { + mod_timer(&lp->multicast_timer, jiffies + 4); + dev->tbusy = 0; + return; + } ll->rap = LE_CSR0; ll->rdp = LE_C0_STOP; @@ -942,6 +963,7 @@ load_csrs (lp); init_restart_lance (lp); dev->tbusy = 0; + mark_bh(NET_BH); } __initfunc(static int @@ -1100,6 +1122,16 @@ dev->dma = 0; ether_setup (dev); + + /* We cannot sleep if the chip is busy during a + * multicast list update event, because such events + * can occur from interrupts (ex. IPv6). So we + * use a timer to try again later when necessary. -DaveM + */ + init_timer(&lp->multicast_timer); + lp->multicast_timer.data = (unsigned long) dev; + lp->multicast_timer.function = + (void (*)(unsigned long)) &lance_set_multicast; #ifdef MODULE dev->ifindex = dev_new_index(); diff -u --recursive --new-file v2.2.3/linux/drivers/net/sunqe.c linux/drivers/net/sunqe.c --- v2.2.3/linux/drivers/net/sunqe.c Tue Jul 28 14:21:08 1998 +++ linux/drivers/net/sunqe.c Mon Mar 15 16:11:30 1999 @@ -36,7 +36,6 @@ #include #include #include -#include #include #include @@ -145,7 +144,7 @@ for(i = 0; i < RX_RING_SIZE; i++) { struct sk_buff *skb; - skb = qe_alloc_skb(RX_BUF_ALLOC_SIZE, gfp_flags); + skb = qe_alloc_skb(RX_BUF_ALLOC_SIZE, gfp_flags | GFP_DMA); if(!skb) continue; @@ -155,8 +154,7 @@ skb_put(skb, ETH_FRAME_LEN); skb_reserve(skb, 34); - qb->qe_rxd[i].rx_addr = - (u32) ((unsigned long)skb->data); + qb->qe_rxd[i].rx_addr = sbus_dvma_addr(skb->data); qb->qe_rxd[i].rx_flags = (RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH)); } @@ -447,10 +445,6 @@ skb = qep->tx_skbs[elem]; qep->tx_skbs[elem] = NULL; qep->net_stats.tx_bytes+=skb->len; -#ifdef NEED_DMA_SYNCHRONIZATION - mmu_sync_dma(((u32)((unsigned long)skb->data)), - skb->len, qep->qe_sbusdev->my_bus); -#endif dev_kfree_skb(skb); qep->net_stats.tx_packets++; @@ -498,22 +492,28 @@ drop_it: /* Return it to the QE. */ qep->net_stats.rx_dropped++; - this->rx_addr = - (u32) ((unsigned long)qep->rx_skbs[elem]->data); + this->rx_addr = sbus_dvma_addr(qep->rx_skbs[elem]->data); this->rx_flags = (RXD_OWN | (RX_BUF_ALLOC_SIZE & RXD_LENGTH)); goto next; } skb = qep->rx_skbs[elem]; #ifdef NEED_DMA_SYNCHRONIZATION - mmu_sync_dma(((u32)((unsigned long)skb->data)), +#ifdef __sparc_v9__ + if ((unsigned long) (skb->data + skb->len) >= MAX_DMA_ADDRESS) { + printk("sunqe: Bogus DMA buffer address " + "[%016lx]\n", ((unsigned long) skb->data)); + panic("DMA address too large, tell DaveM"); + } +#endif + mmu_sync_dma(sbus_dvma_addr(skb->data), skb->len, qep->qe_sbusdev->my_bus); #endif if(len > RX_COPY_THRESHOLD) { struct sk_buff *new_skb; /* Now refill the entry, if we can. */ - new_skb = qe_alloc_skb(RX_BUF_ALLOC_SIZE, GFP_ATOMIC); + new_skb = qe_alloc_skb(RX_BUF_ALLOC_SIZE, (GFP_DMA|GFP_ATOMIC)); if(!new_skb) { drops++; goto drop_it; @@ -524,8 +524,7 @@ skb_put(new_skb, ETH_FRAME_LEN); skb_reserve(new_skb, 34); - rxbase[elem].rx_addr = - (u32) ((unsigned long)new_skb->data); + rxbase[elem].rx_addr = sbus_dvma_addr(new_skb->data); rxbase[elem].rx_flags = (RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH)); @@ -545,8 +544,7 @@ eth_copy_and_sum(copy_skb, (unsigned char *)skb->data, len, 0); /* Reuse original ring buffer. */ - rxbase[elem].rx_addr = - (u32) ((unsigned long)skb->data); + rxbase[elem].rx_addr = sbus_dvma_addr(skb->data); rxbase[elem].rx_flags = (RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH)); @@ -741,6 +739,19 @@ if(!TX_BUFFS_AVAIL(qep)) return 1; +#ifdef NEED_DMA_SYNCHRONIZATION +#ifdef __sparc_v9__ + if ((unsigned long) (skb->data + skb->len) >= MAX_DMA_ADDRESS) { + struct sk_buff *new_skb = skb_copy(skb, GFP_DMA | GFP_ATOMIC); + if(!new_skb) + return 1; + dev_kfree_skb(skb); + skb = new_skb; + } +#endif + mmu_sync_dma(sbus_dvma_addr(skb->data), + skb->len, qep->qe_sbusdev->my_bus); +#endif len = skb->len; entry = qep->tx_new; @@ -749,7 +760,7 @@ qep->tx_skbs[entry] = skb; - qep->qe_block->qe_txd[entry].tx_addr = (u32) ((unsigned long) skb->data); + qep->qe_block->qe_txd[entry].tx_addr = sbus_dvma_addr(skb->data); qep->qe_block->qe_txd[entry].tx_flags = (TXD_OWN | TXD_SOP | TXD_EOP | (len & TXD_LENGTH)); qep->tx_new = NEXT_TX(entry); diff -u --recursive --new-file v2.2.3/linux/drivers/net/wavelan.c linux/drivers/net/wavelan.c --- v2.2.3/linux/drivers/net/wavelan.c Fri Oct 9 13:27:09 1998 +++ linux/drivers/net/wavelan.c Wed Mar 10 16:51:35 1999 @@ -290,24 +290,26 @@ wv_16_on(ioaddr, hacr); } /* psa_write */ -#ifdef PSA_CRC +#ifdef SET_PSA_CRC /*------------------------------------------------------------------*/ /* - * Calculate the PSA CRC (not tested yet) - * As the WaveLAN drivers don't use the CRC, I won't use it either. - * Thanks to Nico Valster for the code + * Calculate the PSA CRC + * Thanks to Valster, Nico for the code * NOTE: By specifying a length including the CRC position the - * returned value should be zero. (i.e. a correct checksum in the PSA). + * returned value should be zero. (i.e. a correct checksum in the PSA) + * + * The Windows drivers don't use the CRC, but the AP and the PtP tool + * depend on it. */ -static u_short -psa_crc(u_short * psa, /* The PSA */ +static inline u_short +psa_crc(u_char * psa, /* The PSA */ int size) /* Number of short for CRC */ { int byte_cnt; /* Loop on the PSA */ u_short crc_bytes = 0; /* Data in the PSA */ int bit_cnt; /* Loop on the bits of the short */ - for(byte_cnt = 0; byte_cnt <= size; byte_cnt++ ) + for(byte_cnt = 0; byte_cnt < size; byte_cnt++ ) { crc_bytes ^= psa[byte_cnt]; /* Its an xor */ @@ -322,7 +324,47 @@ return crc_bytes; } /* psa_crc */ -#endif /* PSA_CRC */ + +/*------------------------------------------------------------------*/ +/* + * update the checksum field in the Wavelan's PSA + */ +static void +update_psa_checksum(device * dev, + u_long ioaddr, + u_short hacr) +{ + psa_t psa; + u_short crc; + + /* read the parameter storage area */ + psa_read(ioaddr, hacr, 0, (unsigned char *) &psa, sizeof(psa)); + + /* update the checksum */ + crc = psa_crc((unsigned char *) &psa, + sizeof(psa) - sizeof(psa.psa_crc[0]) - sizeof(psa.psa_crc[1]) + - sizeof(psa.psa_crc_status)); + + psa.psa_crc[0] = crc & 0xFF; + psa.psa_crc[1] = (crc & 0xFF00) >> 8; + + /* Write it ! */ + psa_write(ioaddr, hacr, (char *)&psa.psa_crc - (char *)&psa, + (unsigned char *)&psa.psa_crc, 2); + +#ifdef DEBUG_IOCTL_INFO + printk (KERN_DEBUG "%s: update_psa_checksum(): crc = 0x%02x%02x\n", + dev->name, psa.psa_crc[0], psa.psa_crc[1]); + + /* Check again (luxury !) */ + crc = psa_crc ((unsigned char *) &psa, + sizeof(psa) - sizeof(psa.psa_crc_status)); + + if(crc != 0) + printk(KERN_WARNING "%s: update_psa_checksum(): CRC does not agree with PSA data (even after recalculating)\n", dev->name); +#endif /* DEBUG_IOCTL_INFO */ +} /* update_psa_checksum */ +#endif /* SET_PSA_CRC */ /*------------------------------------------------------------------*/ /* @@ -706,21 +748,21 @@ unsigned short ias_addr; /* Check mc_config command */ - if(status & AC_SFLD_OK != 0) + if((status & AC_SFLD_OK) != 0) printk(KERN_INFO "wv_config_complete(): set_multicast_address failed; status = 0x%x\n", dev->name, str, status); /* check ia-config command */ ias_addr = mcs_addr - sizeof(ac_ias_t); obram_read(ioaddr, acoff(ias_addr, ac_status), (unsigned char *)&status, sizeof(status)); - if(status & AC_SFLD_OK != 0) + if((status & AC_SFLD_OK) != 0) printk(KERN_INFO "wv_config_complete(): set_MAC_address; status = 0x%x\n", dev->name, str, status); /* Check config command. */ cfg_addr = ias_addr - sizeof(ac_cfg_t); obram_read(ioaddr, acoff(cfg_addr, ac_status), (unsigned char *)&status, sizeof(status)); - if(status & AC_SFLD_OK != 0) + if((status & AC_SFLD_OK) != 0) printk(KERN_INFO "wv_config_complete(): configure; status = 0x%x\n", dev->name, str, status); #endif /* DEBUG_CONFIG_ERROR */ @@ -1890,6 +1932,10 @@ /* Disable NWID in the mmc (no filtering). */ mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel), MMW_LOOPT_SEL_DIS_NWID); } +#ifdef SET_PSA_CRC + /* update the Wavelan checksum */ + update_psa_checksum(dev, ioaddr, lp->hacr); +#endif break; case SIOCGIWNWID: @@ -1946,6 +1992,10 @@ psa.psa_thr_pre_set = wrq->u.sensitivity & 0x3F; psa_write(ioaddr, lp->hacr, (char *)&psa.psa_thr_pre_set - (char *)&psa, (unsigned char *) &psa.psa_thr_pre_set, 1); +#ifdef SET_PSA_CRC + /* update the Wavelan checksum */ + update_psa_checksum(dev, ioaddr, lp->hacr); +#endif mmc_out(ioaddr, mmwoff(0, mmw_thr_pre_set), psa.psa_thr_pre_set); break; @@ -1993,6 +2043,10 @@ mmc_out(ioaddr, mmwoff(0, mmw_encr_enable), 0); } +#ifdef SET_PSA_CRC + /* update the Wavelan checksum */ + update_psa_checksum(dev, ioaddr, lp->hacr); +#endif break; case SIOCGIWENCODE: @@ -2206,6 +2260,10 @@ psa.psa_quality_thr = *(wrq->u.name) & 0x0F; psa_write(ioaddr, lp->hacr, (char *)&psa.psa_quality_thr - (char *)&psa, (unsigned char *)&psa.psa_quality_thr, 1); +#ifdef SET_PSA_CRC + /* update the Wavelan checksum */ + update_psa_checksum(dev, ioaddr, lp->hacr); +#endif mmc_out(ioaddr, mmwoff(0, mmw_quality_thr), psa.psa_quality_thr); break; @@ -2316,7 +2374,7 @@ mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0); /* Copy data to wireless stuff. */ - wstats->status = m.mmr_dce_status; + wstats->status = m.mmr_dce_status & MMR_DCE_STATUS; wstats->qual.qual = m.mmr_sgnl_qual & MMR_SGNL_QUAL; wstats->qual.level = m.mmr_signal_lvl & MMR_SIGNAL_LVL; wstats->qual.noise = m.mmr_silence_lvl & MMR_SILENCE_LVL; @@ -2892,6 +2950,10 @@ (unsigned char *)&psa.psa_quality_thr, 1); psa_write(ioaddr, lp->hacr, (char *)&psa.psa_conf_status - (char *)&psa, (unsigned char *)&psa.psa_conf_status, 1); +#ifdef SET_PSA_CRC + /* update the Wavelan checksum */ + update_psa_checksum(dev, ioaddr, lp->hacr); +#endif #endif } @@ -2918,21 +2980,18 @@ m.mmw_thr_pre_set = psa.psa_thr_pre_set & 0x3F; m.mmw_quality_thr = psa.psa_quality_thr & 0x0F; - /* Encryption stuff is missing. */ - /* * Set default modem control parameters. * See NCR document 407-0024326 Rev. A. */ m.mmw_jabber_enable = 0x01; + m.mmw_freeze = 0; m.mmw_anten_sel = MMW_ANTEN_SEL_ALG_EN; m.mmw_ifs = 0x20; m.mmw_mod_delay = 0x04; m.mmw_jam_time = 0x38; - m.mmw_encr_enable = 0; m.mmw_des_io_invert = 0; - m.mmw_freeze = 0; m.mmw_decay_prm = 0; m.mmw_decay_updat_prm = 0; @@ -3398,39 +3457,29 @@ /* Create a configure action. */ memset(&cfg, 0x00, sizeof(cfg)); -#if 0 - /* - * The default board configuration - */ - cfg.fifolim_bytecnt = 0x080c; - cfg.addrlen_mode = 0x2600; - cfg.linprio_interframe = 0x7820; /* IFS=120, ACS=2 */ - cfg.slot_time = 0xf00c; /* slottime=12 */ - cfg.hardware = 0x0008; /* tx even without CD */ - cfg.min_frame_len = 0x0040; -#endif /* 0 */ - /* * For Linux we invert AC_CFG_ALOC() so as to conform * to the way that net packets reach us from above. * (See also ac_tx_t.) + * + * Updated from Wavelan Manual WCIN085B */ cfg.cfg_byte_cnt = AC_CFG_BYTE_CNT(sizeof(ac_cfg_t) - sizeof(ach_t)); - cfg.cfg_fifolim = AC_CFG_FIFOLIM(8); - cfg.cfg_byte8 = AC_CFG_SAV_BF(0) | + cfg.cfg_fifolim = AC_CFG_FIFOLIM(4); + cfg.cfg_byte8 = AC_CFG_SAV_BF(1) | AC_CFG_SRDY(0); cfg.cfg_byte9 = AC_CFG_ELPBCK(0) | AC_CFG_ILPBCK(0) | AC_CFG_PRELEN(AC_CFG_PLEN_2) | AC_CFG_ALOC(1) | AC_CFG_ADDRLEN(WAVELAN_ADDR_SIZE); - cfg.cfg_byte10 = AC_CFG_BOFMET(0) | - AC_CFG_ACR(0) | + cfg.cfg_byte10 = AC_CFG_BOFMET(1) | + AC_CFG_ACR(6) | AC_CFG_LINPRIO(0); - cfg.cfg_ifs = 32; - cfg.cfg_slotl = 0; + cfg.cfg_ifs = 0x20; + cfg.cfg_slotl = 0x0C; cfg.cfg_byte13 = AC_CFG_RETRYNUM(15) | - AC_CFG_SLTTMHI(2); + AC_CFG_SLTTMHI(0); cfg.cfg_byte14 = AC_CFG_FLGPAD(0) | AC_CFG_BTSTF(0) | AC_CFG_CRC16(0) | @@ -4016,6 +4065,10 @@ #endif psa_write(ioaddr, HACR_DEFAULT, psaoff(0, psa_int_req_no), &irq_mask, 1); +#ifdef SET_PSA_CRC + /* update the Wavelan checksum */ + update_psa_checksum(dev, ioaddr, HACR_DEFAULT); +#endif wv_hacr_reset(ioaddr); } } @@ -4196,7 +4249,7 @@ init_module(void) { mac_addr mac; /* MAC address (check WaveLAN existence) */ - int ret = 0; + int ret = -EIO; /* Return error if no cards found */ int i; #ifdef DEBUG_MODULE_TRACE @@ -4241,7 +4294,11 @@ /* Deallocate everything. */ /* Note: if dev->priv is mallocated, there is no way to fail. */ kfree_s(dev, sizeof(struct device)); - ret = -EIO; + } + else + { + /* If at least one device OK, we do not fail */ + ret = 0; } } /* if there is something at the address */ } /* Loop on all addresses. */ diff -u --recursive --new-file v2.2.3/linux/drivers/net/wavelan.h linux/drivers/net/wavelan.h --- v2.2.3/linux/drivers/net/wavelan.h Tue Jul 28 14:21:08 1998 +++ linux/drivers/net/wavelan.h Wed Mar 10 16:51:35 1999 @@ -28,6 +28,8 @@ { { 0x08, 0x00, 0x0E }, /* AT&T WaveLAN (standard) & DEC RoamAbout */ { 0x08, 0x00, 0x6A }, /* AT&T WaveLAN (alternate) */ + { 0x00, 0x00, 0xE1 }, /* Hitachi Wavelan */ + { 0x00, 0x60, 0x1D } /* Lucent Wavelan (another one) */ /* Add your card here and send me the patch! */ }; @@ -293,6 +295,7 @@ #define MMR_DCE_STATUS_LOOPT_IND 0x02 /* loop test indicated */ #define MMR_DCE_STATUS_TX_BUSY 0x04 /* transmitter on */ #define MMR_DCE_STATUS_JBR_EXPIRED 0x08 /* jabber timer expired */ +#define MMR_DCE_STATUS 0x0F /* mask to get the bits */ unsigned char mmr_dsp_id; /* DSP ID (AA = Daedalus rev A) */ unsigned char mmr_unused2[2]; /* unused */ unsigned char mmr_correct_nwid_l; /* # of correct NWIDs rxd (low) */ diff -u --recursive --new-file v2.2.3/linux/drivers/net/wavelan.p.h linux/drivers/net/wavelan.p.h --- v2.2.3/linux/drivers/net/wavelan.p.h Tue Jul 28 14:21:08 1998 +++ linux/drivers/net/wavelan.p.h Wed Mar 10 16:51:35 1999 @@ -18,13 +18,7 @@ * This driver provides a Linux interface to the WaveLAN ISA hardware. * The WaveLAN is a product of Lucent (http://www.wavelan.com/). * This division was formerly part of NCR and then AT&T. - * WaveLANs are also distributed by DEC (RoamAbout), Digital Ocean and - * Aironet (Arlan). If you have one of those products, you will need to - * make some changes below. - * - * This driver is still beta software. A lot of bugs have been corrected, - * a lot of functionality is implemented, and the whole appears stable, - * but there is still room for improvement (encryption, performance). + * WaveLANs are also distributed by DEC (RoamAbout DS) and Digital Ocean. * * To learn how to use this driver, read the NET3 HOWTO. * If you want to exploit the many other functionalities, read the comments @@ -35,12 +29,22 @@ /* ------------------------ SPECIFIC NOTES ------------------------ */ /* + * Web page + * -------- + * I try to maintain a web page with the Wireless LAN Howto at : + * http://www-uk.hpl.hp.com/people/jt/Linux/Wavelan.html + * * wavelan.o is too darned big * --------------------------- * That's true! There is a very simple way to reduce the driver * object by 33%! Comment out the following line: * #include * + * Debugging and options + * --------------------- + * You will find below a set of '#define" allowing a very fine control + * on the driver behaviour and the debug messages printed. + * * MAC address and hardware detection: * ----------------------------------- * The detection code for the WaveLAN checks that the first three @@ -60,23 +64,6 @@ * 3) Compile and verify * 4) Send me the MAC code. I will include it in the next version. * - * "CU Inactive" message at boot up: - * ----------------------------------- - * It seems that there is some weird timing problem with the - * Intel microcontroller. In fact, this message is triggered by a - * bad reading of the onboard RAM the first time we read the - * control block. If you ignore this message, all is OK (but in - * fact, currently, it resets the WaveLAN hardware). - * - * There are two ways to get rid of that problem. The first - * is to add a dummy read of the scb at the end of - * wv_82586_config. The second is to add the timers - * wv_synchronous_cmd and wv_ack (the udelay just after the - * waiting loops--it seems that the controller is not totally ready - * when it says it is). - * - * In the current code, I use the second solution (to be - * consistent with the original solution of Bruce Janson). */ /* --------------------- WIRELESS EXTENSIONS --------------------- */ @@ -273,6 +260,22 @@ * - encryption setting from Brent Elphick (thanks a lot!) * - 'ioaddr' to 'u_long' for the Alpha (thanks to Stanislav Sinyagin) * + * Other changes (not by me) : + * ------------------------- + * - Spelling and gramar "rectification". + * + * Changes made for release in 2.0.37 & 2.2.2 : + * ------------------------------------------ + * - Correct status in /proc/net/wireless + * - Set PSA CRC to make PtP diagnostic tool happy (Bob Gray) + * - Module init code don't fail if we found at least one card in + * the address list (Karlis Peisenieks) + * - Missing parenthesis (Christopher Peterson) + * - Correct i82586 configuration parameters + * - Encryption initialisation bug (Robert McCormack) + * - New mac addresses detected in the probe + * - Increase watchdog for busy envirnoments + * * Wishes & dreams: * ---------------- * - roaming @@ -342,9 +345,9 @@ /* Options */ #define USE_PSA_CONFIG /* Use info from the PSA. */ +#define SET_PSA_CRC /* Calculate and set the CRC on PSA */ #define IGNORE_NORMAL_XMIT_ERRS /* Don't bother with normal conditions. */ #undef STRUCT_CHECK /* Verify padding of structures. */ -#undef PSA_CRC /* Check CRC in PSA. */ #undef OLDIES /* old code (to redo) */ #undef RECORD_SNR /* to redo */ #undef EEPROM_IS_PROTECTED /* doesn't seem to be necessary */ @@ -359,11 +362,11 @@ /************************ CONSTANTS & MACROS ************************/ #ifdef DEBUG_VERSION_SHOW -static const char *version = "wavelan.c : v16 (wireless extensions) 17/4/97\n"; +static const char *version = "wavelan.c : v18 (wireless extensions) 18/2/99\n"; #endif /* Watchdog temporisation */ -#define WATCHDOG_JIFFIES 32 /* TODO: express in HZ. */ +#define WATCHDOG_JIFFIES 256 /* TODO: express in HZ. */ /* Macro to get the number of elements in an array */ #define NELS(a) (sizeof(a) / sizeof(a[0])) diff -u --recursive --new-file v2.2.3/linux/drivers/pci/oldproc.c linux/drivers/pci/oldproc.c --- v2.2.3/linux/drivers/pci/oldproc.c Tue Jan 19 11:32:51 1999 +++ linux/drivers/pci/oldproc.c Sun Mar 21 09:28:24 1999 @@ -101,6 +101,7 @@ DEVICE( DEC, DEC_21052, "DC21052"), DEVICE( DEC, DEC_21150, "DC21150"), DEVICE( DEC, DEC_21152, "DC21152"), + DEVICE( DEC, DEC_21153, "DC21153"), DEVICE( CIRRUS, CIRRUS_7548, "GD 7548"), DEVICE( CIRRUS, CIRRUS_5430, "GD 5430"), DEVICE( CIRRUS, CIRRUS_5434_4, "GD 5434"), @@ -120,6 +121,7 @@ DEVICE( IBM, IBM_82G2675, "82G2675"), DEVICE( IBM, IBM_MCA, "MicroChannel"), DEVICE( IBM, IBM_82351, "82351"), + DEVICE( IBM, IBM_PYTHON, "Python"), DEVICE( IBM, IBM_SERVERAID, "ServeRAID"), DEVICE( IBM, IBM_TR_WAKE, "Wake On LAN Token Ring"), DEVICE( IBM, IBM_MPIC, "MPIC-2 Interrupt Controller"), diff -u --recursive --new-file v2.2.3/linux/drivers/sbus/audio/audio.c linux/drivers/sbus/audio/audio.c --- v2.2.3/linux/drivers/sbus/audio/audio.c Thu Nov 19 09:56:28 1998 +++ linux/drivers/sbus/audio/audio.c Sun Mar 21 07:23:38 1999 @@ -136,7 +136,7 @@ drv->output_buffer = kmalloc((drv->output_buffer_size * drv->num_output_buffers), - GFP_KERNEL); + (GFP_DMA | GFP_KERNEL)); if (!drv->output_buffer) goto kmalloc_failed2; /* Allocate the pages for each output buffer. */ @@ -167,7 +167,7 @@ if (duplex == 1) { drv->input_buffer = kmalloc((drv->input_buffer_size * drv->num_input_buffers), - GFP_KERNEL); + (GFP_DMA | GFP_KERNEL)); if (!drv->input_buffer) goto kmalloc_failed4; for (i = 0; i < drv->num_input_buffers; i++) { @@ -634,7 +634,29 @@ SPARCAUDIO_DEVICE_SHIFT)]; unsigned long i = 0, j = 0, k = 0; - k = (unsigned long) &arg; + k = arg; + + if(cmd == SOUND_MIXER_INFO) { + audio_device_t tmp; + mixer_info info; + int retval = -EINVAL; + + if(drv->ops->sunaudio_getdev) { + drv->ops->sunaudio_getdev(drv, &tmp); + memset(&info, 0, sizeof(info)); + strncpy(info.id, tmp.name, sizeof(info.id)); + strncpy(info.name, "Sparc Audio", sizeof(info.name)); + + /* XXX do this right... */ + info.modify_counter = 0; + + if(copy_to_user((char *)arg, &info, sizeof(info))) + retval = -EFAULT; + else + retval = 0; + } + return retval; + } switch (cmd) { case SOUND_MIXER_WRITE_RECLEV: @@ -1022,12 +1044,14 @@ break; } } else if (k == 16) { + switch (j) { case AUDIO_ENCODING_LINEAR: i = AFMT_S16_BE; break; case AUDIO_ENCODING_LINEARLE: i = AFMT_S16_LE; break; + } } COPY_OUT(arg, i); break; @@ -1872,6 +1896,11 @@ /* A low-level audio driver must exist. */ if (!drv) return -ENODEV; + +#ifdef S_ZERO_WR + /* This is how 2.0 ended up dealing with 0 len writes */ + inode->i_flags |= S_ZERO_WR; +#endif switch (minor & 0xf) { case SPARCAUDIO_AUDIOCTL_MINOR: diff -u --recursive --new-file v2.2.3/linux/drivers/sbus/audio/cs4215.h linux/drivers/sbus/audio/cs4215.h --- v2.2.3/linux/drivers/sbus/audio/cs4215.h Mon Oct 5 13:13:40 1998 +++ linux/drivers/sbus/audio/cs4215.h Mon Mar 15 16:11:30 1999 @@ -13,8 +13,9 @@ __u8 ctrl[4]; /* Ctrl mode: Time slots 1-4 */ __volatile__ struct dbri_mem td; __volatile__ struct dbri_mem rd; - __u8 version; __u8 onboard; + __u32 status; + __u32 version; }; diff -u --recursive --new-file v2.2.3/linux/drivers/sbus/audio/cs4231.c linux/drivers/sbus/audio/cs4231.c --- v2.2.3/linux/drivers/sbus/audio/cs4231.c Fri Nov 27 13:09:24 1998 +++ linux/drivers/sbus/audio/cs4231.c Mon Mar 15 16:11:30 1999 @@ -14,6 +14,7 @@ * The APC DMA controller support unfortunately is not documented. Thanks, Sun */ +#include #include #include #include @@ -24,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -31,6 +33,10 @@ #include #include #include +#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x200ff && defined(CONFIG_PCI) +#define EB4231_SUPPORT +#include +#endif #include #include "cs4231.h" @@ -120,6 +126,7 @@ tprintk(("enabling play\n")); save_flags(flags); cli(); + cs4231_chip->regs->iar = 0x9; cs4231_chip->regs->idr |= PEN_ENABLE; restore_flags(flags); @@ -261,14 +268,14 @@ if (value != 0) { set_bits = cs4231_encoding_to_bits(drv, value); if (set_bits >= 0) { - cs4231_chip->regs->iar = IAR_AUTOCAL_BEGIN | 0x8; - tmp_bits = cs4231_chip->regs->idr; - cs4231_chip->regs->idr = CHANGE_ENCODING(tmp_bits, set_bits); + cs4231_chip->regs->iar = IAR_AUTOCAL_BEGIN | 0x8; + tmp_bits = cs4231_chip->regs->idr; + cs4231_chip->regs->idr = CHANGE_ENCODING(tmp_bits, set_bits); - CHIP_READY + CHIP_READY - cs4231_chip->perchip_info.play.encoding = value; - return 0; + cs4231_chip->perchip_info.play.encoding = value; + return 0; } } dprintk(("output enc failed\n")); @@ -291,14 +298,14 @@ if (value != 0) { set_bits = cs4231_encoding_to_bits(drv, value); if (set_bits >= 0) { - cs4231_chip->regs->iar = IAR_AUTOCAL_BEGIN | 0x1c; - tmp_bits = cs4231_chip->regs->idr; - cs4231_chip->regs->idr = CHANGE_ENCODING(tmp_bits, set_bits); + cs4231_chip->regs->iar = IAR_AUTOCAL_BEGIN | 0x1c; + tmp_bits = cs4231_chip->regs->idr; + cs4231_chip->regs->idr = CHANGE_ENCODING(tmp_bits, set_bits); - CHIP_READY + CHIP_READY - cs4231_chip->perchip_info.record.encoding = value; - return 0; + cs4231_chip->perchip_info.record.encoding = value; + return 0; } } dprintk(("input enc failed\n")); @@ -321,14 +328,14 @@ if (value != 0) { set_bits = cs4231_rate_to_bits(drv, value); if (set_bits >= 0) { - cs4231_chip->regs->iar = IAR_AUTOCAL_BEGIN | 0x8; - tmp_bits = cs4231_chip->regs->idr; - cs4231_chip->regs->idr = CHANGE_DFR(tmp_bits, set_bits); + cs4231_chip->regs->iar = IAR_AUTOCAL_BEGIN | 0x8; + tmp_bits = cs4231_chip->regs->idr; + cs4231_chip->regs->idr = CHANGE_DFR(tmp_bits, set_bits); - CHIP_READY + CHIP_READY - cs4231_chip->perchip_info.play.sample_rate = value; - return 0; + cs4231_chip->perchip_info.play.sample_rate = value; + return 0; } } dprintk(("output rate failed\n")); @@ -351,14 +358,14 @@ if (value != 0) { set_bits = cs4231_rate_to_bits(drv, value); if (set_bits >= 0) { - cs4231_chip->regs->iar = IAR_AUTOCAL_BEGIN | 0x1c; - tmp_bits = cs4231_chip->regs->idr; - cs4231_chip->regs->idr = CHANGE_DFR(tmp_bits, set_bits); + cs4231_chip->regs->iar = IAR_AUTOCAL_BEGIN | 0x1c; + tmp_bits = cs4231_chip->regs->idr; + cs4231_chip->regs->idr = CHANGE_DFR(tmp_bits, set_bits); - CHIP_READY + CHIP_READY - cs4231_chip->perchip_info.record.sample_rate = value; - return 0; + cs4231_chip->perchip_info.record.sample_rate = value; + return 0; } } dprintk(("input rate failed\n")); @@ -383,14 +390,14 @@ tmp_bits = cs4231_chip->regs->idr; switch (value) { case 1: - cs4231_chip->regs->idr = CS4231_MONO_ON(tmp_bits); - break; + cs4231_chip->regs->idr = CS4231_MONO_ON(tmp_bits); + break; case 2: - cs4231_chip->regs->idr = CS4231_STEREO_ON(tmp_bits); - break; + cs4231_chip->regs->idr = CS4231_STEREO_ON(tmp_bits); + break; default: - dprintk(("input chan failed\n")); - return -(EINVAL); + dprintk(("input chan failed\n")); + return -(EINVAL); } CHIP_READY @@ -417,14 +424,14 @@ tmp_bits = cs4231_chip->regs->idr; switch (value) { case 1: - cs4231_chip->regs->idr = CS4231_MONO_ON(tmp_bits); - break; + cs4231_chip->regs->idr = CS4231_MONO_ON(tmp_bits); + break; case 2: - cs4231_chip->regs->idr = CS4231_STEREO_ON(tmp_bits); - break; + cs4231_chip->regs->idr = CS4231_STEREO_ON(tmp_bits); + break; default: - dprintk(("output chan failed\n")); - return -(EINVAL); + dprintk(("output chan failed\n")); + return -(EINVAL); } CHIP_READY @@ -477,7 +484,7 @@ unsigned int x = 0; cs4231_chip->regs->iar = IAR_AUTOCAL_END; - while (cs4231_chip->regs->iar == IAR_NOT_READY && x <= CS_TIMEOUT) { + while (cs4231_chip->regs->idr == IAR_NOT_READY && x <= CS_TIMEOUT) { x++; } @@ -494,17 +501,17 @@ struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; tprintk(("in cs4231_output_muted: %d\n", value)); if (!value) { - cs4231_chip->regs->iar = 0x7; - cs4231_chip->regs->idr &= OUTCR_UNMUTE; - cs4231_chip->regs->iar = 0x6; - cs4231_chip->regs->idr &= OUTCR_UNMUTE; - cs4231_chip->perchip_info.output_muted = 0; + cs4231_chip->regs->iar = 0x7; + cs4231_chip->regs->idr &= OUTCR_UNMUTE; + cs4231_chip->regs->iar = 0x6; + cs4231_chip->regs->idr &= OUTCR_UNMUTE; + cs4231_chip->perchip_info.output_muted = 0; } else { - cs4231_chip->regs->iar = 0x7; - cs4231_chip->regs->idr |= OUTCR_MUTE; - cs4231_chip->regs->iar = 0x6; - cs4231_chip->regs->idr |= OUTCR_MUTE; - cs4231_chip->perchip_info.output_muted = 1; + cs4231_chip->regs->iar = 0x7; + cs4231_chip->regs->idr |= OUTCR_MUTE; + cs4231_chip->regs->iar = 0x6; + cs4231_chip->regs->idr |= OUTCR_MUTE; + cs4231_chip->perchip_info.output_muted = 1; } return 0; } @@ -555,21 +562,21 @@ cs4231_chip->regs->idr |= PINCR_HDPH_MUTE; if (value & AUDIO_SPEAKER) { - cs4231_chip->regs->iar = 0x1a; - cs4231_chip->regs->idr &= ~MONO_IOCR_MUTE; - retval |= AUDIO_SPEAKER; + cs4231_chip->regs->iar = 0x1a; + cs4231_chip->regs->idr &= ~MONO_IOCR_MUTE; + retval |= AUDIO_SPEAKER; } if (value & AUDIO_HEADPHONE) { - cs4231_chip->regs->iar = 0x0a; - cs4231_chip->regs->idr &= ~PINCR_HDPH_MUTE; - retval |= AUDIO_HEADPHONE; + cs4231_chip->regs->iar = 0x0a; + cs4231_chip->regs->idr &= ~PINCR_HDPH_MUTE; + retval |= AUDIO_HEADPHONE; } if (value & AUDIO_LINE_OUT) { - cs4231_chip->regs->iar = 0x0a; - cs4231_chip->regs->idr &= ~PINCR_LINE_MUTE; - retval |= AUDIO_LINE_OUT; + cs4231_chip->regs->iar = 0x0a; + cs4231_chip->regs->idr &= ~PINCR_LINE_MUTE; + retval |= AUDIO_LINE_OUT; } cs4231_chip->perchip_info.play.port = retval; @@ -600,31 +607,31 @@ /* This apparently applies only to APC ultras, not ebus ultras */ if (!cs4231_chip->status & CS_STATUS_IS_ULTRA) { if (value & AUDIO_INTERNAL_CD_IN) { - cs4231_chip->regs->iar = 0x1; - cs4231_chip->regs->idr = CDROM_ENABLE(cs4231_chip->regs->idr); - cs4231_chip->regs->iar = 0x0; - cs4231_chip->regs->idr = CDROM_ENABLE(cs4231_chip->regs->idr); - retval = AUDIO_INTERNAL_CD_IN; + cs4231_chip->regs->iar = 0x1; + cs4231_chip->regs->idr = CDROM_ENABLE(cs4231_chip->regs->idr); + cs4231_chip->regs->iar = 0x0; + cs4231_chip->regs->idr = CDROM_ENABLE(cs4231_chip->regs->idr); + retval = AUDIO_INTERNAL_CD_IN; } } if ((value & AUDIO_LINE_IN)) { - cs4231_chip->regs->iar = 0x1; - cs4231_chip->regs->idr = LINE_ENABLE(cs4231_chip->regs->idr); - cs4231_chip->regs->iar = 0x0; - cs4231_chip->regs->idr = LINE_ENABLE(cs4231_chip->regs->idr); - retval = AUDIO_LINE_IN; + cs4231_chip->regs->iar = 0x1; + cs4231_chip->regs->idr = LINE_ENABLE(cs4231_chip->regs->idr); + cs4231_chip->regs->iar = 0x0; + cs4231_chip->regs->idr = LINE_ENABLE(cs4231_chip->regs->idr); + retval = AUDIO_LINE_IN; } else if (value & AUDIO_MICROPHONE) { - cs4231_chip->regs->iar = 0x1; - cs4231_chip->regs->idr = MIC_ENABLE(cs4231_chip->regs->idr); - cs4231_chip->regs->iar = 0x0; - cs4231_chip->regs->idr = MIC_ENABLE(cs4231_chip->regs->idr); - retval = AUDIO_MICROPHONE; + cs4231_chip->regs->iar = 0x1; + cs4231_chip->regs->idr = MIC_ENABLE(cs4231_chip->regs->idr); + cs4231_chip->regs->iar = 0x0; + cs4231_chip->regs->idr = MIC_ENABLE(cs4231_chip->regs->idr); + retval = AUDIO_MICROPHONE; } else if (value & AUDIO_ANALOG_LOOPBACK) { - cs4231_chip->regs->iar = 0x1; - cs4231_chip->regs->idr = OUTPUTLOOP_ENABLE(cs4231_chip->regs->idr); - cs4231_chip->regs->iar = 0x0; - cs4231_chip->regs->idr = OUTPUTLOOP_ENABLE(cs4231_chip->regs->idr); - retval = AUDIO_ANALOG_LOOPBACK; + cs4231_chip->regs->iar = 0x1; + cs4231_chip->regs->idr = OUTPUTLOOP_ENABLE(cs4231_chip->regs->idr); + cs4231_chip->regs->iar = 0x0; + cs4231_chip->regs->idr = OUTPUTLOOP_ENABLE(cs4231_chip->regs->idr); + retval = AUDIO_ANALOG_LOOPBACK; } cs4231_chip->perchip_info.record.port = retval; @@ -654,9 +661,9 @@ cs4231_chip->regs->iar = 0x0d; if (a >= CS4231_MON_MAX_ATEN) - cs4231_chip->regs->idr = LOOPB_OFF; + cs4231_chip->regs->idr = LOOPB_OFF; else - cs4231_chip->regs->idr = ((a << 2) | LOOPB_ON); + cs4231_chip->regs->idr = ((a << 2) | LOOPB_ON); if (value == AUDIO_MAX_GAIN) cs4231_chip->perchip_info.monitor_gain = AUDIO_MAX_GAIN; @@ -689,6 +696,32 @@ return (int)cs4231_chip->perchip_info.record.error; } +#ifdef EB4231_SUPPORT +static int eb4231_get_output_samples(struct sparcaudio_driver *drv) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + int count = + cs4231_length_to_samplecount(&cs4231_chip->perchip_info.play, + readl(&cs4231_chip->eb2p->dbcr)); + + return (cs4231_chip->perchip_info.play.samples - + ((count > cs4231_chip->perchip_info.play.samples) + ? 0 : count)); +} + +static int eb4231_get_input_samples(struct sparcaudio_driver *drv) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + int count = + cs4231_length_to_samplecount(&cs4231_chip->perchip_info.record, + readl(&cs4231_chip->eb2c->dbcr)); + + return (cs4231_chip->perchip_info.record.samples - + ((count > cs4231_chip->perchip_info.record.samples) ? + 0 : count)); +} +#endif + static int cs4231_get_output_samples(struct sparcaudio_driver *drv) { struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; @@ -890,13 +923,21 @@ tprintk(("in cs4231_chip_reset\n")); - cs4231_chip->regs->dmacsr = CS_CHIP_RESET; - cs4231_chip->regs->dmacsr = 0x00; - cs4231_chip->regs->dmacsr |= CS_CDC_RESET; + if (cs4231_chip->status & CS_STATUS_IS_EBUS) { +#ifdef EB4231_SUPPORT + writel(EBUS_DCSR_RESET, &cs4231_chip->eb2p->dcsr); + writel(EBUS_DCSR_RESET, &cs4231_chip->eb2c->dcsr); +#endif + } else { + cs4231_chip->regs->dmacsr = APC_CHIP_RESET; + cs4231_chip->regs->dmacsr = 0x00; + cs4231_chip->regs->dmacsr |= APC_CDC_RESET; - udelay(20); + udelay(20); - cs4231_chip->regs->dmacsr &= ~(CS_CDC_RESET); + cs4231_chip->regs->dmacsr &= ~(APC_CDC_RESET); + } + cs4231_chip->regs->iar |= IAR_AUTOCAL_BEGIN; CHIP_READY @@ -928,9 +969,9 @@ cs4231_chip->regs->iar = IAR_AUTOCAL_BEGIN | 0x11; if (cs4231_chip->status & CS_STATUS_REV_A) - cs4231_chip->regs->idr = (HPF_ON | XTALE_ON); + cs4231_chip->regs->idr = (HPF_ON | XTALE_ON); else - cs4231_chip->regs->idr = (HPF_ON); + cs4231_chip->regs->idr = HPF_ON; cs4231_chip->regs->iar = IAR_AUTOCAL_BEGIN | 0x1a; cs4231_chip->regs->idr = 0x00; @@ -990,6 +1031,36 @@ return count; } +#ifdef EB4231_SUPPORT +static void eb4231_getsamplecount(struct sparcaudio_driver *drv, unsigned int length, unsigned int direction) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + struct audio_prinfo *thisdir; + unsigned int count, nextcount, curcount; + + if (direction == 1) /* record */ + { + thisdir = &cs4231_chip->perchip_info.record; + curcount = + cs4231_length_to_samplecount(thisdir, readl(&cs4231_chip->eb2c->dbcr)); + nextcount = + cs4231_length_to_samplecount(thisdir, 0/*cs4231_chip->regs->dmacnc*/); + } + else /* play */ + { + thisdir = &cs4231_chip->perchip_info.play; + curcount = + cs4231_length_to_samplecount(thisdir, readl(&cs4231_chip->eb2p->dbcr)); + nextcount = + cs4231_length_to_samplecount(thisdir, 0/*cs4231_chip->regs->dmapnc*/); + } + count = thisdir->samples; + length = cs4231_length_to_samplecount(thisdir, length); + /* normalize for where we are. */ + thisdir->samples = ((count - nextcount) + (length - curcount)); +} +#endif + static void cs4231_getsamplecount(struct sparcaudio_driver *drv, unsigned int length, unsigned int direction) { struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; @@ -1060,14 +1131,18 @@ /* stop capture here or midlevel? */ cs4231_chip->perchip_info.record.open = 0; if (cs4231_chip->input_dma_handle) { - mmu_release_scsi_one((u32)((unsigned long)cs4231_chip->input_dma_handle), +#if 0 + mmu_release_scsi_one(cs4231_chip->input_dma_handle, cs4231_chip->input_dma_size, drv->dev->my_bus); +#endif cs4231_chip->input_dma_handle = 0; cs4231_chip->input_dma_size = 0; } if (cs4231_chip->input_next_dma_handle) { - mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->input_next_dma_handle), +#if 0 + mmu_release_scsi_one(cs4231_chip->input_next_dma_handle, cs4231_chip->input_next_dma_size, drv->dev->my_bus); +#endif cs4231_chip->input_next_dma_handle = 0; cs4231_chip->input_next_dma_size = 0; } @@ -1077,15 +1152,19 @@ cs4231_chip->perchip_info.play.active = cs4231_chip->perchip_info.play.open = 0; if (cs4231_chip->output_dma_handle) { - mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->output_dma_handle), +#if 0 + mmu_release_scsi_one(cs4231_chip->output_dma_handle, cs4231_chip->output_dma_size, drv->dev->my_bus); +#endif cs4231_chip->output_dma_handle = 0; cs4231_chip->output_dma_size = 0; } if (cs4231_chip->output_next_dma_handle) { - mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->output_next_dma_handle), +#if 0 + mmu_release_scsi_one(cs4231_chip->output_next_dma_handle, cs4231_chip->output_next_dma_size, drv->dev->my_bus); +#endif cs4231_chip->output_next_dma_handle = 0; cs4231_chip->output_next_dma_size = 0; } @@ -1110,7 +1189,7 @@ cs4231_chip->playlen = cs4231_chip->output_size; if (cs4231_chip->output_dma_handle) { - mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->output_dma_handle), + mmu_release_scsi_one(cs4231_chip->output_dma_handle, cs4231_chip->output_dma_size, drv->dev->my_bus); cs4231_chip->output_dma_handle = 0; cs4231_chip->output_dma_size = 0; @@ -1126,11 +1205,10 @@ if ((cs4231_chip->output_ptr && cs4231_chip->output_size > 0) && !(cs4231_chip->perchip_info.play.pause)) { - cs4231_chip->output_next_dma_handle = (u32 *) (unsigned long) + cs4231_chip->output_next_dma_handle = mmu_get_scsi_one((char *) cs4231_chip->output_ptr, cs4231_chip->output_size, drv->dev->my_bus); - cs4231_chip->regs->dmapnva = (u32) (unsigned long) - cs4231_chip->output_next_dma_handle; + cs4231_chip->regs->dmapnva = cs4231_chip->output_next_dma_handle; cs4231_chip->output_next_dma_size = cs4231_chip->regs->dmapnc = cs4231_chip->output_size; cs4231_chip->output_size = 0; @@ -1147,6 +1225,62 @@ return; } +#ifdef EB4231_SUPPORT +static void eb4231_playintr(struct sparcaudio_driver *drv) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + int status = 0; + unsigned int dcsr; + + dcsr = readl(&cs4231_chip->eb2p->dcsr); + + printk("pintr\ncsr 0x%x acr 0x%x bcr %d\n", readl(&cs4231_chip->eb2p->dcsr), readl(&cs4231_chip->eb2p->dacr), readl(&cs4231_chip->eb2p->dbcr)); + if (cs4231_chip->playlen == 0 && cs4231_chip->output_size > 0) + cs4231_chip->playlen = cs4231_chip->output_size; + + if (cs4231_chip->output_dma_handle) { + cs4231_chip->output_dma_handle = 0; + cs4231_chip->output_dma_size = 0; + cs4231_chip->playing_count--; + status++; + } + +#if 0 + if (cs4231_chip->output_next_dma_handle) { + cs4231_chip->output_dma_handle = cs4231_chip->output_next_dma_handle; + cs4231_chip->output_dma_size = cs4231_chip->output_next_dma_size; + cs4231_chip->output_next_dma_size = 0; + cs4231_chip->output_next_dma_handle = 0; + } +#endif + + if ((cs4231_chip->output_ptr && cs4231_chip->output_size > 0) && + !(cs4231_chip->perchip_info.play.pause)) { +#if 0 + if (dcsr & EBUS_DCSR_A_LOADED) { + cs4231_chip->output_next_dma_handle = virt_to_bus(cs4231_chip->output_ptr); + cs4231_chip->output_next_dma_size = cs4231_chip->output_size; + } else { +#endif + cs4231_chip->output_dma_handle = virt_to_bus(cs4231_chip->output_ptr); + cs4231_chip->output_dma_size = cs4231_chip->output_size; +#if 0 + } +#endif + writel(virt_to_bus(cs4231_chip->output_ptr), &cs4231_chip->eb2p->dacr); + writel(cs4231_chip->output_size, &cs4231_chip->eb2p->dbcr); + cs4231_chip->output_size = 0; + cs4231_chip->output_ptr = NULL; + cs4231_chip->playing_count++; + status+=2; + } + + sparcaudio_output_done(drv, status); + + return; +} +#endif + static void cs4231_recclear(int fmt, char *dmabuf, int length) { switch (fmt) { @@ -1174,7 +1308,7 @@ } if (cs4231_chip->input_dma_handle) { - mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->input_dma_handle), + mmu_release_scsi_one(cs4231_chip->input_dma_handle, cs4231_chip->input_dma_size, drv->dev->my_bus); cs4231_chip->input_dma_handle = 0; cs4231_chip->input_dma_size = 0; @@ -1192,11 +1326,10 @@ !(cs4231_chip->perchip_info.record.pause)) { cs4231_recclear(cs4231_chip->perchip_info.record.encoding, (char *)cs4231_chip->input_ptr, cs4231_chip->input_size); - cs4231_chip->input_next_dma_handle = (u32*) (unsigned long) + cs4231_chip->input_next_dma_handle = mmu_get_scsi_one((char *) cs4231_chip->input_ptr, cs4231_chip->input_size, drv->dev->my_bus); - cs4231_chip->regs->dmacnva = (u32) (unsigned long) - cs4231_chip->input_next_dma_handle; + cs4231_chip->regs->dmacnva = cs4231_chip->input_next_dma_handle; cs4231_chip->input_next_dma_size = cs4231_chip->regs->dmacnc = cs4231_chip->input_size; cs4231_chip->input_size = 0; @@ -1213,6 +1346,107 @@ return 1; } +#ifdef EB4231_SUPPORT +static int eb4231_recintr(struct sparcaudio_driver *drv) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + int status = 0; + + if (cs4231_chip->perchip_info.record.active == 0) { + dprintk(("going inactive\n")); + cs4231_disable_rec(drv); + } + + if (cs4231_chip->input_dma_handle) { +#if 0 + mmu_release_scsi_one(cs4231_chip->input_dma_handle, + cs4231_chip->input_dma_size, drv->dev->my_bus); +#endif + cs4231_chip->input_dma_handle = 0; + cs4231_chip->input_dma_size = 0; + cs4231_chip->recording_count--; + status++; + } + if (cs4231_chip->input_next_dma_handle) { + cs4231_chip->input_dma_handle = cs4231_chip->input_next_dma_handle; + cs4231_chip->input_dma_size = cs4231_chip->input_next_dma_size; + cs4231_chip->input_next_dma_size = 0; + cs4231_chip->input_next_dma_handle = 0; + } + + if ((cs4231_chip->input_ptr && cs4231_chip->input_size > 0) && + !(cs4231_chip->perchip_info.record.pause)) { + cs4231_recclear(cs4231_chip->perchip_info.record.encoding, + (char *)cs4231_chip->input_ptr, cs4231_chip->input_size); +#if 0 + cs4231_chip->input_next_dma_handle = + mmu_get_scsi_one((char *) cs4231_chip->input_ptr, + cs4231_chip->input_size, drv->dev->my_bus); + cs4231_chip->regs->dmacnva = cs4231_chip->input_next_dma_handle; + cs4231_chip->input_next_dma_size = cs4231_chip->regs->dmacnc = + cs4231_chip->input_size; +#else + cs4231_chip->input_next_dma_handle = cs4231_chip->eb2c->dacr = virt_to_bus(cs4231_chip->input_ptr); + cs4231_chip->input_next_dma_size = cs4231_chip->eb2c->dbcr = cs4231_chip->input_size; +#endif + cs4231_chip->input_size = 0; + cs4231_chip->input_ptr = NULL; + cs4231_chip->recording_count++; + status += 2; + } else { +#if 0 + cs4231_chip->regs->dmacnva = 0; + cs4231_chip->regs->dmacnc = 0; +#else + cs4231_chip->eb2c->dacr = 0; + cs4231_chip->eb2c->dbcr = 0; +#endif + } + + sparcaudio_input_done(drv, 1); + + return 1; +} +#endif + +#ifdef EB4231_SUPPORT +static void eb4231_start_output(struct sparcaudio_driver *drv, __u8 * buffer, + unsigned long count) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + unsigned int dcsr; + + dprintk(("in eb4231 start output act %d pau %d\n", cs4231_chip->perchip_info.play.active, cs4231_chip->perchip_info.play.pause)); + cs4231_chip->output_ptr = buffer; + cs4231_chip->output_size = count; + + if (cs4231_chip->perchip_info.play.active || + (cs4231_chip->perchip_info.play.pause)) + return; + + cs4231_ready(drv); + + cs4231_chip->perchip_info.play.active = 1; + cs4231_chip->playing_count = 0; + + dcsr = readl(&cs4231_chip->eb2p->dcsr); + if (!(dcsr & EBUS_DCSR_EN_DMA)) { + dprintk(("about to go setup\n")); + + dcsr = EB2_PLAY_SETUP; + writel(dcsr, (unsigned long)&cs4231_chip->eb2p->dcsr); + eb4231_playintr(drv); + dprintk(("enabling\n")); + cs4231_enable_play(drv); + + cs4231_ready(drv); + } else { + dprintk(("playing next block\n")); + eb4231_playintr(drv); + } +} +#endif + static void cs4231_start_output(struct sparcaudio_driver *drv, __u8 * buffer, unsigned long count) { @@ -1231,14 +1465,14 @@ cs4231_chip->perchip_info.play.active = 1; cs4231_chip->playing_count = 0; - if ((cs4231_chip->regs->dmacsr & CS_PPAUSE) || - !(cs4231_chip->regs->dmacsr & PDMA_READY)) { - cs4231_chip->regs->dmacsr &= ~CS_XINT_PLAY; - cs4231_chip->regs->dmacsr &= ~CS_PPAUSE; + if ((cs4231_chip->regs->dmacsr & APC_PPAUSE) || + !(cs4231_chip->regs->dmacsr & APC_PDMA_READY)) { + cs4231_chip->regs->dmacsr &= ~APC_XINT_PLAY; + cs4231_chip->regs->dmacsr &= ~APC_PPAUSE; cs4231_playintr(drv); - cs4231_chip->regs->dmacsr |= CS_PLAY_SETUP; + cs4231_chip->regs->dmacsr |= APC_PLAY_SETUP; cs4231_enable_play(drv); cs4231_ready(drv); @@ -1246,6 +1480,28 @@ cs4231_playintr(drv); } +#ifdef EB4231_SUPPORT +static void eb4231_stop_output(struct sparcaudio_driver *drv) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + + dprintk(("in cs4231_stop_output\n")); + dprintk(("csr 0x%x acr 0x%x bcr %d\n", readl(&cs4231_chip->eb2p->dcsr), readl(&cs4231_chip->eb2p->dacr), readl(&cs4231_chip->eb2p->dbcr))); + cs4231_chip->output_ptr = NULL; + cs4231_chip->output_size = 0; + if (cs4231_chip->output_dma_handle) { + cs4231_chip->output_dma_handle = 0; + cs4231_chip->output_dma_size = 0; + } +#if 0 + if (cs4231_chip->output_next_dma_handle) { + cs4231_chip->output_next_dma_handle = 0; + cs4231_chip->output_next_dma_size = 0; + } +#endif +} +#endif + static void cs4231_stop_output(struct sparcaudio_driver *drv) { struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; @@ -1254,13 +1510,13 @@ cs4231_chip->output_ptr = NULL; cs4231_chip->output_size = 0; if (cs4231_chip->output_dma_handle) { - mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->output_dma_handle), + mmu_release_scsi_one(cs4231_chip->output_dma_handle, cs4231_chip->output_dma_size, drv->dev->my_bus); cs4231_chip->output_dma_handle = 0; cs4231_chip->output_dma_size = 0; } if (cs4231_chip->output_next_dma_handle) { - mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->output_next_dma_handle), + mmu_release_scsi_one(cs4231_chip->output_next_dma_handle, cs4231_chip->output_next_dma_size, drv->dev->my_bus); cs4231_chip->output_next_dma_handle = 0; cs4231_chip->output_next_dma_size = 0; @@ -1272,10 +1528,10 @@ struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; int x = 0; - while (!(cs4231_chip->regs->dmacsr & CS_XINT_COVF) && x <= CS_TIMEOUT) { + while (!(cs4231_chip->regs->dmacsr & APC_XINT_COVF) && x <= CS_TIMEOUT) { x++; } - cs4231_chip->regs->dmacsr |= CS_XINT_CEMP; + cs4231_chip->regs->dmacsr |= APC_XINT_CEMP; } static void cs4231_start_input(struct sparcaudio_driver *drv, __u8 * buffer, @@ -1295,14 +1551,14 @@ cs4231_chip->perchip_info.record.active = 1; cs4231_chip->recording_count = 0; - if ((cs4231_chip->regs->dmacsr & CS_CPAUSE) || - !(cs4231_chip->regs->dmacsr & CDMA_READY)) { - cs4231_chip->regs->dmacsr &= ~CS_XINT_CAPT; - cs4231_chip->regs->dmacsr &= ~CS_CPAUSE; + if ((cs4231_chip->regs->dmacsr & APC_CPAUSE) || + !(cs4231_chip->regs->dmacsr & APC_CDMA_READY)) { + cs4231_chip->regs->dmacsr &= ~APC_XINT_CAPT; + cs4231_chip->regs->dmacsr &= ~APC_CPAUSE; cs4231_recintr(drv); - cs4231_chip->regs->dmacsr |= CS_CAPT_SETUP; + cs4231_chip->regs->dmacsr |= APC_CAPT_SETUP; cs4231_enable_rec(drv); cs4231_ready(drv); @@ -1315,18 +1571,18 @@ struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; cs4231_chip->perchip_info.record.active = 0; - cs4231_chip->regs->dmacsr |= (CS_CPAUSE); + cs4231_chip->regs->dmacsr |= (APC_CPAUSE); cs4231_chip->input_ptr = NULL; cs4231_chip->input_size = 0; if (cs4231_chip->input_dma_handle) { - mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->input_dma_handle), + mmu_release_scsi_one(cs4231_chip->input_dma_handle, cs4231_chip->input_dma_size, drv->dev->my_bus); cs4231_chip->input_dma_handle = 0; cs4231_chip->input_dma_size = 0; } if (cs4231_chip->input_next_dma_handle) { - mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->input_next_dma_handle), + mmu_release_scsi_one(cs4231_chip->input_next_dma_handle, cs4231_chip->input_next_dma_size, drv->dev->my_bus); cs4231_chip->input_next_dma_handle = 0; cs4231_chip->input_next_dma_size = 0; @@ -1408,7 +1664,11 @@ struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; strncpy(audinfo->name, "SUNW,CS4231", sizeof(audinfo->name) - 1); - /* versions: SPARCstation 4/5=a, Ultra=b */ + /* versions */ + /* a: SPARCstation 4/5 b: Ultra 1/2 (electron) */ + /* c: Ultra 1/2 PCI? (positron) d: ppc */ + /* e: x86 f: Ultra Enterprise? (tazmo) */ + /* g: Ultra 30? (quark) h: Ultra 5/10? (darwin) */ /* apparently Ultra 1, Ultra 2 don't have internal CD input */ if (cs4231_chip->status & CS_STATUS_IS_ULTRA) strncpy(audinfo->version, "b", sizeof(audinfo->version) - 1); @@ -1450,6 +1710,114 @@ return retval; } +#ifdef EB4231_SUPPORT +/* ebus audio capture interrupt handler. */ +void eb4231_cinterrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct sparcaudio_driver *drv = (struct sparcaudio_driver *)dev_id; + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + int dummy1, dummy2; + + printk("in eb4231_interrupt\n"); + + /* Clear the interrupt. */ + dummy1 = readl(&cs4231_chip->eb2p->dcsr); + dummy2 = readl(&cs4231_chip->eb2c->dcsr); + + printk("play csr 0x%x capt csr 0x%x\n", dummy1, dummy2); + + cs4231_chip->eb2p->dcsr = dummy1; + cs4231_chip->eb2c->dcsr = dummy2; +#if 0 + if (dummy & APC_PLAY_INT) { + if (dummy & APC_XINT_PNVA) { + cs4231_chip->perchip_info.play.samples += + cs4231_length_to_samplecount(&(cs4231_chip->perchip_info.play), + cs4231_chip->playlen); + eb4231_playintr(drv); + } + /* Any other conditions we need worry about? */ + } + + if (dummy & APC_CAPT_INT) { + if (dummy & APC_XINT_CNVA) { + cs4231_chip->perchip_info.record.samples += + cs4231_length_to_samplecount(&(cs4231_chip->perchip_info.record), + cs4231_chip->reclen); + eb4231_recintr(drv); + } + /* Any other conditions we need worry about? */ + } + + + if (dummy & APC_XINT_CEMP) { + if (cs4231_chip->perchip_info.record.active == 0) { + /* Fix me */ + cs4231_chip->perchip_info.record.active = 0; + cs4231_chip->perchip_info.record.error = 1; + eb4231_recintr(drv); + } + } + + if (dummy & APC_XINT_EMPT) { + if (!cs4231_chip->output_next_dma_handle) { + cs4231_chip->regs->dmacsr |= (APC_PPAUSE); + cs4231_disable_play(drv); + cs4231_chip->perchip_info.play.error = 1; + } + cs4231_chip->perchip_info.play.active = 0; + eb4231_playintr(drv); + + eb4231_getsamplecount(drv, cs4231_chip->playlen, 0); + } +#endif +} + +/* ebus audio play interrupt handler. */ +void eb4231_pinterrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct sparcaudio_driver *drv = (struct sparcaudio_driver *)dev_id; + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + unsigned int dummy; + + dprintk(("in eb4231_interrupt2\n")); + + /* Clear the interrupt. */ + dummy = readl(&cs4231_chip->eb2p->dcsr); + dprintk(("play csr 0x%x\n", dummy)); + + if (dummy & EBUS_DCSR_INT_PEND) { +#if 0 + if (!(dummy & EBUS_DCSR_NA_LOADED)) { +#endif + cs4231_chip->perchip_info.play.samples += + cs4231_length_to_samplecount(&(cs4231_chip->perchip_info.play), + cs4231_chip->playlen); + eb4231_playintr(drv); +#if 0 + } +#endif + } + + if (!(dummy & EBUS_DCSR_A_LOADED)) { +#if 0 + if (!cs4231_chip->output_next_dma_handle) { +#endif + writel((dummy & ~EBUS_DCSR_EN_DMA), &cs4231_chip->eb2p->dcsr); + cs4231_disable_play(drv); + cs4231_chip->perchip_info.play.error = 1; +#if 0 + } +#endif + cs4231_chip->perchip_info.play.active = 0; +#if 0 + eb4231_playintr(drv); +#endif + eb4231_getsamplecount(drv, cs4231_chip->playlen, 0); + } + +} +#endif /* Audio interrupt handler. */ void cs4231_interrupt(int irq, void *dev_id, struct pt_regs *regs) @@ -1458,7 +1826,7 @@ struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; __u32 dummy; - tprintk(("in cs4231_interrupt\n")); + dprintk(("in cs4231_interrupt\n")); /* Clear the interrupt. */ dummy = cs4231_chip->regs->dmacsr; @@ -1468,8 +1836,8 @@ * if anything since we may be doing shared interrupts */ - if (dummy & CS_PLAY_INT) { - if (dummy & CS_XINT_PNVA) { + if (dummy & APC_PLAY_INT) { + if (dummy & APC_XINT_PNVA) { cs4231_chip->perchip_info.play.samples += cs4231_length_to_samplecount(&(cs4231_chip->perchip_info.play), cs4231_chip->playlen); @@ -1478,8 +1846,8 @@ /* Any other conditions we need worry about? */ } - if (dummy & CS_CAPT_INT) { - if (dummy & CS_XINT_CNVA) { + if (dummy & APC_CAPT_INT) { + if (dummy & APC_XINT_CNVA) { cs4231_chip->perchip_info.record.samples += cs4231_length_to_samplecount(&(cs4231_chip->perchip_info.record), cs4231_chip->reclen); @@ -1489,7 +1857,7 @@ } - if (dummy & CS_XINT_CEMP) { + if (dummy & APC_XINT_CEMP) { if (cs4231_chip->perchip_info.record.active == 0) { /* Fix me */ cs4231_chip->perchip_info.record.active = 0; @@ -1498,9 +1866,9 @@ } } - if (dummy & CS_XINT_EMPT) { + if (dummy & APC_XINT_EMPT) { if (!cs4231_chip->output_next_dma_handle) { - cs4231_chip->regs->dmacsr |= (CS_PPAUSE); + cs4231_chip->regs->dmacsr |= (APC_PPAUSE); cs4231_disable_play(drv); cs4231_chip->perchip_info.play.error = 1; } @@ -1510,7 +1878,7 @@ cs4231_getsamplecount(drv, cs4231_chip->playlen, 0); } - if (dummy & CS_GENL_INT) { + if (dummy & APC_GENL_INT) { /* If we get here we must be sharing an interrupt, but I haven't code to handle this right now */ } @@ -1576,6 +1944,67 @@ cs4231_get_formats, }; +#ifdef EB4231_SUPPORT +static struct sparcaudio_operations eb4231_ops = { + cs4231_open, + cs4231_release, + cs4231_ioctl, + eb4231_start_output, + eb4231_stop_output, + cs4231_start_input, + cs4231_stop_input, + cs4231_audio_getdev, + cs4231_set_output_volume, + cs4231_get_output_volume, + cs4231_set_input_volume, + cs4231_get_input_volume, + cs4231_set_monitor_volume, + cs4231_get_monitor_volume, + cs4231_set_output_balance, + cs4231_get_output_balance, + cs4231_set_input_balance, + cs4231_get_input_balance, + cs4231_set_output_channels, + cs4231_get_output_channels, + cs4231_set_input_channels, + cs4231_get_input_channels, + cs4231_set_output_precision, + cs4231_get_output_precision, + cs4231_set_input_precision, + cs4231_get_input_precision, + cs4231_set_output_port, + cs4231_get_output_port, + cs4231_set_input_port, + cs4231_get_input_port, + cs4231_set_output_encoding, + cs4231_get_output_encoding, + cs4231_set_input_encoding, + cs4231_get_input_encoding, + cs4231_set_output_rate, + cs4231_get_output_rate, + cs4231_set_input_rate, + cs4231_get_input_rate, + cs4231_audio_getdev_sunos, + cs4231_get_output_ports, + cs4231_get_input_ports, + cs4231_output_muted, + cs4231_get_output_muted, + cs4231_set_output_pause, + cs4231_get_output_pause, + cs4231_set_input_pause, + cs4231_get_input_pause, + cs4231_set_output_samples, + eb4231_get_output_samples, + cs4231_set_input_samples, + eb4231_get_input_samples, + cs4231_set_output_error, + cs4231_get_output_error, + cs4231_set_input_error, + cs4231_get_input_error, + cs4231_get_formats, +}; +#endif + /* Attach to an cs4231 chip given its PROM node. */ static int cs4231_attach(struct sparcaudio_driver *drv, struct linux_sbus_device *sdev) @@ -1642,6 +2071,8 @@ enable_irq(cs4231_chip->irq); + cs4231_chip->nirqs = 1; + cs4231_enable_interrupts(drv); /* Reset the audio chip. */ @@ -1676,13 +2107,149 @@ AUDIO_ANALOG_LOOPBACK); /* Announce the hardware to the user. */ - printk(KERN_INFO "audio%d: cs4231%c at 0x%lx irq %d\n", +#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x20100 + printk(KERN_INFO "audio%d: cs4231%c at 0x%x irq %d\n", drv->index, (cs4231_chip->status & CS_STATUS_REV_A) ? 'a' : ' ', (unsigned long)cs4231_chip->regs, cs4231_chip->irq); +#else + printk(KERN_INFO "audio%d: cs4231%c at %p irq %s\n", + drv->index, (cs4231_chip->status & CS_STATUS_REV_A) ? 'a' : ' ', + cs4231_chip->regs, __irq_itoa(cs4231_chip->irq)); +#endif + + /* Success! */ + return 0; +} + +#ifdef EB4231_SUPPORT +/* Attach to an cs4231 chip given its PROM node. */ +static int eb4231_attach(struct sparcaudio_driver *drv, + struct linux_ebus_device *edev) +{ + struct cs4231_chip *cs4231_chip; + int len, err, nregs; + struct linux_prom_registers regs[4]; + + /* Allocate our private information structure. */ + drv->private = kmalloc(sizeof(struct cs4231_chip), GFP_KERNEL); + if (!drv->private) + return -ENOMEM; + + /* Point at the information structure and initialize it. */ + drv->ops = &eb4231_ops; + cs4231_chip = (struct cs4231_chip *)drv->private; + cs4231_chip->input_ptr = cs4231_chip->output_ptr = NULL; + cs4231_chip->input_size = cs4231_chip->output_size = 0; + cs4231_chip->status = 0; + + len = prom_getproperty(edev->prom_node, "reg", (void *)regs, sizeof(regs)); + + if ((len % sizeof(regs[0])) != 0) { + printk("eb4231: Strange reg property size %d\n", len); + return -ENODEV; + } + + nregs = len / sizeof(regs[0]); + + /* Make sure we can map the registers first */ + if (check_region(edev->base_address[0], + sizeof(struct cs4231_regs))) { + printk("eb4231_attach: can't get region at %016lx\n", + edev->base_address[0]); + goto cleanup; + } + if (check_region(edev->base_address[1], + sizeof(struct linux_ebus_dma))) { + printk("eb4231_attach: can't get region at %016lx\n", + edev->base_address[1]); + goto cleanup; + } + if (check_region(edev->base_address[2], + sizeof(struct linux_ebus_dma))) { + printk("eb4231_attach: can't get region at %016lx\n", + edev->base_address[2]); + goto cleanup; + } + + cs4231_chip->regs = (struct cs4231_regs *)edev->base_address[0]; + cs4231_chip->eb2p = (struct linux_ebus_dma *)edev->base_address[1]; + cs4231_chip->eb2c = (struct linux_ebus_dma *)edev->base_address[2]; + + request_region((unsigned long)cs4231_chip->regs, + sizeof(struct cs4231_regs), "cs4231 regs"); + request_region((unsigned long)cs4231_chip->eb2c, + sizeof(struct linux_ebus_dma), "4231 capture DMA"); + request_region((unsigned long)cs4231_chip->eb2p, + sizeof(struct linux_ebus_dma), "4231 playback DMA"); + + cs4231_chip->status |= CS_STATUS_IS_EBUS; + + /* Attach the interrupt handler to the audio interrupt. */ + cs4231_chip->irq = edev->irqs[0]; + cs4231_chip->irq2 = edev->irqs[1]; + + request_irq(cs4231_chip->irq, eb4231_cinterrupt, SA_SHIRQ, "cs4231", drv); +#if 0 + enable_irq(cs4231_chip->irq); +#endif + + request_irq(cs4231_chip->irq2, eb4231_pinterrupt, SA_SHIRQ, "cs4231", drv); + enable_irq(cs4231_chip->irq2); + + cs4231_chip->nirqs = 2; + + cs4231_enable_interrupts(drv); + + /* Reset the audio chip. */ + cs4231_chip_reset(drv); + + /* Register ourselves with the midlevel audio driver. */ + err = register_sparcaudio_driver(drv, 1); + + if (err < 0) { + printk(KERN_ERR "cs4231: unable to register\n"); + cs4231_disable_interrupts(drv); + disable_irq(cs4231_chip->irq); + free_irq(cs4231_chip->irq, drv); + if (cs4231_chip->nirqs == 2) { + disable_irq(cs4231_chip->irq2); + free_irq(cs4231_chip->irq2, drv); + } + release_region((unsigned long)cs4231_chip->regs, + sizeof(struct cs4231_regs)); + release_region((unsigned long)cs4231_chip->eb2c, + sizeof(struct linux_ebus_dma)); + release_region((unsigned long)cs4231_chip->eb2p, + sizeof(struct linux_ebus_dma)); + cleanup: + kfree(drv->private); + return -EIO; + } + + cs4231_chip->perchip_info.play.active = + cs4231_chip->perchip_info.play.pause = 0; + + cs4231_chip->perchip_info.record.active = + cs4231_chip->perchip_info.record.pause = 0; + + cs4231_chip->perchip_info.play.avail_ports = (AUDIO_HEADPHONE | + AUDIO_SPEAKER | + AUDIO_LINE_OUT); + + cs4231_chip->perchip_info.record.avail_ports = (AUDIO_INTERNAL_CD_IN | + AUDIO_LINE_IN | + AUDIO_MICROPHONE | + AUDIO_ANALOG_LOOPBACK); + + /* Announce the hardware to the user. */ + printk(KERN_INFO "audio%d: cs4231%c(eb2) at %p irq %s\n", + drv->index, (cs4231_chip->status & CS_STATUS_REV_A) ? 'a' : ' ', + cs4231_chip->regs, __irq_itoa(cs4231_chip->irq)); /* Success! */ return 0; } +#endif /* Probe for the cs4231 chip and then attach the driver. */ #ifdef MODULE @@ -1691,13 +2258,17 @@ __initfunc(int cs4231_init(void)) #endif { - struct linux_sbus *bus; + struct linux_sbus *sbus; struct linux_sbus_device *sdev; - +#ifdef EB4231_SUPPORT + struct linux_ebus *ebus; + struct linux_ebus_device *edev; +#endif + num_drivers = 0; /* Probe each SBUS for cs4231 chips. */ - for_all_sbusdev(sdev,bus) { + for_all_sbusdev(sdev,sbus) { if (!strcmp(sdev->prom_name, "SUNW,CS4231")) { /* Don't go over the max number of drivers. */ if (num_drivers >= MAX_DRIVERS) @@ -1708,6 +2279,21 @@ } } +#ifdef EB4231_SUPPORT + for_each_ebus(ebus) { + for_each_ebusdev(edev, ebus) { + if (!strcmp(edev->prom_name, "SUNW,CS4231")) { + /* Don't go over the max number of drivers. */ + if (num_drivers >= MAX_DRIVERS) + continue; + + if (eb4231_attach(&drivers[num_drivers], edev) == 0) + num_drivers++; + } + } + } +#endif + /* Only return success if we found some cs4231 chips. */ return (num_drivers > 0) ? 0 : -EIO; } @@ -1716,13 +2302,21 @@ /* Detach from an cs4231 chip given the device structure. */ static void cs4231_detach(struct sparcaudio_driver *drv) { - struct cs4231_chip *info = (struct cs4231_chip *)drv->private; + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; cs4231_disable_interrupts(drv); unregister_sparcaudio_driver(drv, 1); - disable_irq(info->irq); - free_irq(info->irq, drv); - sparc_free_io(info->regs, info->regs_size); + disable_irq(cs4231_chip->irq); + free_irq(cs4231_chip->irq, drv); + if (!(cs4231_chip->status & CS_STATUS_IS_EBUS)) { + sparc_free_io(cs4231_chip->regs, cs4231_chip->regs_size); + } else { +#ifdef EB4231_SUPPORT + release_region(cs4231_chip->regs, sizeof(struct cs4231_regs)); + release_region(cs4231_chip->eb2c, sizeof(struct linux_ebus_dma)); + release_region(cs4231_chip->eb2p, sizeof(struct linux_ebus_dma)); +#endif + } kfree(drv->private); } diff -u --recursive --new-file v2.2.3/linux/drivers/sbus/audio/cs4231.h linux/drivers/sbus/audio/cs4231.h --- v2.2.3/linux/drivers/sbus/audio/cs4231.h Thu Nov 19 09:56:28 1998 +++ linux/drivers/sbus/audio/cs4231.h Mon Mar 15 16:11:30 1999 @@ -37,9 +37,11 @@ struct cs4231_chip { struct cs4231_regs *regs; + struct linux_ebus_dma *eb2c; + struct linux_ebus_dma *eb2p; struct audio_info perchip_info; unsigned int playlen, reclen; - int irq; + int irq, irq2, nirqs; unsigned long regs_size; /* Keep track of various info */ @@ -48,13 +50,13 @@ /* Current buffer that the driver is playing. */ volatile __u8 * output_ptr; volatile unsigned long output_size; - volatile __u32 * output_dma_handle, * output_next_dma_handle; + volatile __u32 output_dma_handle, output_next_dma_handle; volatile unsigned long output_dma_size, output_next_dma_size; /* Current record buffer. */ volatile __u8 * input_ptr; volatile unsigned long input_size; - volatile __u32 * input_dma_handle, * input_next_dma_handle; + volatile __u32 input_dma_handle, input_next_dma_handle; volatile unsigned long input_dma_size, input_next_dma_size; /* Number of buffers in the pipe. */ @@ -68,6 +70,7 @@ #define CS_STATUS_REV_A 0x04 #define CS_STATUS_INTS_ON 0x08 #define CS_STATUS_IS_ULTRA 0x10 +#define CS_STATUS_IS_EBUS 0x20 #define CS_TIMEOUT 9000000 @@ -221,36 +224,43 @@ /* 30 - Capture Upper */ /* 31 - Capture Lower */ -/* Following are CSR register definitions for the Sparc */ +/* Following are APC CSR register definitions for the Sparc */ -#define CS_INT_PENDING 0x800000 /* Interrupt Pending */ -#define CS_PLAY_INT 0x400000 /* Playback interrupt */ -#define CS_CAPT_INT 0x200000 /* Capture interrupt */ -#define CS_GENL_INT 0x100000 /* General interrupt */ -#define CS_XINT_ENA 0x80000 /* General ext int. enable */ -#define CS_XINT_PLAY 0x40000 /* Playback ext intr */ -#define CS_XINT_CAPT 0x20000 /* Capture ext intr */ -#define CS_XINT_GENL 0x10000 /* Error ext intr */ -#define CS_XINT_EMPT 0x8000 /* Pipe empty interrupt */ -#define CS_XINT_PEMP 0x4000 /* Play pipe empty */ -#define CS_XINT_PNVA 0x2000 /* Playback NVA dirty */ -#define CS_XINT_PENA 0x1000 /* play pipe empty Int enable */ -#define CS_XINT_COVF 0x800 /* Cap data dropped on floor */ -#define CS_XINT_CNVA 0x400 /* Capture NVA dirty */ -#define CS_XINT_CEMP 0x200 /* Capture pipe empty interrupt */ -#define CS_XINT_CENA 0x100 /* Cap. pipe empty int enable */ -#define CS_PPAUSE 0x80 /* Pause the play DMA */ -#define CS_CPAUSE 0x40 /* Pause the capture DMA */ -#define CS_CDC_RESET 0x20 /* CODEC RESET */ -#define PDMA_READY 0x08 /* Play DMA Go */ -#define CDMA_READY 0x04 /* Capture DMA Go */ -#define CS_CHIP_RESET 0x01 /* Reset the chip */ +#define APC_INT_PENDING 0x800000 /* Interrupt Pending */ +#define APC_PLAY_INT 0x400000 /* Playback interrupt */ +#define APC_CAPT_INT 0x200000 /* Capture interrupt */ +#define APC_GENL_INT 0x100000 /* General interrupt */ +#define APC_XINT_ENA 0x80000 /* General ext int. enable */ +#define APC_XINT_PLAY 0x40000 /* Playback ext intr */ +#define APC_XINT_CAPT 0x20000 /* Capture ext intr */ +#define APC_XINT_GENL 0x10000 /* Error ext intr */ +#define APC_XINT_EMPT 0x8000 /* Pipe empty interrupt */ +#define APC_XINT_PEMP 0x4000 /* Play pipe empty */ +#define APC_XINT_PNVA 0x2000 /* Playback NVA dirty */ +#define APC_XINT_PENA 0x1000 /* play pipe empty Int enable */ +#define APC_XINT_COVF 0x800 /* Cap data dropped on floor */ +#define APC_XINT_CNVA 0x400 /* Capture NVA dirty */ +#define APC_XINT_CEMP 0x200 /* Capture pipe empty interrupt */ +#define APC_XINT_CENA 0x100 /* Cap. pipe empty int enable */ +#define APC_PPAUSE 0x80 /* Pause the play DMA */ +#define APC_CPAUSE 0x40 /* Pause the capture DMA */ +#define APC_CDC_RESET 0x20 /* CODEC RESET */ +#define APC_PDMA_READY 0x08 /* Play DMA Go */ +#define APC_CDMA_READY 0x04 /* Capture DMA Go */ +#define APC_CHIP_RESET 0x01 /* Reset the chip */ + +#define APC_INIT_SETUP (APC_CDMA_READY | APC_PDMA_READY | APC_XINT_ENA | APC_XINT_PLAY | APC_XINT_GENL | APC_INT_PENDING | APC_PLAY_INT | APC_CAPT_INT | APC_GENL_INT) + +#define APC_PLAY_SETUP (APC_GENL_INT | APC_PLAY_INT | APC_XINT_ENA | APC_XINT_PLAY | APC_XINT_EMPT | APC_XINT_GENL | APC_XINT_PENA | APC_PDMA_READY) -#define CS_INIT_SETUP (CDMA_READY | PDMA_READY | CS_XINT_ENA | CS_XINT_PLAY | CS_XINT_GENL | CS_INT_PENDING | CS_PLAY_INT | CS_CAPT_INT | CS_GENL_INT) +#define APC_CAPT_SETUP (APC_GENL_INT | APC_CAPT_INT | APC_XINT_ENA | APC_XINT_CAPT | APC_XINT_CEMP | APC_XINT_GENL | APC_CDMA_READY) -#define CS_PLAY_SETUP (CS_GENL_INT | CS_PLAY_INT | CS_XINT_ENA | CS_XINT_PLAY | CS_XINT_EMPT | CS_XINT_GENL | CS_XINT_PENA | PDMA_READY) +/* Following are EB2 CSR register definitions for the Sparc */ -#define CS_CAPT_SETUP (CS_GENL_INT | CS_CAPT_INT | CS_XINT_ENA | CS_XINT_CAPT | CS_XINT_CEMP | CS_XINT_GENL | CDMA_READY) +/* asm/ebus.h has the base settings */ + +#define EB2_PLAY_SETUP (EBUS_DCSR_BURST_SZ_8|EBUS_DCSR_INT_EN|EBUS_DCSR_EN_DMA|EBUS_DCSR_EN_CNT|EBUS_DCSR_TC) +#define EB2_CAPT_SETUP (EBUS_DCSR_BURST_SZ_8|EBUS_DCSR_INT_EN|EBUS_DCSR_EN_DMA|EBUS_DCSR_EN_CNT|EBUS_DCSR_TC|EBUS_DCSR_WRITE) #define CS4231_MIN_ATEN (0) #define CS4231_MAX_ATEN (31) diff -u --recursive --new-file v2.2.3/linux/drivers/sbus/audio/dbri.c linux/drivers/sbus/audio/dbri.c --- v2.2.3/linux/drivers/sbus/audio/dbri.c Thu Nov 19 09:56:28 1998 +++ linux/drivers/sbus/audio/dbri.c Mon Mar 15 16:11:30 1999 @@ -2,11 +2,10 @@ * drivers/sbus/audio/dbri.c * * Copyright (C) 1997 Rudolf Koenig (rfkoenig@immd4.informatik.uni-erlangen.de) - * The SparcLinux interface was adopted from the CS4231 driver. + * Copyright (C) 1998, 1999 Brent Baccala (baccala@freesoft.org) * * This is the lowlevel driver for the DBRI & MMCODEC duo used for ISDN & AUDIO * on Sun SPARCstation 10, 20, LX and Voyager models. - * NOTE: This driver only supports audio for now, there is NO SUPPORT for ISDN. * * - DBRI: AT&T T5900FX Dual Basic Rates ISDN Interface. It is a 32 channel * data time multiplexer with ISDN support (aka T7259) @@ -59,6 +58,7 @@ #include #include #include +#include #include #include "dbri.h" @@ -69,7 +69,7 @@ #include "../../isdn/hisax/foreign.h" #endif -/* #define DBRI_DEBUG */ +#define DBRI_DEBUG #ifdef DBRI_DEBUG @@ -80,14 +80,17 @@ #define D_MM (1<<3) #define D_USR (1<<4) -static int dbri_debug = D_GEN|D_INT|D_CMD|D_MM|D_USR; +/* static int dbri_debug = D_GEN|D_INT|D_CMD|D_MM|D_USR; */ +static int dbri_debug = 0; +MODULE_PARM(dbri_debug, "i"); + static char *cmds[] = { "WAIT", "PAUSE", "JUMP", "IIQ", "REX", "SDP", "CDP", "DTS", "SSP", "CHI", "NT", "TE", "CDEC", "TEST", "CDM", "RESRV" }; /* Bit hunting */ -#define dumpcmd {int i; for(i=0; icmd[i]); } +#define dumpcmd {int i; for(i=0; idma->cmd[i]); } #define DBRI_CMD(cmd, intr, value) ((cmd << 28) | (1 << 27) | value) @@ -103,14 +106,16 @@ #define MAX_DRIVERS 2 /* Increase this if need more than 2 DBRI's */ -#define WAIT_INTR1 0xbe -#define WAIT_INTR2 0xbf - static struct sparcaudio_driver drivers[MAX_DRIVERS]; -static char drv_name[] = "DBRI/audio"; -static int num_drivers; +static int num_drivers = 0; + + +/* +**************************************************************************** +************** DBRI initialization and command synchronization ************* +**************************************************************************** +*/ -static void * output_callback_arg; /* * Commands are sent to the DBRI by building a list of them in memory, @@ -137,35 +142,46 @@ * in DBRI register 0. I've tried to implement this in such a way * that might make implementing a more sophisticated scheme easier. * - * Every time a routine wants to write commands to the DBRI, it - * must first call dbri_cmdlock() and get an initial index into dbri->cmd - * (currently always 0) in return. After the commands have been - * write (index incremented after each one), dbri_cmdsend() is called - * with the final index value. + * Every time a routine wants to write commands to the DBRI, it must + * first call dbri_cmdlock() and get an initial pointer into dbri->dma->cmd + * in return. After the commands have been writen, dbri_cmdsend() is + * called with the final pointer value. */ -static int dbri_cmdlock(struct dbri *dbri) +static int dbri_locked = 0; /* XXX not SMP safe! XXX */ + +static volatile int * dbri_cmdlock(struct dbri *dbri) { - return 0; + if (dbri_locked) { + printk("DBRI: Command buffer locked! (bug in driver)\n"); + } + dbri_locked ++; + return dbri->dma->cmd; } -static void dbri_cmdsend(struct dbri *dbri, int n) +static void dbri_cmdsend(struct dbri *dbri, volatile int * cmd) { int maxloops = 1000000; - dbri->cmd[n++] = DBRI_CMD(D_WAIT, 0, WAIT_INTR1); - dbri->regs->reg8 = (int)dbri->cmd; - - while (maxloops > 0 && (dbri->regs->reg0 & D_P)); + dbri_locked --; + if (dbri_locked != 0) { + printk("DBRI: Command buffer improperly locked! (bug in driver)\n"); + } else if ((cmd - dbri->dma->cmd) >= DBRI_NO_CMDS-1) { + printk("DBRI: Command buffer overflow! (bug in driver)\n"); + } else { + *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0); + *(cmd++) = DBRI_CMD(D_WAIT, 0, 0); + dbri->regs->reg8 = (int)dbri->dma_dvma->cmd; + while ((maxloops--) > 0 && (dbri->regs->reg0 & D_P)); + } if (maxloops == 0) { - printk("DBRI: Maxloops exceeded in dbri_cmdsend\n"); + printk("DBRI: Chip never completed command buffer\n"); } } -static void dbri_reset(struct sparcaudio_driver *drv) +static void dbri_reset(struct dbri *dbri) { - struct dbri *dbri = (struct dbri *)drv->private; int i; dprintk(D_GEN, ("DBRI: reset 0:%x 2:%x 8:%x 9:%x\n", @@ -177,75 +193,662 @@ udelay(10); } -static void dbri_detach(struct sparcaudio_driver *drv) +static void dbri_detach(struct dbri *dbri) { - struct dbri *info = (struct dbri *)drv->private; - - dbri_reset(drv); - unregister_sparcaudio_driver(drv, 1); - free_irq(info->irq, drv); - sparc_free_io(info->regs, info->regs_size); - kfree(drv->private); + dbri_reset(dbri); + free_irq(dbri->irq, dbri); + sparc_free_io(dbri->regs, dbri->regs_size); + /* Should we release the DMA structure dbri->dma here? */ + kfree(dbri); } -static void dbri_initialize(struct sparcaudio_driver *drv) +static void dbri_initialize(struct dbri *dbri) { - struct dbri *dbri = (struct dbri *)drv->private; - int n; + int n; + volatile int *cmd; - dbri_reset(drv); - dbri->wait = NULL; + dbri_reset(dbri); dprintk(D_GEN, ("DBRI: init: cmd: %x, int: %x\n", - (int)dbri->cmd, (int)dbri->intr)); + (int)dbri->dma->cmd, (int)dbri->dma->intr)); /* * Initialize the interrupt ringbuffer. */ for(n = 0; n < DBRI_NO_INTS-1; n++) - dbri->intr[n * DBRI_INT_BLK] = - (int)(&dbri->intr[(n+1)*DBRI_INT_BLK]); - dbri->intr[n * DBRI_INT_BLK] = (int)(dbri->intr); + dbri->dma->intr[n * DBRI_INT_BLK] = + (int)(&dbri->dma_dvma->intr[(n+1)*DBRI_INT_BLK]); + dbri->dma->intr[n * DBRI_INT_BLK] = (int)(dbri->dma_dvma->intr); dbri->dbri_irqp = 1; -#ifdef USE_SBUS_BURSTS - /* Enable 4-word, 8-word, and 16-word SBus Bursts */ - dbri->regs->reg0 |= (D_G|D_S|D_E); -#else - /* Disable 4-word, 8-word, and 16-word SBus Bursts */ + /* We should query the openprom to see what burst sizes this + * SBus supports. For now, just disable all SBus bursts */ dbri->regs->reg0 &= ~(D_G|D_S|D_E); -#endif /* * Set up the interrupt queue */ - n = dbri_cmdlock(dbri); + cmd = dbri_cmdlock(dbri); - dbri->cmd[n++] = DBRI_CMD(D_IIQ, 0, 0); - dbri->cmd[n++] = (int)(dbri->intr); + *(cmd++) = DBRI_CMD(D_IIQ, 0, 0); + *(cmd++) = (int)(dbri->dma_dvma->intr); - dbri_cmdsend(dbri, n); + dbri_cmdsend(dbri, cmd); } +/* +**************************************************************************** +*************************** DBRI interrupt handler ************************* +**************************************************************************** +*/ + /* * Short data pipes transmit LSB first. The CS4215 receives MSB first. Grrr. - * So we have to reverse the bits. Note: only 1, 2 or 4 bytes are supported. + * So we have to reverse the bits. Note: not all bit lengths are supported */ static __u32 reverse_bytes(__u32 b, int len) { switch(len) { - case 4: b = ((b & 0xffff0000) >> 16) | ((b & 0x0000ffff) << 16); - case 2: b = ((b & 0xff00ff00) >> 8) | ((b & 0x00ff00ff) << 8); - case 1: b = ((b & 0xf0f0f0f0) >> 4) | ((b & 0x0f0f0f0f) << 4); - b = ((b & 0xcccccccc) >> 2) | ((b & 0x33333333) << 2); - b = ((b & 0xaaaaaaaa) >> 1) | ((b & 0x55555555) << 1); + case 32: + b = ((b & 0xffff0000) >> 16) | ((b & 0x0000ffff) << 16); + case 16: + b = ((b & 0xff00ff00) >> 8) | ((b & 0x00ff00ff) << 8); + case 8: + b = ((b & 0xf0f0f0f0) >> 4) | ((b & 0x0f0f0f0f) << 4); + case 4: + b = ((b & 0xcccccccc) >> 2) | ((b & 0x33333333) << 2); + case 2: + b = ((b & 0xaaaaaaaa) >> 1) | ((b & 0x55555555) << 1); + case 1: + break; + default: + printk("DBRI reverse_bytes: unsupported length\n"); } return b; } +/* transmission_complete_intr() + * + * Called by main interrupt handler when DBRI signals transmission complete + * on a pipe. + * + * Walks through the pipe's list of transmit buffer descriptors, releasing + * each one's DMA buffer (if present) and signaling its callback routine + * (if present), before flaging the descriptor available and proceeding + * to the next one. + * + * Assumes that only the last in a chain of descriptors will have FINT + * sent to signal an interrupt, so that the chain will be completely + * transmitted by the time we get here, and there's no need to save + * any of the descriptors. In particular, use of the DBRI's CDP command + * is precluded, but I've not been able to get CDP working reliably anyway. + */ + +static void transmission_complete_intr(struct dbri *dbri, int pipe) +{ + int td = dbri->pipes[pipe].desc; + int status; + void *buffer; + void (*callback)(void *, int); + + dbri->pipes[pipe].desc = -1; + + for (; td >= 0; td = dbri->descs[td].next) { + + if (td >= DBRI_NO_DESCS) { + printk("DBRI: invalid td on pipe %d\n", pipe); + return; + } + + status = dbri->dma->desc[td].word4; + + buffer = dbri->descs[td].buffer; + if (buffer) { + mmu_release_scsi_one(sbus_dvma_addr(buffer), + dbri->descs[td].len, + dbri->sdev->my_bus); + } + + callback = dbri->descs[td].output_callback; + if (callback != NULL) { + callback(dbri->descs[td].output_callback_arg, + DBRI_TD_STATUS(status) & 0xe); + } + + dbri->descs[td].inuse = 0; + } +} + +static void reception_complete_intr(struct dbri *dbri, int pipe) +{ + int rd = dbri->pipes[pipe].desc; + int status; + void *buffer; + void (*callback)(void *, int, unsigned int); + + if (rd < 0 || rd >= DBRI_NO_DESCS) { + printk("DBRI: invalid rd on pipe %d\n", pipe); + return; + } + + dbri->descs[rd].inuse = 0; + dbri->pipes[pipe].desc = -1; + status = dbri->dma->desc[rd].word1; + + buffer = dbri->descs[rd].buffer; + if (buffer) { + mmu_release_scsi_one(sbus_dvma_addr(buffer), + dbri->descs[rd].len, + dbri->sdev->my_bus); + } + + callback = dbri->descs[rd].input_callback; + if (callback != NULL) { + callback(dbri->descs[rd].input_callback_arg, + DBRI_RD_STATUS(status), + DBRI_RD_CNT(status)-2); + } +} + +static void dbri_intr(int irq, void *opaque, struct pt_regs *regs) +{ + struct dbri *dbri = (struct dbri *)opaque; + int x; + + /* + * Read it, so the interrupt goes away. + */ + x = dbri->regs->reg1; + + if ( x & (D_MRR|D_MLE|D_LBG|D_MBE) ) { + /* + * What should I do here ? + */ + if(x & D_MRR) printk("DBRI: Multiple Error Ack on SBus\n"); + if(x & D_MLE) printk("DBRI: Multiple Late Error on SBus\n"); + if(x & D_LBG) printk("DBRI: Lost Bus Grant on SBus\n"); + if(x & D_MBE) printk("DBRI: Burst Error on SBus\n"); + } + + if (!(x & D_IR)) /* Not for us */ + return; + + x = dbri->dma->intr[dbri->dbri_irqp]; + while (x != 0) { + int val = D_INTR_GETVAL(x); + int channel = D_INTR_GETCHAN(x); + + dbri->dma->intr[dbri->dbri_irqp] = 0; + + if(D_INTR_GETCHAN(x) == D_INTR_CMD) { + dprintk(D_INT,("DBRI: INTR: Command: %-5s Value:%d\n", + cmds[D_INTR_GETCMD(x)], D_INTR_GETVAL(x))); + } else { + dprintk(D_INT,("DBRI: INTR: Chan:%d Code:%d Val:%#x\n", + D_INTR_GETCHAN(x), D_INTR_GETCODE(x), + D_INTR_GETRVAL(x))); + } + + if (D_INTR_GETCODE(x) == D_INTR_SBRI) { + + /* SBRI - BRI status change */ + + int liu_states[] = {1, 0, 8, 3, 4, 5, 6, 7}; + dbri->liu_state = liu_states[val & 0x7]; + if (dbri->liu_callback) + dbri->liu_callback(dbri->liu_callback_arg); + } + + if (D_INTR_GETCODE(x) == D_INTR_BRDY) { + reception_complete_intr(dbri, channel); + } + + if (D_INTR_GETCODE(x) == D_INTR_XCMP) { + transmission_complete_intr(dbri, channel); + } + + if (D_INTR_GETCODE(x) == D_INTR_FXDT) { + + /* FXDT - Fixed data change */ + + if (dbri->pipes[D_INTR_GETCHAN(x)].sdp & D_SDP_MSB) { + val = reverse_bytes(val, dbri->pipes[channel].length); + } + + if (dbri->pipes[D_INTR_GETCHAN(x)].recv_fixed_ptr) { + * dbri->pipes[channel].recv_fixed_ptr = val; + } + } + + + dbri->dbri_irqp++; + if (dbri->dbri_irqp == (DBRI_NO_INTS * DBRI_INT_BLK)) + dbri->dbri_irqp = 1; + else if ((dbri->dbri_irqp & (DBRI_INT_BLK-1)) == 0) + dbri->dbri_irqp++; + x = dbri->dma->intr[dbri->dbri_irqp]; + } +} + + +/* +**************************************************************************** +************************** DBRI data pipe management *********************** +**************************************************************************** +*/ + + +/* reset_pipe(dbri, pipe) + * + * Called on an in-use pipe to clear anything being transmitted or received + */ + +static void reset_pipe(struct dbri *dbri, int pipe) +{ + int sdp; + volatile int *cmd; + + if (pipe < 0 || pipe > 31) { + printk("DBRI: reset_pipe called with illegal pipe number\n"); + return; + } + + sdp = dbri->pipes[pipe].sdp; + if (sdp == 0) { + printk("DBRI: reset_pipe called on uninitialized pipe\n"); + return; + } + + cmd = dbri_cmdlock(dbri); + *(cmd++) = DBRI_CMD(D_SDP, 0, sdp | D_SDP_C | D_SDP_P); + *(cmd++) = 0; + dbri_cmdsend(dbri, cmd); + + dbri->pipes[pipe].desc = -1; +} + +static void setup_pipe(struct dbri *dbri, int pipe, int sdp) +{ + if (pipe < 0 || pipe > 31) { + printk("DBRI: setup_pipe called with illegal pipe number\n"); + return; + } + + if ((sdp & 0xf800) != sdp) { + printk("DBRI: setup_pipe called with strange SDP value\n"); + /* sdp &= 0xf800; */ + } + + sdp |= D_PIPE(pipe); + dbri->pipes[pipe].sdp = sdp; + + reset_pipe(dbri, pipe); +} + +enum master_or_slave { CHImaster, CHIslave }; + +static void reset_chi(struct dbri *dbri, enum master_or_slave master_or_slave, + int bits_per_frame) +{ + volatile int *cmd; + int val; + + cmd = dbri_cmdlock(dbri); + + /* Set CHI Anchor: Pipe 16 */ + + val = D_DTS_VI | D_DTS_VO | D_DTS_INS | + D_DTS_PRVIN(D_P_16) | D_DTS_PRVOUT(D_P_16) | D_PIPE(D_P_16); + *(cmd++) = DBRI_CMD(D_DTS, 0, val); + *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(D_P_16); + *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(D_P_16); + + dbri->pipes[16].sdp = 1; + dbri->pipes[16].nextpipe = 16; + + if (master_or_slave == CHIslave) { + /* Setup DBRI for CHI Slave - receive clock, frame sync (FS) + * + * CHICM = 0 (slave mode, 8 kHz frame rate) + * IR = give immediate CHI status interrupt + * EN = give CHI status interrupt upon change + */ + *(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(0) + | D_CHI_IR | D_CHI_EN); + } else { + /* Setup DBRI for CHI Master - generate clock, FS + * + * BPF = bits per 8 kHz frame + * 12.288 MHz / CHICM_divisor = clock rate + * FD = 1 - drive CHIFS on rising edge of CHICK + */ + + int clockrate = bits_per_frame * 8; + int divisor = 12288 / clockrate; + + if (divisor > 255 || divisor * clockrate != 12288) { + printk("DBRI: illegal bits_per_frame in setup_chi\n"); + } + + *(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(divisor) | D_CHI_FD + | D_CHI_IR | D_CHI_EN + | D_CHI_BPF(bits_per_frame)); + } + + /* CHI Data Mode + * + * RCE = 0 - receive on falling edge of CHICK + * XCE = 1 - transmit on rising edge of CHICK + * XEN = 1 - enable transmitter + * REN = 1 - enable receiver + */ + + *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0); + + *(cmd++) = DBRI_CMD(D_CDM, 0, D_CDM_XCE|D_CDM_XEN|D_CDM_REN); + + dbri_cmdsend(dbri, cmd); +} + +enum in_or_out { PIPEinput, PIPEoutput }; + +static void link_time_slot(struct dbri *dbri, int pipe, + enum in_or_out direction, int prevpipe, + int length, int cycle) +{ + volatile int *cmd; + int val; + int nextpipe; + + if (pipe < 0 || pipe > 31 || prevpipe < 0 || prevpipe > 31) { + printk("DBRI: link_time_slot called with illegal pipe number\n"); + return; + } + + if (dbri->pipes[pipe].sdp == 0 || dbri->pipes[prevpipe].sdp == 0) { + printk("DBRI: link_time_slot called on uninitialized pipe\n"); + return; + } + + if (pipe == prevpipe) { + nextpipe = pipe; + } else { + nextpipe = dbri->pipes[prevpipe].nextpipe; + } + + dbri->pipes[pipe].nextpipe = nextpipe; + dbri->pipes[pipe].cycle = cycle; + dbri->pipes[pipe].length = length; + + cmd = dbri_cmdlock(dbri); + + if (direction == PIPEinput) { + val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(prevpipe) | pipe; + *(cmd++) = DBRI_CMD(D_DTS, 0, val); + *(cmd++) = D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe); + *(cmd++) = 0; + } else { + val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(prevpipe) | pipe; + *(cmd++) = DBRI_CMD(D_DTS, 0, val); + *(cmd++) = 0; + *(cmd++) = D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe); + } + + dbri_cmdsend(dbri, cmd); +} + +static void xmit_fixed(struct dbri *dbri, int pipe, unsigned int data) +{ + volatile int *cmd; + + if (pipe < 16 || pipe > 31) { + printk("DBRI: xmit_fixed called with illegal pipe number\n"); + return; + } + + if (D_SDP_MODE(dbri->pipes[pipe].sdp) != D_SDP_FIXED) { + printk("DBRI: xmit_fixed called on non-fixed pipe\n"); + return; + } + + if (! dbri->pipes[pipe].sdp & D_SDP_TO_SER) { + printk("DBRI: xmit_fixed called on receive pipe\n"); + return; + } + + /* DBRI short pipes always transmit LSB first */ + + if (dbri->pipes[pipe].sdp & D_SDP_MSB) { + data = reverse_bytes(data, dbri->pipes[pipe].length); + } + + cmd = dbri_cmdlock(dbri); + + *(cmd++) = DBRI_CMD(D_SSP, 0, pipe); + *(cmd++) = data; + + dbri_cmdsend(dbri, cmd); +} + +/* recv_fixed() + * + * Receive data on a "fixed" pipe - i.e, one whose contents are not + * expected to change much, and which we don't need to read constantly + * into a buffer. The DBRI only interrupts us when the data changes. + * Only short pipes (numbers 16-31) can be used in fixed data mode. + * + * Pass this function a pointer to a 32-bit field, no matter how large + * the actual time slot is. The interrupt handler takes care of bit + * ordering and alignment. An 8-bit time slot will always end up + * in the low-order 8 bits, filled either MSB-first or LSB-first, + * depending on the settings passed to setup_pipe() + */ + +static void recv_fixed(struct dbri *dbri, int pipe, __u32 *ptr) +{ + if (pipe < 16 || pipe > 31) { + printk("DBRI: recv_fixed called with illegal pipe number\n"); + return; + } + + if (D_SDP_MODE(dbri->pipes[pipe].sdp) != D_SDP_FIXED) { + printk("DBRI: recv_fixed called on non-fixed pipe\n"); + return; + } + + if (dbri->pipes[pipe].sdp & D_SDP_TO_SER) { + printk("DBRI: recv_fixed called on transmit pipe\n"); + return; + } + + dbri->pipes[pipe].recv_fixed_ptr = ptr; +} + + +static void xmit_on_pipe(struct dbri *dbri, int pipe, + void * buffer, unsigned int len, + void (*callback)(void *, int), void * callback_arg) +{ + volatile int *cmd; + int td = 0; + int first_td = -1; + int last_td; + __u32 dvma_buffer; + + if (pipe < 0 || pipe > 15) { + printk("DBRI: xmit_on_pipe called with illegal pipe number\n"); + return; + } + + if (dbri->pipes[pipe].sdp == 0) { + printk("DBRI: xmit_on_pipe called on uninitialized pipe\n"); + return; + } + + if (! dbri->pipes[pipe].sdp & D_SDP_TO_SER) { + printk("DBRI: xmit_on_pipe called on receive pipe\n"); + return; + } + + /* XXX Fix this XXX + * Should be able to queue multiple buffers to send on a pipe + */ + + if (dbri->pipes[pipe].desc != -1) { + printk("DBRI: xmit_on_pipe called on active pipe\n"); + return; + } + + dvma_buffer = mmu_get_scsi_one(buffer, len, dbri->sdev->my_bus); + + while (len > 0) { + int mylen; + + for (td; td < DBRI_NO_DESCS; td ++) { + if (! dbri->descs[td].inuse) break; + } + if (td == DBRI_NO_DESCS) { + break; + } + + if (len > (1 << 13) - 1) { + mylen = (1 << 13) - 1; + } else { + mylen = len; + } + + dbri->descs[td].inuse = 1; + dbri->descs[td].next = -1; + dbri->descs[td].buffer = NULL; + dbri->descs[td].output_callback = NULL; + dbri->descs[td].input_callback = NULL; + + dbri->dma->desc[td].word1 = DBRI_TD_CNT(mylen); + dbri->dma->desc[td].ba = dvma_buffer; + dbri->dma->desc[td].nda = 0; + dbri->dma->desc[td].word4 = 0; + + if (first_td == -1) { + first_td = td; + } else { + dbri->descs[last_td].next = td; + dbri->dma->desc[last_td].nda = + (int) & dbri->dma_dvma->desc[td]; + } + + last_td = td; + dvma_buffer += mylen; + len -= mylen; + } + + if (first_td == -1) { + printk("xmit_on_pipe: No descriptors available\n"); + return; + } + + if (len > 0) { + printk("xmit_on_pipe: Insufficient descriptors; data truncated\n"); + } + + dbri->dma->desc[last_td].word1 |= DBRI_TD_I | DBRI_TD_F | DBRI_TD_B; + + dbri->descs[last_td].buffer = buffer; + dbri->descs[last_td].len = len; + dbri->descs[last_td].output_callback = callback; + dbri->descs[last_td].output_callback_arg = callback_arg; + + dbri->pipes[pipe].desc = first_td; + + cmd = dbri_cmdlock(dbri); + + *(cmd++) = DBRI_CMD(D_SDP, 0, dbri->pipes[pipe].sdp | D_SDP_P | D_SDP_C); + *(cmd++) = (int) & dbri->dma_dvma->desc[first_td]; + + dbri_cmdsend(dbri, cmd); +} + +static void recv_on_pipe(struct dbri *dbri, int pipe, + void * buffer, unsigned int len, + void (*callback)(void *, int, unsigned int), + void * callback_arg) +{ + volatile int *cmd; + int rd; + + if (pipe < 0 || pipe > 15) { + printk("DBRI: recv_on_pipe called with illegal pipe number\n"); + return; + } + + if (dbri->pipes[pipe].sdp == 0) { + printk("DBRI: recv_on_pipe called on uninitialized pipe\n"); + return; + } + + if (dbri->pipes[pipe].sdp & D_SDP_TO_SER) { + printk("DBRI: recv_on_pipe called on transmit pipe\n"); + return; + } + + /* XXX Fix this XXX + * Should be able to queue multiple buffers to send on a pipe + */ + + if (dbri->pipes[pipe].desc != -1) { + printk("DBRI: recv_on_pipe called on active pipe\n"); + return; + } + + /* XXX Fix this XXX + * Use multiple descriptors, if needed, to fit in all the data + */ + + if (len > (1 << 13) - 1) { + printk("recv_on_pipe called with len=%d; truncated\n", len); + len = (1 << 13) - 1; + } + + /* Make sure buffer size is multiple of four */ + len &= ~3; + + for (rd = 0; rd < DBRI_NO_DESCS; rd ++) { + if (! dbri->descs[rd].inuse) break; + } + if (rd == DBRI_NO_DESCS) { + printk("DBRI xmit_on_pipe: No descriptors available\n"); + return; + } + + dbri->dma->desc[rd].word1 = 0; + dbri->dma->desc[rd].ba = mmu_get_scsi_one(buffer, len, + dbri->sdev->my_bus); + dbri->dma->desc[rd].nda = 0; + dbri->dma->desc[rd].word4 = DBRI_RD_B | DBRI_RD_BCNT(len); + + dbri->descs[rd].buffer = buffer; + dbri->descs[rd].len = len; + dbri->descs[rd].input_callback = callback; + dbri->descs[rd].input_callback_arg = callback_arg; + + dbri->pipes[pipe].desc = rd; + + cmd = dbri_cmdlock(dbri); + + *(cmd++) = DBRI_CMD(D_SDP, 0, dbri->pipes[pipe].sdp | D_SDP_P); + *(cmd++) = (int) & dbri->dma_dvma->desc[rd]; + + dbri_cmdsend(dbri, cmd); +} + + +/* +**************************************************************************** +*********************** CS4215 audio codec management ********************** +**************************************************************************** +*/ static void mmcodec_default(struct cs4215 *mm) @@ -266,7 +869,7 @@ * Control Time Slot 1-4 * 0: Default I/O voltage scale * 1: 8 bit ulaw, 8kHz, mono, high pass filter disabled - * 2: Serial enable, CHI master, 1 CHI device (64bit), clock 1 + * 2: Serial enable, CHI master, 128 bits per frame, clock 1 * 3: Tests disabled */ mm->ctrl[0] = CS4215_RSRVD_1; @@ -278,10 +881,6 @@ static void mmcodec_init_data(struct dbri *dbri) { - int val, n; - - n = dbri_cmdlock(dbri); - /* * Data mode: * Pipe 4: Send timeslots 1-4 (audio data) @@ -295,80 +894,27 @@ * bits. The CS4215, it seems, observes TSIN (the delayed signal) * even if it's the CHI master. Don't ask me... */ - - - /* Pipe 4: SDP */ - val = D_SDP_MEM|D_SDP_TO_SER|D_SDP_C|D_SDP_P|D_SDP_MSB|D_PIPE(D_P_4); - dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); - dbri->cmd[n++] = 0; - - - /* Pipe 17: SDP */ - val = D_SDP_FIXED|D_SDP_TO_SER|D_SDP_C|D_PIPE(D_P_17); - dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); - dbri->cmd[n++] = 0; /* Fixed data */ - - /* Pipe 17: SSP */ - dbri->cmd[n++] = DBRI_CMD(D_SSP, 0, D_PIPE(D_P_17)); - dbri->cmd[n++] = reverse_bytes(*(int *)dbri->mm.data, 4); - - - /* Pipe 6: SDP */ - val=D_SDP_MEM|D_SDP_FROM_SER|D_SDP_C|D_SDP_P|D_SDP_MSB|D_PIPE(D_P_6); - dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); - dbri->cmd[n++] = 0; - - - /* Pipe 20: SDP */ - val = D_SDP_FIXED|D_SDP_FROM_SER|D_SDP_CHANGE|D_SDP_C|D_PIPE(D_P_20); - dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); - dbri->cmd[n++] = 0; /* Fixed data */ - - dbri->cmd[n++] = DBRI_CMD(D_PAUSE, 0, 0); + /* Switch CS4215 to data mode - set PIO3 to 1 */ + dbri->regs->reg2 = D_ENPIO | D_PIO1 | D_PIO3 | + (dbri->mm.onboard ? D_PIO0 : D_PIO2); + reset_chi(dbri, CHIslave, 0); - /* Pipe 4: DTS */ - val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_16) | D_PIPE(D_P_4); - dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val); - dbri->cmd[n++] = 0; -#if 0 - /* Full blown, four time slots, 16 bit stereo */ - dbri->cmd[n++] = D_TS_LEN(32) | D_TS_CYCLE(8)| D_TS_NEXT(D_P_16); -#else - /* Single time slot, 8 bit mono */ - dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(8)| D_TS_NEXT(D_P_16); -#endif + setup_pipe(dbri, 4, D_SDP_MEM | D_SDP_TO_SER | D_SDP_MSB); + setup_pipe(dbri, 17, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB); + setup_pipe(dbri, 6, D_SDP_MEM | D_SDP_FROM_SER | D_SDP_MSB); + setup_pipe(dbri, 20, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB); - /* Pipe 17: DTS */ - val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_4) | D_PIPE(D_P_17); - dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val); - dbri->cmd[n++] = 0; - dbri->cmd[n++] = D_TS_LEN(32) | D_TS_CYCLE(40) | D_TS_NEXT(D_P_16); - - /* Pipe 6: DTS */ - val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_16) | D_PIPE(D_P_6); - dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val); -#if 0 - /* Full blown, four time slots, 16 bit stereo */ - dbri->cmd[n++] = D_TS_LEN(32) | D_TS_CYCLE(8)| D_TS_NEXT(D_P_16); -#else - /* Single time slot, 8 bit mono */ - dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(8)| D_TS_NEXT(D_P_16); -#endif - dbri->cmd[n++] = 0; - - /* Pipe 20: DTS */ - val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_6) | D_PIPE(D_P_20); - dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val); - dbri->cmd[n++] = D_TS_LEN(16) | D_TS_CYCLE(48) | D_TS_NEXT(D_P_16); - dbri->cmd[n++] = 0; + /* Pipes 4 and 6 - Single time slot, 8 bit mono */ - /* CHI: Slave mode; enable interrupts */ - dbri->cmd[n++] = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(0) | D_CHI_IR | D_CHI_EN); + link_time_slot(dbri, 17, PIPEoutput, 16, 32, 32); + link_time_slot(dbri, 4, PIPEoutput, 17, 8, 128); + link_time_slot(dbri, 6, PIPEinput, 16, 8, 0); + link_time_slot(dbri, 20, PIPEinput, 6, 16, 40); - dbri_cmdsend(dbri, n); + xmit_fixed(dbri, 17, *(int *)dbri->mm.data); } @@ -377,7 +923,7 @@ */ static void mmcodec_setctrl(struct dbri *dbri) { - int n, val; + int i, val; /* * Enable Control mode: Set DBRI's PIO3 (4215's D/~C) to 0, then wait @@ -398,15 +944,15 @@ * into Data mode and put the DBRI into slave mode. Various timing * requirements must be observed along the way. * - * Oh, and one more thing - when the DBRI is master (and only when - * the DBRI is master), the addressing of the CS4215's time slots - * is offset by eight bits, so we add eight to all the "cycle" - * values in the Define Time Slot (DTS) commands. This is done in - * hardware by a TI 248 that delays the DBRI->4215 frame sync signal - * by eight clock cycles. Anybody know why? + * Oh, and one more thing, on a SPARCStation 20 (and maybe + * others?), the addressing of the CS4215's time slots is + * offset by eight bits, so we add eight to all the "cycle" + * values in the Define Time Slot (DTS) commands. This is + * done in hardware by a TI 248 that delays the DBRI->4215 + * frame sync signal by eight clock cycles. Anybody know why? */ - n = dbri_cmdlock(dbri); + reset_chi(dbri, CHImaster, 128); /* * Control mode: @@ -415,472 +961,325 @@ * Pipe 19: Receive timeslot 7 (version). */ - /* Set CHI Anchor: Pipe 16. This should take care of the rest. */ - val = D_DTS_VI | D_DTS_VO | D_DTS_INS | - D_DTS_PRVIN(D_P_16) | D_DTS_PRVOUT(D_P_16) | D_PIPE(D_P_16); - dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val); - dbri->cmd[n++] = D_TS_ANCHOR | D_TS_NEXT(D_P_16); - dbri->cmd[n++] = D_TS_ANCHOR | D_TS_NEXT(D_P_16); - - - /* Setup the pipes first */ - val = D_SDP_FIXED|D_SDP_TO_SER|D_SDP_P|D_SDP_C|D_PIPE(D_P_17); - dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); - dbri->cmd[n++] = 0; - - val = D_SDP_FIXED|D_SDP_CHANGE|D_SDP_C|D_PIPE(D_P_18); - dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); - dbri->cmd[n++] = 0; - - val = D_SDP_FIXED|D_SDP_CHANGE|D_SDP_C|D_PIPE(D_P_19); - dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); - dbri->cmd[n++] = 0; + setup_pipe(dbri, 17, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB); + setup_pipe(dbri, 18, D_SDP_FIXED | D_SDP_CHANGE | D_SDP_MSB); + setup_pipe(dbri, 19, D_SDP_FIXED | D_SDP_CHANGE | D_SDP_MSB); + + link_time_slot(dbri, 17, PIPEoutput, 16, 32, 128); + link_time_slot(dbri, 18, PIPEinput, 16, 8, 0); + link_time_slot(dbri, 19, PIPEinput, 18, 8, 48); + + recv_fixed(dbri, 18, & dbri->mm.status); + recv_fixed(dbri, 19, & dbri->mm.version); + + /* Wait for the chip to echo back CLB (Control Latch Bit) as zero */ - /* Fill in the data to send */ dbri->mm.ctrl[0] &= ~CS4215_CLB; - dbri->cmd[n++] = DBRI_CMD(D_SSP, 0, D_PIPE(D_P_17)); - dbri->cmd[n++] = reverse_bytes(*(int *)dbri->mm.ctrl, 4); + xmit_fixed(dbri, 17, *(int *)dbri->mm.ctrl); - dbri->cmd[n++] = DBRI_CMD(D_PAUSE, 0, 0); + i = 1000000; + while ((! dbri->mm.status & CS4215_CLB) && i--); + if (i == 0) { + printk("CS4215 didn't respond to CLB\n"); + return; + } + /* Terminate CS4215 control mode - data sheet says + * "Set CLB=1 and send two more frames of valid control info" + */ + dbri->mm.ctrl[0] |= CS4215_CLB; + xmit_fixed(dbri, 17, *(int *)dbri->mm.ctrl); - /* Link the timeslots */ + /* Two frames of control info @ 8kHz frame rate = 250 us delay */ + udelay(250); +} - /* Pipe 17 - CS4215 Status, Data Format, Serial Control, Test - output - * time slots 1, 2, 3 and 4 - 32 bits - */ +static int mmcodec_init(struct sparcaudio_driver *drv) +{ + struct dbri *dbri = (struct dbri *)drv->private; + int reg2 = dbri->regs->reg2; - val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_16) | D_PIPE(D_P_17); - dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val); - dbri->cmd[n++] = 0; - dbri->cmd[n++] = D_TS_LEN(32) | D_TS_CYCLE(8) | D_TS_NEXT(D_P_16); - /* Pipe 18 - CS4215 Status and Data Format - input - * time slots 1 & 2 - 16 bits - */ + /* Look for the cs4215 chips */ + if(reg2 & D_PIO2) { + dprintk(D_MM, ("DBRI: Onboard CS4215 detected\n")); + dbri->mm.onboard = 1; + } + if(reg2 & D_PIO0) { + dprintk(D_MM, ("DBRI: Speakerbox detected\n")); + dbri->mm.onboard = 0; + } + - val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_16) | D_PIPE(D_P_18); - dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val); - dbri->cmd[n++] = D_TS_LEN(16) | D_TS_CYCLE(8) | D_TS_NEXT(D_P_16); - dbri->cmd[n++] = 0; + /* Using the Speakerbox, if both are attached. */ + if((reg2 & D_PIO2) && (reg2 & D_PIO0)) { + printk("DBRI: Using speakerbox / ignoring onboard mmcodec.\n"); + dbri->regs->reg2 = D_ENPIO2; + dbri->mm.onboard = 0; + } + if( !(reg2 & (D_PIO0|D_PIO2)) ) { + printk("DBRI: no mmcodec found.\n"); + return -EIO; + } - /* Pipe 19 - CS4215 Revision - time slot 7, eight bits - input - */ - val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_18) | D_PIPE(D_P_19); - dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val); - dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(56) | D_TS_NEXT(D_P_16); - dbri->cmd[n++] = 0; + /* Now talk to our baby */ + dbri->regs->reg0 |= D_C; /* Enable CHI */ + mmcodec_default(&dbri->mm); - /* Setup DBRI for CHI Master - * - * BPF = 128 (128 bits per 8 kHz frame = 1.024 MHz clock rate) - * CHICM = 12 (12.288 MHz / 24 = 1.024 MHz clock rate) - * FD = 1 - drive CHIFS on rising edge of CHICK - * - * RCE = 0 - receive on falling edge of CHICK - * XCE = 1 - transmit on rising edge of CHICK - */ - dbri->cmd[n++] = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(12) | D_CHI_FD | - D_CHI_IR | D_CHI_EN | D_CHI_BPF(128)); - dbri->cmd[n++] = DBRI_CMD(D_CDM, 0, D_CDM_XCE|D_CDM_XEN|D_CDM_REN); - dbri->cmd[n++] = DBRI_CMD(D_PAUSE, 0, 0); + dbri->mm.version = 0xff; + mmcodec_setctrl(dbri); + if(dbri->mm.version == 0xff) + return -EIO; - /* Wait for the command to complete */ - dbri_cmdsend(dbri, n); + mmcodec_init_data(dbri); - /* Switch CS4215 to data mode - data sheet says - * "Set CLB=1 and send two more frames of valid control info" - */ - n = dbri_cmdlock(dbri); + return 0; +} - dbri->mm.ctrl[0] |= CS4215_CLB; - dbri->cmd[n++] = DBRI_CMD(D_SSP, 0, D_PIPE(D_P_17)); - dbri->cmd[n++] = reverse_bytes(*(int *)dbri->mm.ctrl, 4); - dbri_cmdsend(dbri, n); +/* +**************************************************************************** +******************** Interface with sparcaudio midlevel ******************** +**************************************************************************** +*/ - /* Two frames of control info @ 8kHz frame rate = 250 us delay */ - udelay(250); - n = dbri_cmdlock(dbri); +static int dbri_open(struct inode * inode, struct file * file, + struct sparcaudio_driver *drv) +{ + struct dbri *dbri = (struct dbri *)drv->private; - /* Now switch back to data mode */ - /* Reset CHI Anchor: Stop Send/Receive */ - val = D_DTS_VI | D_DTS_VO | D_DTS_INS | - D_DTS_PRVIN(D_P_16) | D_DTS_PRVOUT(D_P_16) | D_PIPE(D_P_16); - dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val); - dbri->cmd[n++] = D_TS_ANCHOR | D_TS_NEXT(D_P_16); - dbri->cmd[n++] = D_TS_ANCHOR | D_TS_NEXT(D_P_16); + MOD_INC_USE_COUNT; + return 0; +} - /* Setup DBRI for CHI Slave */ - dbri->cmd[n++] = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(0)); - /* dbri->cmd[n++] = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(0) | D_CHI_IR | D_CHI_EN); */ - dbri->cmd[n++] = DBRI_CMD(D_PAUSE, 0, 0x16); +static void dbri_release(struct inode * inode, struct file * file, + struct sparcaudio_driver *drv) +{ + MOD_DEC_USE_COUNT; +} +static int dbri_ioctl(struct inode * inode, struct file * file, + unsigned int x, unsigned long y, + struct sparcaudio_driver *drv) +{ + return 0; +} - /* Wait for command to complete */ - dbri_cmdsend(dbri, n); +static void dbri_audio_output_callback(void * callback_arg, int status) +{ + struct sparcaudio_driver *drv = callback_arg; - /* Switch CS4215 to data mode - set PIO3 to 1 */ - dbri->regs->reg2 = D_ENPIO | D_PIO1 | D_PIO3 | - (dbri->mm.onboard ? D_PIO0 : D_PIO2); + sparcaudio_output_done(drv, 1); } -static int mmcodec_init(struct sparcaudio_driver *drv) +static void dbri_start_output(struct sparcaudio_driver *drv, + __u8 * buffer, unsigned long count) { struct dbri *dbri = (struct dbri *)drv->private; - int reg2 = dbri->regs->reg2; - - /* Look for the cs4215 chips */ - if(reg2 & D_PIO2) { - dprintk(D_MM, ("DBRI: Onboard CS4215 detected\n")); - dbri->mm.onboard = 1; - } - if(reg2 & D_PIO0) { - dprintk(D_MM, ("DBRI: Speakerbox detected\n")); - dbri->mm.onboard = 0; - } - + /* Pipe 4 is audio transmit */ + xmit_on_pipe(dbri, 4, buffer, count, &dbri_audio_output_callback, drv); +} - /* Using the Speakerbox, if both are attached. */ - if((reg2 & D_PIO2) && (reg2 & D_PIO0)) { - printk("DBRI: Using speakerbox / ignoring onboard mmcodec.\n"); - dbri->regs->reg2 = D_ENPIO2; - dbri->mm.onboard = 0; - } - if( !(reg2 & (D_PIO0|D_PIO2)) ) { - printk("DBRI: no mmcodec found.\n"); - return -EIO; - } +static void dbri_stop_output(struct sparcaudio_driver *drv) +{ + struct dbri *dbri = (struct dbri *)drv->private; + reset_pipe(dbri, 4); +} - /* Now talk to our baby */ - dbri->regs->reg0 |= D_C; /* Enable CHI */ +static void dbri_start_input(struct sparcaudio_driver *drv, + __u8 * buffer, unsigned long len) +{ +} - mmcodec_default(&dbri->mm); +static void dbri_stop_input(struct sparcaudio_driver *drv) +{ +} - dbri->mm.version = 0xff; - mmcodec_setctrl(dbri); - if(dbri->mm.version == 0xff) - return -EIO; +static void dbri_audio_getdev(struct sparcaudio_driver *drv, + audio_device_t *devptr) +{ +} - mmcodec_init_data(dbri); +static int dbri_set_output_volume(struct sparcaudio_driver *drv, int volume) +{ + return 0; +} - return 0; +static int dbri_get_output_volume(struct sparcaudio_driver *drv) +{ + return 0; } -void dbri_isdn_init(struct dbri *dbri) +static int dbri_set_input_volume(struct sparcaudio_driver *drv, int volume) { - int n, val; + return 0; +} - /* Pipe 0: Receive D channel - * Pipe 8: Receive B1 channel - * Pipe 9: Receive B2 channel - * Pipe 1: Transmit D channel - * Pipe 10: Transmit B1 channel - * Pipe 11: Transmit B2 channel - */ +static int dbri_get_input_volume(struct sparcaudio_driver *drv) +{ + return 0; +} - n = dbri_cmdlock(dbri); +static int dbri_set_monitor_volume(struct sparcaudio_driver *drv, int volume) +{ + return 0; +} - /* Pipe 0: SDP */ - val = D_SDP_HDLC|D_SDP_FROM_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_0); - dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); - dbri->cmd[n++] = 0; - - /* Pipe 8: SDP */ - val = D_SDP_HDLC|D_SDP_FROM_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_8); - dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); - dbri->cmd[n++] = 0; - - /* Pipe 9: SDP */ - val = D_SDP_HDLC|D_SDP_FROM_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_9); - dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); - dbri->cmd[n++] = 0; - - /* Pipe 1: SDP */ - val = D_SDP_HDLC_D|D_SDP_TO_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_1); - dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); - dbri->cmd[n++] = 0; - - /* Pipe 10: SDP */ - val = D_SDP_HDLC|D_SDP_TO_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_10); - dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); - dbri->cmd[n++] = 0; - - /* Pipe 11: SDP */ - val = D_SDP_HDLC|D_SDP_TO_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_11); - dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); - dbri->cmd[n++] = 0; - - - dbri->cmd[n++] = DBRI_CMD(D_PAUSE, 0, 0); - - /* Pipe 0: DTS */ - val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_0) | D_PIPE(D_P_0); - dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val); - dbri->cmd[n++] = D_TS_LEN(2) | D_TS_CYCLE(17)| D_TS_NEXT(D_P_0); - dbri->cmd[n++] = 0; - - /* Pipe 8: DTS */ - val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_0) | D_PIPE(D_P_8); - dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val); - dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(0)| D_TS_NEXT(D_P_0); - dbri->cmd[n++] = 0; - - /* Pipe 9: DTS */ - val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_8) | D_PIPE(D_P_9); - dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val); - dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(8)| D_TS_NEXT(D_P_0); - dbri->cmd[n++] = 0; - - /* Pipe 1: DTS */ - val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_1) | D_PIPE(D_P_1); - dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val); - dbri->cmd[n++] = 0; - dbri->cmd[n++] = D_TS_LEN(2) | D_TS_CYCLE(17)| D_TS_NEXT(D_P_1); - - /* Pipe 10: DTS */ - val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_1) | D_PIPE(D_P_10); - dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val); - dbri->cmd[n++] = 0; - dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(0)| D_TS_NEXT(D_P_1); - - /* Pipe 11: DTS */ - val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_10) | D_PIPE(D_P_11); - dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val); - dbri->cmd[n++] = 0; - dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(8)| D_TS_NEXT(D_P_1); +static int dbri_get_monitor_volume(struct sparcaudio_driver *drv) +{ + return 0; +} +static int dbri_set_output_balance(struct sparcaudio_driver *drv, int balance) +{ + return 0; +} - /* Wait for command to complete */ - dbri_cmdsend(dbri, n); +static int dbri_get_output_balance(struct sparcaudio_driver *drv) +{ + return 0; } -void dbri_intr(int irq, void *dev_id, struct pt_regs *regs) +static int dbri_set_input_balance(struct sparcaudio_driver *drv, int balance) { - struct sparcaudio_driver *drv = (struct sparcaudio_driver *)dev_id; - struct dbri *dbri = (struct dbri *)drv->private; - int x, val; - static int numint = 0; - - /* - * Read it, so the interrupt goes away. - */ - x = dbri->regs->reg1; -#if 0 - if(numint++ > 20) { - dbri->regs->reg0 = D_R; /* Soft Reset */ - numint = 0; - printk("Soft reset\n"); - } -#endif + return 0; +} - if ( x & (D_MRR|D_MLE|D_LBG|D_MBE) ) { - /* - * What should I do here ? - */ - if(x & D_MRR) printk("DBRI: Multiple Error Ack on SBus\n"); - if(x & D_MLE) printk("DBRI: Multiple Late Error on SBus\n"); - if(x & D_LBG) printk("DBRI: Lost Bus Grant on SBus\n"); - if(x & D_MBE) printk("DBRI: Burst Error on SBus\n"); - } +static int dbri_get_input_balance(struct sparcaudio_driver *drv) +{ + return 0; +} - if (!(x & D_IR)) /* Not for us */ - return; +static int dbri_set_output_channels(struct sparcaudio_driver *drv, int chan) +{ + return 0; +} - x = dbri->intr[dbri->dbri_irqp]; - while (x != 0) { - dbri->intr[dbri->dbri_irqp] = 0; +static int dbri_get_output_channels(struct sparcaudio_driver *drv) +{ + return 0; +} - if(D_INTR_GETCHAN(x) == D_INTR_CMD) { - dprintk(D_INT,("DBRI: INTR: Command: %-5s Value:%d\n", - cmds[D_INTR_GETCMD(x)], D_INTR_GETVAL(x))); - } else { - dprintk(D_INT,("DBRI: INTR: Chan:%d Code:%d Val:%#x\n", - D_INTR_GETCHAN(x), D_INTR_GETCODE(x), - D_INTR_GETRVAL(x))); - } +static int dbri_set_input_channels(struct sparcaudio_driver *drv, int chan) +{ + return 0; +} - val = D_INTR_GETVAL(x); +static int dbri_get_input_channels(struct sparcaudio_driver *drv) +{ + return 0; +} - if (D_INTR_GETCODE(x) == D_INTR_SBRI) { - int liu_states[] = {1, 0, 8, 3, 4, 5, 6, 7}; - dbri->liu_state = liu_states[val & 0x7]; - if (dbri->liu_callback) - dbri->liu_callback(dbri->liu_callback_arg); - } +static int dbri_set_output_precision(struct sparcaudio_driver *drv, int prec) +{ + return 8; +} - switch(D_INTR_GETCHAN(x)) { - case D_INTR_CMD: -#if 0 - if(D_INTR_GETCMD(x) == D_WAIT) - if(val == WAIT_INTR1) { - dbri_cmdlocked = 0; - wake_up(&dbri->wait); - } - if(val == WAIT_INTR2) - wake_up(&dbri->int_wait); -#endif - break; +static int dbri_get_output_precision(struct sparcaudio_driver *drv) +{ + return 8; +} - case D_P_0: - /* Pipe 0 - D channel receive */ - if (D_INTR_GETCODE(x) == D_INTR_BRDY && - dbri->D.input_callback) { - dbri->D.input_callback(dbri->D.input_callback_arg, - DBRI_RD_STATUS(dbri->D.rd.flags), - DBRI_RD_CNT(dbri->D.rd.flags)-2); - } - break; - - case D_P_1: - /* Pipe 1 - D channel transmit */ - if (D_INTR_GETCODE(x) == D_INTR_XCMP && - dbri->D.output_callback) { - dbri->D.output_callback(dbri->D.output_callback_arg, - DBRI_TD_STATUS(dbri->D.rd.flags)&0xe); - } - break; - - case D_P_4: - /* Pipe 4 - audio transmit */ - if (D_INTR_GETCODE(x) == D_INTR_XCMP) { - sparcaudio_output_done(output_callback_arg, 1); - } - break; - - case D_P_8: - /* Pipe 8 - B1 channel receive */ - if (D_INTR_GETCODE(x) == D_INTR_BRDY && - dbri->B[0].input_callback) { - dbri->B[0].input_callback(dbri->B[0].input_callback_arg, - DBRI_RD_STATUS(dbri->B[0].rd.flags), - DBRI_RD_CNT(dbri->B[0].rd.flags)-2); - } - break; - - case D_P_9: - /* Pipe 9 - B2 channel receive */ - if (D_INTR_GETCODE(x) == D_INTR_BRDY && - dbri->B[1].input_callback) { - dbri->B[1].input_callback(dbri->B[1].input_callback_arg, - DBRI_RD_STATUS(dbri->B[1].rd.flags), - DBRI_RD_CNT(dbri->B[1].rd.flags)-2); - } - break; - - case D_P_10: - /* Pipe 10 - B1 channel transmit */ - if (D_INTR_GETCODE(x) == D_INTR_XCMP && - dbri->B[0].output_callback) { - dbri->B[0].output_callback(dbri->B[0].output_callback_arg, - DBRI_TD_STATUS(dbri->B[0].rd.flags)&0xfe); - } - break; - - case D_P_11: - /* Pipe 11 - B2 channel transmit */ - if (D_INTR_GETCODE(x) == D_INTR_XCMP && - dbri->B[1].output_callback) { - dbri->B[1].output_callback(dbri->B[1].output_callback_arg, - DBRI_TD_STATUS(dbri->B[1].rd.flags)&0xfe); - } - break; - - case D_P_18: - /* Pipe 18 - receive CS4215 status */ - if(val != 0) { - x = reverse_bytes(val,2)&CS4215_12_MASK; - printk("Comparing int: %x with hi(%x)\n", x, *(int *)dbri->mm.ctrl); - if(x == (*(int *)dbri->mm.ctrl >> 16)) { - printk("Comp ok\n"); - wake_up(&dbri->int_wait); - } - } - break; - case D_P_19: - /* Pipe 19 - receive CS4215 version */ - if(val != 0) { - dbri->mm.version = - reverse_bytes(val, 1) & 0xf; - } - break; - } +static int dbri_set_input_precision(struct sparcaudio_driver *drv, int prec) +{ + return 8; +} - dbri->dbri_irqp++; - if (dbri->dbri_irqp == (DBRI_NO_INTS * DBRI_INT_BLK)) - dbri->dbri_irqp = 1; - else if ((dbri->dbri_irqp & (DBRI_INT_BLK-1)) == 0) - dbri->dbri_irqp++; - x = dbri->intr[dbri->dbri_irqp]; - } +static int dbri_get_input_precision(struct sparcaudio_driver *drv) +{ + return 8; +} + +static int dbri_set_output_port(struct sparcaudio_driver *drv, int port) +{ + return 0; } +static int dbri_get_output_port(struct sparcaudio_driver *drv) +{ + return 0; +} -/* -**************************************************************************** -******************** Interface with sparcaudio midlevel ******************** -**************************************************************************** -*/ +static int dbri_set_input_port(struct sparcaudio_driver *drv, int port) +{ + return 0; +} +static int dbri_get_input_port(struct sparcaudio_driver *drv) +{ + return 0; +} -static void dummy() +static int dbri_set_output_encoding(struct sparcaudio_driver *drv, int enc) { + return 0; } -static int dbri_open(struct inode * inode, struct file * file, - struct sparcaudio_driver *drv) +static int dbri_get_output_encoding(struct sparcaudio_driver *drv) { - struct dbri *dbri = (struct dbri *)drv->private; + return 0; +} - MOD_INC_USE_COUNT; +static int dbri_set_input_encoding(struct sparcaudio_driver *drv, int enc) +{ + return 0; +} - return 0; +static int dbri_get_input_encoding(struct sparcaudio_driver *drv) +{ + return 0; } -static void dbri_release(struct inode * inode, struct file * file, - struct sparcaudio_driver *drv) +static int dbri_set_output_rate(struct sparcaudio_driver *drv, int rate) { - MOD_DEC_USE_COUNT; + return 0; } -static void dbri_start_output(struct sparcaudio_driver *drv, - __u8 * buffer, unsigned long count) +static int dbri_get_output_rate(struct sparcaudio_driver *drv) { - struct dbri *dbri = (struct dbri *)drv->private; - int val, n; + return 0; +} - if (count > (1 << 14) - 1) { - printk("dbri_start_output called with count=%d; truncated", count); - count = (1 << 14) - 1; - } +static int dbri_set_input_rate(struct sparcaudio_driver *drv, int rate) +{ + return 0; +} - n = dbri_cmdlock(dbri); +static int dbri_get_input_rate(struct sparcaudio_driver *drv) +{ + return 0; +} - dbri->mm.td.flags = DBRI_TD_F | DBRI_TD_B | DBRI_TD_D | DBRI_TD_CNT(count); - dbri->mm.td.ba = (__u32) buffer; - dbri->mm.td.nda = 0; - dbri->mm.td.status = 0; +static int dbri_sunaudio_getdev_sunos(struct sparcaudio_driver *drv) +{ + return 0; +} - /* Pipe 4 is audio transmit */ - val = D_SDP_MEM|D_SDP_TO_SER|D_SDP_P|D_SDP_MSB|D_PIPE(D_P_4); - dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); - dbri->cmd[n++] = (__u32)&dbri->mm.td; +static int dbri_get_output_ports(struct sparcaudio_driver *drv) +{ + return 0; +} - output_callback_arg = drv; +static int dbri_get_input_ports(struct sparcaudio_driver *drv) +{ + return 0; +} - dbri_cmdsend(dbri, n); +static int dbri_set_output_muted(struct sparcaudio_driver *drv, int mute) +{ + return 0; } -static void dbri_stop_output(struct sparcaudio_driver *drv) +static int dbri_get_output_muted(struct sparcaudio_driver *drv) { - struct dbri *dbri = (struct dbri *)drv->private; + return 0; } @@ -888,60 +1287,89 @@ static struct sparcaudio_operations dbri_ops = { dbri_open, dbri_release, - dummy, /* dbri_ioctl, */ + dbri_ioctl, dbri_start_output, dbri_stop_output, - dummy, /* dbri_start_input, */ - dummy, /* dbri_stop_input, */ - dummy, /* dbri_audio_getdev, */ - dummy, /* dbri_set_output_volume, */ - dummy, /* dbri_get_output_volume, */ - dummy, /* dbri_set_input_volume, */ - dummy, /* dbri_get_input_volume, */ - dummy, /* dbri_set_monitor_volume, */ - dummy, /* dbri_get_monitor_volume, */ - dummy, /* dbri_set_output_balance */ - dummy, /* dbri_get_output_balance, */ - dummy, /* dbri_set_input_balance */ - dummy, /* dbri_get_input_balance, */ - dummy, /* dbri_set_output_channels */ - dummy, /* dbri_get_output_channels, */ - dummy, /* dbri_set_input_channels */ - dummy, /* dbri_get_input_channels, */ - dummy, /* dbri_set_output_precision */ - dummy, /* dbri_get_output_precision, */ - dummy, /* dbri_set_input_precision */ - dummy, /* dbri_get_input_precision, */ - dummy, /* dbri_set_output_port */ - dummy, /* dbri_get_output_port, */ - dummy, /* dbri_set_input_port */ - dummy, /* dbri_get_input_port, */ - dummy, /* dbri_set_output_encoding */ - dummy, /* dbri_get_output_encoding, */ - dummy, /* dbri_set_input_encoding */ - dummy, /* dbri_get_input_encoding, */ - dummy, /* dbri_set_output_rate */ - dummy, /* dbri_get_output_rate, */ - dummy, /* dbri_set_input_rate */ - dummy, /* dbri_get_input_rate, */ - dummy, /* dbri_sunaudio_getdev_sunos, */ - dummy, /* dbri_get_output_ports, */ - dummy, /* dbri_get_input_ports, */ - dummy, /* dbri_set_output_muted */ - dummy, /* dbri_get_output_muted, */ + dbri_start_input, + dbri_stop_input, + dbri_audio_getdev, + dbri_set_output_volume, + dbri_get_output_volume, + dbri_set_input_volume, + dbri_get_input_volume, + dbri_set_monitor_volume, + dbri_get_monitor_volume, + dbri_set_output_balance, + dbri_get_output_balance, + dbri_set_input_balance, + dbri_get_input_balance, + dbri_set_output_channels, + dbri_get_output_channels, + dbri_set_input_channels, + dbri_get_input_channels, + dbri_set_output_precision, + dbri_get_output_precision, + dbri_set_input_precision, + dbri_get_input_precision, + dbri_set_output_port, + dbri_get_output_port, + dbri_set_input_port, + dbri_get_input_port, + dbri_set_output_encoding, + dbri_get_output_encoding, + dbri_set_input_encoding, + dbri_get_input_encoding, + dbri_set_output_rate, + dbri_get_output_rate, + dbri_set_input_rate, + dbri_get_input_rate, + dbri_sunaudio_getdev_sunos, + dbri_get_output_ports, + dbri_get_input_ports, + dbri_set_output_muted, + dbri_get_output_muted, }; + /* **************************************************************************** ************************** ISDN (Hisax) Interface ************************** **************************************************************************** */ + +void dbri_isdn_init(struct dbri *dbri) +{ + /* Pipe 0: Receive D channel + * Pipe 8: Receive B1 channel + * Pipe 9: Receive B2 channel + * Pipe 1: Transmit D channel + * Pipe 10: Transmit B1 channel + * Pipe 11: Transmit B2 channel + */ + + setup_pipe(dbri, 0, D_SDP_HDLC | D_SDP_FROM_SER | D_SDP_LSB); + setup_pipe(dbri, 8, D_SDP_HDLC | D_SDP_FROM_SER | D_SDP_LSB); + setup_pipe(dbri, 9, D_SDP_HDLC | D_SDP_FROM_SER | D_SDP_LSB); + + setup_pipe(dbri, 1, D_SDP_HDLC_D | D_SDP_TO_SER | D_SDP_LSB); + setup_pipe(dbri,10, D_SDP_HDLC | D_SDP_TO_SER | D_SDP_LSB); + setup_pipe(dbri,11, D_SDP_HDLC | D_SDP_TO_SER | D_SDP_LSB); + + link_time_slot(dbri, 0, PIPEinput, 0, 2, 17); + link_time_slot(dbri, 8, PIPEinput, 8, 8, 0); + link_time_slot(dbri, 9, PIPEinput, 9, 8, 8); + + link_time_slot(dbri, 1, PIPEoutput, 1, 2, 17); + link_time_slot(dbri, 10, PIPEoutput, 1, 8, 0); + link_time_slot(dbri, 11, PIPEoutput, 10, 8, 8); +} + int dbri_get_irqnum(int dev) { struct dbri *dbri; - if (dev > num_drivers) { + if (dev >= num_drivers) { return(0); } @@ -955,7 +1383,7 @@ { struct dbri *dbri; - if (dev > num_drivers) { + if (dev >= num_drivers) { return(0); } @@ -964,11 +1392,13 @@ return dbri->liu_state; } +void dbri_liu_activate(int dev, int priority); + void dbri_liu_init(int dev, void (*callback)(void *), void *callback_arg) { struct dbri *dbri; - if (dev > num_drivers) { + if (dev >= num_drivers) { return; } @@ -979,26 +1409,28 @@ dbri->liu_callback_arg = callback_arg; dbri_isdn_init(dbri); + dbri_liu_activate(dev, 0); } void dbri_liu_activate(int dev, int priority) { struct dbri *dbri; - int n, val; + int val; + volatile int *cmd; - if (dev > num_drivers) { + if (dev >= num_drivers) { return; } dbri = (struct dbri *) drivers[dev].private; - n = dbri_cmdlock(dbri); + cmd = dbri_cmdlock(dbri); /* Turn on the ISDN TE interface and request activation */ val = D_NT_IRM_IMM | D_NT_IRM_EN | D_NT_ACT; - dbri->cmd[n++] = DBRI_CMD(D_TE, 0, val); + *(cmd++) = DBRI_CMD(D_TE, 0, val); - dbri_cmdsend(dbri, n); + dbri_cmdsend(dbri, cmd); /* Activate the interface */ dbri->regs->reg0 |= D_T; @@ -1008,7 +1440,7 @@ { struct dbri *dbri; - if (dev > num_drivers) { + if (dev >= num_drivers) { return; } @@ -1022,37 +1454,15 @@ void (*callback)(void *, int), void *callback_arg) { struct dbri *dbri; - int n, val; - if (dev > num_drivers) { + if (dev >= num_drivers) { return; } dbri = (struct dbri *) drivers[dev].private; - if (count > (1 << 14) - 1) { - printk("dbri_dxmit called with count=%d; truncated", count); - count = (1 << 14) - 1; - } - - n = dbri_cmdlock(dbri); - - /* XXX - Shouldn't I check to make sure D.td isn't is use? */ - - dbri->D.td.flags = DBRI_TD_F | DBRI_TD_B | DBRI_TD_CNT(count) | DBRI_TD_I; - dbri->D.td.ba = (__u32) buffer; - dbri->D.td.nda = 0; - dbri->D.td.status = 0; - - /* Pipe 1 is D channel transmit */ - val = D_SDP_HDLC_D|D_SDP_TO_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_1); - dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); - dbri->cmd[n++] = (__u32)&dbri->D.td; - - dbri->D.output_callback = callback; - dbri->D.output_callback_arg = callback_arg; - - dbri_cmdsend(dbri, n); + /* Pipe 1 is D channel transmit */ + xmit_on_pipe(dbri, 1, buffer, count, callback, callback_arg); } void dbri_drecv(int dev, __u8 *buffer, unsigned int size, @@ -1060,106 +1470,63 @@ void *callback_arg) { struct dbri *dbri; - int n, val; - if (dev > num_drivers) { + if (dev >= num_drivers) { return; } dbri = (struct dbri *) drivers[dev].private; - if (size > (1 << 14) - 1) { - printk("dbri_drecv called with size=%d; truncated", size); - size = (1 << 14) - 1; - } - - /* Make sure size is a multiple of four */ - size &= ~3; - - n = dbri_cmdlock(dbri); - - /* XXX - Shouldn't I check to make sure D.rd isn't is use? */ - - dbri->D.rd.flags = 0; - dbri->D.rd.ba = (__u32) buffer; - dbri->D.rd.nda = 0; - dbri->D.rd.status = DBRI_RD_B | DBRI_RD_BCNT(size); - - /* Pipe 0 is D channel receive */ - val = D_SDP_HDLC|D_SDP_FROM_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_0); - dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); - dbri->cmd[n++] = (__u32)&dbri->D.rd; - - dbri_cmdsend(dbri, n); - - dbri->D.input_callback = callback; - dbri->D.input_callback_arg = callback_arg; + /* Pipe 0 is D channel receive */ + recv_on_pipe(dbri, 0, buffer, size, callback, callback_arg); } int dbri_bopen(int dev, unsigned int chan, int hdlcmode, u_char xmit_idle_char) { struct dbri *dbri; - int n, val; + int val; - if (dev > num_drivers || chan > 1) { + if (dev >= num_drivers || chan > 1) { return -1; } dbri = (struct dbri *) drivers[dev].private; - if (hdlcmode) { - - return -1; - - /* Pipe 8/9: receive B1/B2 channel */ - dbri->B[chan].recvSDP = D_SDP_HDLC|D_SDP_FROM_SER|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_8+chan); - - /* Pipe 10/11: transmit B1/B2 channel */ - dbri->B[chan].xmitSDP = D_SDP_HDLC|D_SDP_TO_SER|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_10+chan); + if (hdlcmode) { - } else { /* !hdlcmode means transparent */ - - /* Pipe 8/9: receive B1/B2 channel */ - dbri->B[chan].recvSDP = D_SDP_MEM|D_SDP_FROM_SER|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_8+chan); - - /* Pipe 10/11: transmit B1/B2 channel */ - dbri->B[chan].xmitSDP = D_SDP_MEM|D_SDP_TO_SER|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_10+chan); - - } + /* return -1; */ - n = dbri_cmdlock(dbri); + /* Pipe 8/9: receive B1/B2 channel */ + setup_pipe(dbri, 8+chan, D_SDP_HDLC | D_SDP_FROM_SER|D_SDP_LSB); - /* Pipe 8/9: receive B1/B2 channel */ - val = dbri->B[chan].recvSDP | D_SDP_C; - dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); - dbri->cmd[n++] = 0; + /* Pipe 10/11: transmit B1/B2 channel */ + setup_pipe(dbri,10+chan, D_SDP_HDLC | D_SDP_TO_SER | D_SDP_LSB); - /* Pipe 10/11: transmit B1/B2 channel */ - val = dbri->B[chan].xmitSDP | D_SDP_C; - dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); - dbri->cmd[n++] = 0; + } else { /* !hdlcmode means transparent */ - dbri->B[chan].output_callback = NULL; - dbri->B[chan].input_callback = NULL; + /* Pipe 8/9: receive B1/B2 channel */ + setup_pipe(dbri, 8+chan, D_SDP_MEM | D_SDP_FROM_SER|D_SDP_LSB); - dbri_cmdsend(dbri, n); + /* Pipe 10/11: transmit B1/B2 channel */ + setup_pipe(dbri,10+chan, D_SDP_MEM | D_SDP_TO_SER | D_SDP_LSB); - return 0; + } + return 0; } void dbri_bclose(int dev, unsigned int chan) { struct dbri *dbri; - if (dev > num_drivers || chan > 1) { + if (dev >= num_drivers || chan > 1) { return; } dbri = (struct dbri *) drivers[dev].private; - dbri->B[chan].output_callback = NULL; - dbri->B[chan].input_callback = NULL; + reset_pipe(dbri, 8+chan); + reset_pipe(dbri, 10+chan); } void dbri_bxmit(int dev, unsigned int chan, @@ -1168,37 +1535,15 @@ void *callback_arg) { struct dbri *dbri; - int n, val; - if (dev > num_drivers || chan > 1) { + if (dev >= num_drivers || chan > 1) { return; } dbri = (struct dbri *) drivers[dev].private; - if (count > (1 << 14) - 1) { - printk("dbri_bxmit called with count=%ld; truncated", count); - count = (1 << 14) - 1; - } - - n = dbri_cmdlock(dbri); - - /* XXX - Shouldn't I check to make sure td isn't is use? */ - - dbri->B[chan].td.flags = DBRI_TD_F | DBRI_TD_B | DBRI_TD_CNT(count); - dbri->B[chan].td.ba = (__u32) buffer; - dbri->B[chan].td.nda = 0; - dbri->B[chan].td.status = 0; - - /* Pipe 10/11 is B1/B2 channel transmit */ - val = dbri->B[chan].xmitSDP; - dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); - dbri->cmd[n++] = (__u32)&dbri->B[chan].td; - - dbri->B[chan].output_callback = callback; - dbri->B[chan].output_callback_arg = callback_arg; - - dbri_cmdsend(dbri, n); + /* Pipe 10/11 is B1/B2 channel transmit */ + xmit_on_pipe(dbri, 10+chan, buffer, count, callback, callback_arg); } void dbri_brecv(int dev, unsigned int chan, @@ -1207,40 +1552,15 @@ void *callback_arg) { struct dbri *dbri; - int n, val; - if (dev > num_drivers || chan > 1) { + if (dev >= num_drivers || chan > 1) { return; } dbri = (struct dbri *) drivers[dev].private; - if (size > (1 << 14) - 1) { - printk("dbri_brecv called with size=%ld; truncated", size); - size = (1 << 14) - 1; - } - - /* Make sure size is a multiple of four */ - size &= ~3; - - n = dbri_cmdlock(dbri); - - /* XXX - Shouldn't I check to make sure RD isn't is use? */ - - dbri->B[chan].rd.flags = 0; - dbri->B[chan].rd.ba = (__u32) buffer; - dbri->B[chan].rd.nda = 0; - dbri->B[chan].rd.status = DBRI_RD_B | DBRI_RD_BCNT(size); - - /* Pipe 8/9 is B1/B2 channel receive */ - val = dbri->B[chan].recvSDP; - dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); - dbri->cmd[n++] = (__u32)&dbri->B[chan].rd; - - dbri_cmdsend(dbri, n); - - dbri->B[chan].input_callback = callback; - dbri->B[chan].input_callback_arg = callback_arg; + /* Pipe 8/9 is B1/B2 channel receive */ + recv_on_pipe(dbri, 8+chan, buffer, size, callback, callback_arg); } #if defined(DBRI_ISDN) || defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x200ff @@ -1271,6 +1591,7 @@ { struct dbri *dbri; struct linux_prom_irqs irq; + __u32 dma_dvma; int err; if (sdev->prom_name[9] < 'e') { @@ -1287,7 +1608,13 @@ memset(dbri, 0, sizeof(*dbri)); + /* sparc_dvma_malloc() will halt the kernel if the malloc fails */ + dbri->dma = sparc_dvma_malloc (sizeof (struct dbri_dma), + "DBRI DMA Cmd Block", &dma_dvma); + dbri->dma_dvma = (struct dbri_dma *) dma_dvma; + dbri->dbri_version = sdev->prom_name[9]; + dbri->sdev = sdev; /* Map the registers into memory. */ prom_apply_sbus_ranges(sdev->my_bus, &sdev->reg_addrs[0], @@ -1295,7 +1622,7 @@ dbri->regs_size = sdev->reg_addrs[0].reg_size; dbri->regs = sparc_alloc_io(sdev->reg_addrs[0].phys_addr, 0, sdev->reg_addrs[0].reg_size, - drv_name, sdev->reg_addrs[0].which_io, 0); + "DBRI Registers", sdev->reg_addrs[0].which_io, 0); if (!dbri->regs) { printk(KERN_ERR "DBRI: could not allocate registers\n"); kfree(drv->private); @@ -1305,7 +1632,8 @@ prom_getproperty(sdev->prom_node, "intr", (char *)&irq, sizeof(irq)); dbri->irq = irq.pri; - err = request_irq(dbri->irq, dbri_intr, SA_SHIRQ, "DBRI/audio", drv); + err = request_irq(dbri->irq, dbri_intr, SA_SHIRQ, + "DBRI audio/ISDN", dbri); if (err) { printk(KERN_ERR "DBRI: Can't get irq %d\n", dbri->irq); sparc_free_io(dbri->regs, dbri->regs_size); @@ -1313,23 +1641,20 @@ return err; } + dbri_initialize(dbri); + err = mmcodec_init(drv); + if(err) { + dbri_detach(dbri); + return err; + } + /* Register ourselves with the midlevel audio driver. */ err = register_sparcaudio_driver(drv,1); if (err) { printk(KERN_ERR "DBRI: unable to register audio\n"); - free_irq(dbri->irq, drv); - sparc_free_io(dbri->regs, dbri->regs_size); - kfree(drv->private); - return err; - } - - dbri_initialize(drv); - err = mmcodec_init(drv); - if(err) { - dbri_detach(drv); + dbri_detach(dbri); return err; } - dbri->perchip_info.play.active = dbri->perchip_info.play.pause = 0; dbri->perchip_info.record.active = dbri->perchip_info.record.pause = 0; @@ -1380,7 +1705,8 @@ register int i; for (i = 0; i < num_drivers; i++) { - dbri_detach(&drivers[i]); + dbri_detach((struct dbri *) drivers[i].private); + unregister_sparcaudio_driver(& drivers[i], 1); num_drivers--; } } diff -u --recursive --new-file v2.2.3/linux/drivers/sbus/audio/dbri.h linux/drivers/sbus/audio/dbri.h --- v2.2.3/linux/drivers/sbus/audio/dbri.h Fri Jan 8 22:36:09 1999 +++ linux/drivers/sbus/audio/dbri.h Mon Mar 15 16:11:30 1999 @@ -22,53 +22,77 @@ #define DBRI_NO_CMDS 64 #define DBRI_NO_INTS 2 #define DBRI_INT_BLK 64 +#define DBRI_NO_DESCS 64 #define DBRI_MM_ONB 1 #define DBRI_MM_SB 2 struct dbri_mem { - __u32 flags; + __u32 word1; __u32 ba; /* Transmit/Receive Buffer Address */ __u32 nda; /* Next Descriptor Address */ - __u32 status; + __u32 word4; }; -struct dbri_channel { - struct dbri_mem td; - struct dbri_mem rd; - unsigned int recvSDP; - unsigned int xmitSDP; +#include "cs4215.h" + +/* This structure is in a DMA region where it can accessed by both + * the CPU and the DBRI + */ + +struct dbri_dma { + int cmd[DBRI_NO_CMDS]; /* Place for commands */ + int intr[DBRI_NO_INTS * DBRI_INT_BLK]; /* Interrupt field */ + struct dbri_mem desc[DBRI_NO_DESCS]; /* Xmit/receive descriptors */ +}; + +struct dbri_pipe { + u32 sdp; /* SDP command word */ + int nextpipe; /* Next pipe in linked list */ + int cycle; /* Offset of timeslot (bits) */ + int length; /* Length of timeslot (bits) */ + int desc; /* Index of active descriptor*/ + __u32 *recv_fixed_ptr; /* Ptr to receive fixed data */ +}; + +struct dbri_desc { + int inuse; /* Boolean flag */ + int next; /* Index of next desc, or -1 */ + void *buffer; + unsigned int len; void (*output_callback)(void *, int); void *output_callback_arg; void (*input_callback)(void *, int, unsigned int); void *input_callback_arg; }; -#include "cs4215.h" - /* This structure holds the information for both chips (DBRI & CS4215) */ + struct dbri { int regs_size, irq; /* Needed for unload */ + struct linux_sbus_device *sdev; + + volatile struct dbri_dma *dma; /* Pointer to our DMA block */ + struct dbri_dma *dma_dvma; /* DBRI visible DMA address */ struct dbri_regs *regs; /* dbri HW regs */ int dbri_version; /* 'e' and up is OK */ int dbri_irqp; /* intr queue pointer */ - __volatile__ int cmd[DBRI_NO_CMDS]; /* Place for commands */ - __volatile__ int intr[DBRI_NO_INTS * DBRI_INT_BLK]; /* Interrupt field */ - + + struct dbri_pipe pipes[32]; /* DBRI's 32 data pipes */ + struct dbri_desc descs[DBRI_NO_DESCS]; + struct cs4215 mm; /* mmcodec special info */ - + +#if 0 struct wait_queue *wait, *int_wait; /* Where to sleep if busy */ +#endif struct audio_info perchip_info; /* Track ISDN LIU and notify changes */ int liu_state; void (*liu_callback)(void *); void *liu_callback_arg; - - /* Callback routines and descriptors for ISDN channels */ - struct dbri_channel D; - struct dbri_channel B[2]; }; @@ -145,6 +169,7 @@ #define D_SDP_HDLC_D (3<<13) /* D Channel (prio control)*/ #define D_SDP_SER (4<<13) /* Serial to serial */ #define D_SDP_FIXED (6<<13) /* Short only */ +#define D_SDP_MODE(v) ((v)&(7<<13)) #define D_SDP_TO_SER (1<<12) /* Direction */ #define D_SDP_FROM_SER (0<<12) /* Direction */ @@ -163,8 +188,8 @@ #define D_DTS_PRVOUT(v) ((v)<<5) /* Previous Out Pipe */ /* Time Slot defines */ -#define D_TS_LEN(v) (v<<24) /* Number of bits in this time slot */ -#define D_TS_CYCLE(v) (v<<14) /* Bit Count at start of TS */ +#define D_TS_LEN(v) ((v)<<24) /* Number of bits in this time slot */ +#define D_TS_CYCLE(v) ((v)<<14) /* Bit Count at start of TS */ #define D_TS_DI(v) (1<<13) /* Data Invert */ #define D_TS_1CHANNEL (0<<10) /* Single Channel / Normal mode */ #define D_TS_MONITOR (2<<10) /* Monitor pipe */ @@ -174,13 +199,13 @@ #define D_TS_NEXT(v) ((v)<<0) /* Pipe Nr: 0-15 long, 16-21 short */ /* Concentration Highway Interface Modes */ -#define D_CHI_CHICM(v) (v<<16) /* Clock mode */ +#define D_CHI_CHICM(v) ((v)<<16) /* Clock mode */ #define D_CHI_IR (1<<15) /* Immediate Interrupt Report */ #define D_CHI_EN (1<<14) /* CHIL Interrupt enabled */ #define D_CHI_OD (1<<13) /* Open Drain Enable */ #define D_CHI_FE (1<<12) /* Sample CHIFS on Rising Frame Edge */ #define D_CHI_FD (1<<11) /* Frame Drive */ -#define D_CHI_BPF(v) (v<<0) /* Bits per Frame */ +#define D_CHI_BPF(v) ((v)<<0) /* Bits per Frame */ /* NT: These are here for completeness */ #define D_NT_FBIT (1<<17) /* Frame Bit */ @@ -199,13 +224,13 @@ #define D_NT_ABV (1<<0) /* Activate Bipolar Violation */ /* Codec Setup */ -#define D_CDEC_CK(v) (v<<24) /* Clock Select */ -#define D_CDEC_FED(v) (v<<12) /* FSCOD Falling Edge Delay */ -#define D_CDEC_RED(v) (v<<0) /* FSCOD Rising Edge Delay */ +#define D_CDEC_CK(v) ((v)<<24) /* Clock Select */ +#define D_CDEC_FED(v) ((v)<<12) /* FSCOD Falling Edge Delay */ +#define D_CDEC_RED(v) ((v)<<0) /* FSCOD Rising Edge Delay */ /* Test */ -#define D_TEST_RAM(v) (v<<16) /* RAM Pointer */ -#define D_TEST_SIZE(v) (v<<11) /* */ +#define D_TEST_RAM(v) ((v)<<16) /* RAM Pointer */ +#define D_TEST_SIZE(v) ((v)<<11) /* */ #define D_TEST_ROMONOFF 0x5 /* Toggle ROM opcode monitor on/off */ #define D_TEST_PROC 0x6 /* MicroProcessor test */ #define D_TEST_SER 0x7 /* Serial-Controller test */ @@ -245,11 +270,11 @@ #define D_INTR_CHI 36 #define D_INTR_CMD 38 -#define D_INTR_GETCHAN(v) ((v>>24) & 0x3f) -#define D_INTR_GETCODE(v) ((v>>20) & 0xf) -#define D_INTR_GETCMD(v) ((v>>16) & 0xf) -#define D_INTR_GETVAL(v) (v & 0xffff) -#define D_INTR_GETRVAL(v) (v & 0xfffff) +#define D_INTR_GETCHAN(v) (((v)>>24) & 0x3f) +#define D_INTR_GETCODE(v) (((v)>>20) & 0xf) +#define D_INTR_GETCMD(v) (((v)>>16) & 0xf) +#define D_INTR_GETVAL(v) ((v) & 0xffff) +#define D_INTR_GETRVAL(v) ((v) & 0xfffff) #define D_P_0 0 /* TE receive anchor */ #define D_P_1 1 /* TE transmit anchor */ @@ -288,11 +313,11 @@ /* Transmit descriptor defines */ #define DBRI_TD_F (1<<31) /* End of Frame */ #define DBRI_TD_D (1<<30) /* Do not append CRC */ -#define DBRI_TD_CNT(v) (v<<16) /* Number of valid bytes in the buffer */ +#define DBRI_TD_CNT(v) ((v)<<16) /* Number of valid bytes in the buffer */ #define DBRI_TD_B (1<<15) /* Final interrupt */ #define DBRI_TD_M (1<<14) /* Marker interrupt */ #define DBRI_TD_I (1<<13) /* Transmit Idle Characters */ -#define DBRI_TD_FCNT(v) v /* Flag Count */ +#define DBRI_TD_FCNT(v) (v) /* Flag Count */ #define DBRI_TD_UNR (1<<3) /* Underrun: transmitter is out of data */ #define DBRI_TD_ABT (1<<2) /* Abort: frame aborted */ #define DBRI_TD_TBC (1<<0) /* Transmit buffer Complete */ @@ -303,12 +328,12 @@ #define DBRI_RD_C (1<<30) /* Completed buffer */ #define DBRI_RD_B (1<<15) /* Final interrupt */ #define DBRI_RD_M (1<<14) /* Marker interrupt */ -#define DBRI_RD_BCNT(v) v /* Buffer size */ +#define DBRI_RD_BCNT(v) (v) /* Buffer size */ #define DBRI_RD_CRC (1<<7) /* 0: CRC is correct */ #define DBRI_RD_BBC (1<<6) /* 1: Bad Byte received */ #define DBRI_RD_ABT (1<<5) /* Abort: frame aborted */ #define DBRI_RD_OVRN (1<<3) /* Overrun: data lost */ #define DBRI_RD_STATUS(v) ((v)&0xff) /* Receive status */ -#define DBRI_RD_CNT(v) ((v>>16)&0x1fff) /* Number of valid bytes in the buffer */ +#define DBRI_RD_CNT(v) (((v)>>16)&0x1fff) /* Number of valid bytes in the buffer */ #endif /* _DBRI_H_ */ diff -u --recursive --new-file v2.2.3/linux/drivers/sbus/audio/dmy.c linux/drivers/sbus/audio/dmy.c --- v2.2.3/linux/drivers/sbus/audio/dmy.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/sbus/audio/dmy.c Mon Mar 15 16:11:30 1999 @@ -0,0 +1,737 @@ +/* + * drivers/sbus/audio/dummy.c + * + * Copyright 1998 Derrick J Brashear (shadow@andrew.cmu.edu) + * + * This is a dummy lowlevel driver. Consider it a distant cousin of + * /proc/audio; It pretends to be a piece of audio hardware, and writes + * to a file instead. (or will shortly) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "dummy.h" + +#define MAX_DRIVERS 1 +static struct sparcaudio_driver drivers[MAX_DRIVERS]; +static int num_drivers; + +static int dummy_play_gain(struct sparcaudio_driver *drv, int value, + unsigned char balance); +static int dummy_record_gain(struct sparcaudio_driver *drv, int value, + unsigned char balance); +static int dummy_output_muted(struct sparcaudio_driver *drv, int value); +static int dummy_attach(struct sparcaudio_driver *drv); + +static int +dummy_set_output_encoding(struct sparcaudio_driver *drv, int value) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + if (value != 0) { + dummy_chip->perchip_info.play.encoding = value; + return 0; + } + return -EINVAL; +} + +static int +dummy_set_input_encoding(struct sparcaudio_driver *drv, int value) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + if (value != 0) { + dummy_chip->perchip_info.record.encoding = value; + return 0; + } + return -EINVAL; +} + +static int dummy_get_output_encoding(struct sparcaudio_driver *drv) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + return dummy_chip->perchip_info.play.encoding; +} + +static int dummy_get_input_encoding(struct sparcaudio_driver *drv) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + return dummy_chip->perchip_info.record.encoding; +} + +static int +dummy_set_output_rate(struct sparcaudio_driver *drv, int value) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + if (value != 0) { + dummy_chip->perchip_info.play.sample_rate = value; + return 0; + } + return -EINVAL; +} + +static int +dummy_set_input_rate(struct sparcaudio_driver *drv, int value) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + if (value != 0) { + dummy_chip->perchip_info.record.sample_rate = value; + return 0; + } + return -EINVAL; +} + +static int dummy_get_output_rate(struct sparcaudio_driver *drv) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + return dummy_chip->perchip_info.play.sample_rate; +} + +static int dummy_get_input_rate(struct sparcaudio_driver *drv) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + return dummy_chip->perchip_info.record.sample_rate; +} + +/* Generically we support 4 channels. This does 2 */ +static int +dummy_set_output_channels(struct sparcaudio_driver *drv, int value) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + switch (value) { + case 1: + case 2: + break; + default: + return -(EINVAL); + } + dummy_chip->perchip_info.play.channels = value; + return 0; +} + +/* Generically we support 4 channels. This does 2 */ +static int +dummy_set_input_channels(struct sparcaudio_driver *drv, int value) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + switch (value) { + case 1: + case 2: + break; + default: + return -(EINVAL); + } + dummy_chip->perchip_info.record.channels = value; + return 0; +} + +static int dummy_get_input_channels(struct sparcaudio_driver *drv) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + return dummy_chip->perchip_info.record.channels; +} + +static int dummy_get_output_channels(struct sparcaudio_driver *drv) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + return dummy_chip->perchip_info.play.channels; +} + +static int dummy_get_output_precision(struct sparcaudio_driver *drv) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + return dummy_chip->perchip_info.play.precision; +} + +static int dummy_get_input_precision(struct sparcaudio_driver *drv) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + return dummy_chip->perchip_info.record.precision; +} + +static int dummy_set_output_precision(struct sparcaudio_driver *drv, int val) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + dummy_chip->perchip_info.play.precision = val; + return dummy_chip->perchip_info.play.precision; +} + +static int dummy_set_input_precision(struct sparcaudio_driver *drv, int val) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + dummy_chip->perchip_info.record.precision = val; + return dummy_chip->perchip_info.record.precision; +} + +/* Set output mute */ +static int dummy_output_muted(struct sparcaudio_driver *drv, int value) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + if (!value) + dummy_chip->perchip_info.output_muted = 0; + else + dummy_chip->perchip_info.output_muted = 1; + return 0; +} + +static int dummy_get_output_muted(struct sparcaudio_driver *drv) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + return dummy_chip->perchip_info.output_muted; +} + +static int dummy_get_formats(struct sparcaudio_driver *drv) +{ + return (AFMT_MU_LAW | AFMT_A_LAW | AFMT_U8 | AFMT_IMA_ADPCM | + AFMT_S16_LE | AFMT_S16_BE); +} + +static int dummy_get_output_ports(struct sparcaudio_driver *drv) +{ + return (AUDIO_LINE_OUT | AUDIO_SPEAKER | AUDIO_HEADPHONE); +} + +static int dummy_get_input_ports(struct sparcaudio_driver *drv) +{ + return (AUDIO_ANALOG_LOOPBACK); +} + +/* Set chip "output" port */ +static int dummy_set_output_port(struct sparcaudio_driver *drv, int value) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + dummy_chip->perchip_info.play.port = value; + return (value); +} + +static int dummy_set_input_port(struct sparcaudio_driver *drv, int value) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + dummy_chip->perchip_info.record.port = value; + return (value); +} + +static int dummy_get_output_port(struct sparcaudio_driver *drv) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + return dummy_chip->perchip_info.play.port; +} + +static int dummy_get_input_port(struct sparcaudio_driver *drv) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + return dummy_chip->perchip_info.record.port; +} + +static int dummy_get_output_error(struct sparcaudio_driver *drv) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + return (int)dummy_chip->perchip_info.play.error; +} + +static int dummy_get_input_error(struct sparcaudio_driver *drv) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + return (int)dummy_chip->perchip_info.record.error; +} + +static int dummy_get_output_samples(struct sparcaudio_driver *drv) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + return dummy_chip->perchip_info.play.samples; +} + +static int dummy_get_output_pause(struct sparcaudio_driver *drv) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + return (int)dummy_chip->perchip_info.play.pause; +} + +static int dummy_set_output_volume(struct sparcaudio_driver *drv, int value) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + dummy_play_gain(drv, value, dummy_chip->perchip_info.play.balance); + return 0; +} + +static int dummy_get_output_volume(struct sparcaudio_driver *drv) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + return dummy_chip->perchip_info.play.gain; +} + +static int dummy_set_output_balance(struct sparcaudio_driver *drv, int value) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + dummy_chip->perchip_info.play.balance = value; + dummy_play_gain(drv, dummy_chip->perchip_info.play.gain, + dummy_chip->perchip_info.play.balance); + return 0; +} + +static int dummy_get_output_balance(struct sparcaudio_driver *drv) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + return (int)dummy_chip->perchip_info.play.balance; +} + +/* Set chip play gain */ +static int dummy_play_gain(struct sparcaudio_driver *drv, int value, unsigned char balance) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + int tmp = 0, r, l, r_adj, l_adj; + r = l = value; + if (balance < AUDIO_MID_BALANCE) { + r = (int)(value - ((AUDIO_MID_BALANCE - balance) << AUDIO_BALANCE_SHIFT)); + if (r < 0) r = 0; + } else if (balance > AUDIO_MID_BALANCE) { + l = (int)(value - ((balance - AUDIO_MID_BALANCE) << AUDIO_BALANCE_SHIFT)); + if (l < 0) l = 0; + } + (l == 0) ? (l_adj = DUMMY_MAX_DEV_ATEN) : (l_adj = DUMMY_MAX_ATEN - + (l * (DUMMY_MAX_ATEN + 1) / + (AUDIO_MAX_GAIN + 1))); + (r == 0) ? (r_adj = DUMMY_MAX_DEV_ATEN) : (r_adj = DUMMY_MAX_ATEN - + (r * (DUMMY_MAX_ATEN + 1) / + (AUDIO_MAX_GAIN + 1))); + if ((value == 0) || (value == AUDIO_MAX_GAIN)) { + tmp = value; + } else { + if (value == l) + tmp = ((DUMMY_MAX_ATEN - l_adj) * (AUDIO_MAX_GAIN + 1) / + (DUMMY_MAX_ATEN + 1)); + else if (value == r) + tmp = ((DUMMY_MAX_ATEN - r_adj) * (AUDIO_MAX_GAIN + 1) / + (DUMMY_MAX_ATEN + 1)); + } + dummy_chip->perchip_info.play.gain = tmp; + return 0; +} + +static int dummy_get_input_samples(struct sparcaudio_driver *drv) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + return dummy_chip->perchip_info.record.samples; +} + +static int dummy_get_input_pause(struct sparcaudio_driver *drv) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + return (int)dummy_chip->perchip_info.record.pause; +} + +static int dummy_set_monitor_volume(struct sparcaudio_driver *drv, int value) +{ + return 0; +} + +static int dummy_get_monitor_volume(struct sparcaudio_driver *drv) +{ + return 0; +} + +static int dummy_set_input_volume(struct sparcaudio_driver *drv, int value) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + dummy_record_gain(drv, value, dummy_chip->perchip_info.record.balance); + return 0; +} + +static int dummy_get_input_volume(struct sparcaudio_driver *drv) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + return dummy_chip->perchip_info.record.gain; +} + +static int dummy_set_input_balance(struct sparcaudio_driver *drv, int value) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + dummy_chip->perchip_info.record.balance = value; + dummy_record_gain(drv, dummy_chip->perchip_info.record.gain, + dummy_chip->perchip_info.play.balance); + return 0; +} + +static int dummy_get_input_balance(struct sparcaudio_driver *drv) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + return (int)dummy_chip->perchip_info.record.balance; +} + +static int dummy_record_gain(struct sparcaudio_driver *drv, int value, unsigned char balance) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + int tmp = 0, r, l, r_adj, l_adj; + r = l = value; + if (balance < AUDIO_MID_BALANCE) { + r = (int)(value - ((AUDIO_MID_BALANCE - balance) << AUDIO_BALANCE_SHIFT)); + if (r < 0) r = 0; + } else if (balance > AUDIO_MID_BALANCE) { + l = (int)(value - ((balance - AUDIO_MID_BALANCE) << AUDIO_BALANCE_SHIFT)); + if (l < 0) l = 0; + } + (l == 0) ? (l_adj = DUMMY_MAX_DEV_ATEN) : (l_adj = DUMMY_MAX_ATEN - + (l * (DUMMY_MAX_ATEN + 1) / + (AUDIO_MAX_GAIN + 1))); + (r == 0) ? (r_adj = DUMMY_MAX_DEV_ATEN) : (r_adj = DUMMY_MAX_ATEN - + (r * (DUMMY_MAX_ATEN + 1) / + (AUDIO_MAX_GAIN + 1))); + if ((value == 0) || (value == AUDIO_MAX_GAIN)) { + tmp = value; + } else { + if (value == l) + tmp = ((DUMMY_MAX_ATEN - l_adj) * (AUDIO_MAX_GAIN + 1) / + (DUMMY_MAX_ATEN + 1)); + else if (value == r) + tmp = ((DUMMY_MAX_ATEN - r_adj) * (AUDIO_MAX_GAIN + 1) / + (DUMMY_MAX_ATEN + 1)); + } + dummy_chip->perchip_info.record.gain = tmp; + return 0; +} + +/* Reset the audio chip to a sane state. */ +static void dummy_chip_reset(struct sparcaudio_driver *drv) +{ + dummy_set_output_encoding(drv, AUDIO_ENCODING_ULAW); + dummy_set_output_rate(drv, DUMMY_RATE); + dummy_set_output_channels(drv, DUMMY_CHANNELS); + dummy_set_output_precision(drv, DUMMY_PRECISION); + dummy_set_output_balance(drv, AUDIO_MID_BALANCE); + dummy_set_output_volume(drv, DUMMY_DEFAULT_PLAYGAIN); + dummy_set_output_port(drv, AUDIO_SPEAKER); + dummy_output_muted(drv, 0); + dummy_set_input_encoding(drv, AUDIO_ENCODING_ULAW); + dummy_set_input_rate(drv, DUMMY_RATE); + dummy_set_input_channels(drv, DUMMY_CHANNELS); + dummy_set_input_precision(drv, DUMMY_PRECISION); + dummy_set_input_balance(drv, AUDIO_MID_BALANCE); + dummy_set_input_volume(drv, DUMMY_DEFAULT_PLAYGAIN); + dummy_set_input_port(drv, AUDIO_SPEAKER); +} + +static int dummy_open(struct inode * inode, struct file * file, struct sparcaudio_driver *drv) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + /* Set the default audio parameters if not already in use. */ + if (file->f_mode & FMODE_WRITE) { + if (!(drv->flags & SDF_OPEN_WRITE) && + (dummy_chip->perchip_info.play.active == 0)) { + dummy_chip->perchip_info.play.open = 1; + dummy_chip->perchip_info.play.samples = + dummy_chip->perchip_info.play.error = 0; + } + } + if (file->f_mode & FMODE_READ) { + if (!(drv->flags & SDF_OPEN_READ) && + (dummy_chip->perchip_info.record.active == 0)) { + dummy_chip->perchip_info.record.open = 1; + dummy_chip->perchip_info.record.samples = + dummy_chip->perchip_info.record.error = 0; + } + } + + MOD_INC_USE_COUNT; + + return 0; +} + +static void dummy_release(struct inode * inode, struct file * file, struct sparcaudio_driver *drv) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + if (file->f_mode & FMODE_WRITE) { + dummy_chip->perchip_info.play.active = + dummy_chip->perchip_info.play.open = 0; + } + if (file->f_mode & FMODE_READ) { + dummy_chip->perchip_info.record.active = + dummy_chip->perchip_info.record.open = 0; + } + MOD_DEC_USE_COUNT; +} + +static void dummy_output_done_task(void * arg) +{ + struct sparcaudio_driver *drv = (struct sparcaudio_driver *)arg; + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + + sparcaudio_output_done(drv, 1); + if (dummy_chip->perchip_info.record.active) + sparcaudio_input_done(drv, 1); +} + +static void dummy_start_output(struct sparcaudio_driver *drv, __u8 * buffer, + unsigned long count) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + + if (dummy_chip->perchip_info.play.pause || !count) + return; + + dummy_chip->perchip_info.play.active = 1; + + /* fake an "interrupt" to deal with this block */ + dummy_chip->tqueue.next = NULL; + dummy_chip->tqueue.sync = 0; + dummy_chip->tqueue.routine = dummy_output_done_task; + dummy_chip->tqueue.data = drv; + + queue_task(&dummy_chip->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static void dummy_start_input(struct sparcaudio_driver *drv, __u8 * buffer, + unsigned long count) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + dummy_chip->perchip_info.record.active = 1; +} + +static void dummy_stop_output(struct sparcaudio_driver *drv) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + dummy_chip->perchip_info.play.active = 0; +} + +static void dummy_stop_input(struct sparcaudio_driver *drv) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + dummy_chip->perchip_info.record.active = 0; +} + +static int dummy_set_output_pause(struct sparcaudio_driver *drv, int value) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + dummy_chip->perchip_info.play.pause = value; + + if (!value) + sparcaudio_output_done(drv, 0); + + return value; +} + +static int dummy_set_input_pause(struct sparcaudio_driver *drv, int value) +{ + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + dummy_chip->perchip_info.record.pause = value; + + /* This should probably cause play pause. */ + + return value; +} + +static int dummy_set_input_error(struct sparcaudio_driver *drv, int value) +{ + return 0; +} + +static int dummy_set_output_error(struct sparcaudio_driver *drv, int value) +{ + int i; + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + i = dummy_chip->perchip_info.play.error; + dummy_chip->perchip_info.play.error = value; + return i; +} + +static int dummy_set_output_samples(struct sparcaudio_driver *drv, int value) +{ + int i; + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + i = dummy_chip->perchip_info.play.samples; + dummy_chip->perchip_info.play.samples = value; + return i; +} + +static int dummy_set_input_samples(struct sparcaudio_driver *drv, int value) +{ + int i; + struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; + i = dummy_chip->perchip_info.play.samples; + dummy_chip->perchip_info.record.samples = value; + return i; +} + +/* In order to fake things which care out, play we're a 4231 */ +static void dummy_audio_getdev(struct sparcaudio_driver *drv, + audio_device_t * audinfo) +{ + strncpy(audinfo->name, "SUNW,cs4231", sizeof(audinfo->name) - 1); + strncpy(audinfo->version, "a", sizeof(audinfo->version) - 1); + strncpy(audinfo->config, "onboard1", sizeof(audinfo->config) - 1); +} + + +static int dummy_audio_getdev_sunos(struct sparcaudio_driver *drv) +{ + return (5); +} + +static struct sparcaudio_operations dummy_ops = { + dummy_open, + dummy_release, + NULL, + dummy_start_output, + dummy_stop_output, + dummy_start_input, + dummy_stop_input, + dummy_audio_getdev, + dummy_set_output_volume, + dummy_get_output_volume, + dummy_set_input_volume, + dummy_get_input_volume, + dummy_set_monitor_volume, + dummy_get_monitor_volume, + dummy_set_output_balance, + dummy_get_output_balance, + dummy_set_input_balance, + dummy_get_input_balance, + dummy_set_output_channels, + dummy_get_output_channels, + dummy_set_input_channels, + dummy_get_input_channels, + dummy_set_output_precision, + dummy_get_output_precision, + dummy_set_input_precision, + dummy_get_input_precision, + dummy_set_output_port, + dummy_get_output_port, + dummy_set_input_port, + dummy_get_input_port, + dummy_set_output_encoding, + dummy_get_output_encoding, + dummy_set_input_encoding, + dummy_get_input_encoding, + dummy_set_output_rate, + dummy_get_output_rate, + dummy_set_input_rate, + dummy_get_input_rate, + dummy_audio_getdev_sunos, + dummy_get_output_ports, + dummy_get_input_ports, + dummy_output_muted, + dummy_get_output_muted, + dummy_set_output_pause, + dummy_get_output_pause, + dummy_set_input_pause, + dummy_get_input_pause, + dummy_set_output_samples, + dummy_get_output_samples, + dummy_set_input_samples, + dummy_get_input_samples, + dummy_set_output_error, + dummy_get_output_error, + dummy_set_input_error, + dummy_get_input_error, + dummy_get_formats, +}; + +/* Probe for the dummy chip and then attach the driver. */ +#ifdef MODULE +int init_module(void) +#else +__initfunc(int dummy_init(void)) +#endif +{ + num_drivers = 0; + + /* Add support here for specifying multiple dummies to attach at once. */ + if (dummy_attach(&drivers[num_drivers]) == 0) + num_drivers++; + + /* Only return success if we found some dummy chips. */ + return (num_drivers > 0) ? 0 : -EIO; +} + +/* Attach to an dummy chip given its PROM node. */ +static int dummy_attach(struct sparcaudio_driver *drv) +{ + struct dummy_chip *dummy_chip; + int err; + + /* Allocate our private information structure. */ + drv->private = kmalloc(sizeof(struct dummy_chip), GFP_KERNEL); + if (!drv->private) + return -ENOMEM; + + /* Point at the information structure and initialize it. */ + drv->ops = &dummy_ops; + dummy_chip = (struct dummy_chip *)drv->private; + + /* Reset parameters. */ + dummy_chip_reset(drv); + + /* Register ourselves with the midlevel audio driver. */ + err = register_sparcaudio_driver(drv, 2); + + if (err < 0) { + printk(KERN_ERR "dummy: unable to register\n"); + kfree(drv->private); + return -EIO; + } + + dummy_chip->perchip_info.play.active = + dummy_chip->perchip_info.play.pause = 0; + + dummy_chip->perchip_info.play.avail_ports = (AUDIO_HEADPHONE | + AUDIO_SPEAKER | + AUDIO_LINE_OUT); + + /* Announce the hardware to the user. */ + printk(KERN_INFO "audio%d: dummy at 0x0 irq 0\n", drv->index); + + /* Success! */ + return 0; +} + +#ifdef MODULE +/* Detach from an dummy chip given the device structure. */ +static void dummy_detach(struct sparcaudio_driver *drv) +{ + unregister_sparcaudio_driver(drv, 2); + kfree(drv->private); +} + +void cleanup_module(void) +{ + register int i; + + for (i = 0; i < num_drivers; i++) { + dummy_detach(&drivers[i]); + num_drivers--; + } +} +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-indent-level: 4 + * c-brace-imaginary-offset: 0 + * c-brace-offset: -4 + * c-argdecl-indent: 4 + * c-label-offset: -4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: 0 + * indent-tabs-mode: nil + * tab-width: 8 + * End: + */ diff -u --recursive --new-file v2.2.3/linux/drivers/sbus/audio/dummy.h linux/drivers/sbus/audio/dummy.h --- v2.2.3/linux/drivers/sbus/audio/dummy.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/sbus/audio/dummy.h Mon Mar 15 16:11:30 1999 @@ -0,0 +1,41 @@ +/* + * drivers/sbus/audio/dummy.h + * + * Copyright (C) 1998 Derrick J. Brashear (shadow@dementia.org) + */ + +#ifndef _DUMMY_H_ +#define _DUMMY_H_ + +#include +#include + +#define DUMMY_OUTFILE "/usr/tmp/dummy.au" + +/* Our structure for each chip */ + +struct dummy_chip { + struct audio_info perchip_info; + unsigned int playlen; + struct tq_struct tqueue; +}; + +#define DUMMY_MIN_ATEN (0) +#define DUMMY_MAX_ATEN (31) +#define DUMMY_MAX_DEV_ATEN (63) + +#define DUMMY_MON_MIN_ATEN (0) +#define DUMMY_MON_MAX_ATEN (63) + +#define DUMMY_DEFAULT_PLAYGAIN (132) +#define DUMMY_DEFAULT_RECGAIN (126) + +#define DUMMY_MIN_GAIN (0) +#define DUMMY_MAX_GAIN (15) + +#define DUMMY_PRECISION (8) /* # of bits/sample */ +#define DUMMY_CHANNELS (1) /* channels/sample */ + +#define DUMMY_RATE (8000) /* default sample rate */ + +#endif diff -u --recursive --new-file v2.2.3/linux/drivers/sbus/char/bpp.c linux/drivers/sbus/char/bpp.c --- v2.2.3/linux/drivers/sbus/char/bpp.c Mon Dec 28 15:00:52 1998 +++ linux/drivers/sbus/char/bpp.c Mon Mar 15 16:11:30 1999 @@ -337,7 +337,7 @@ * responds real good. The first while loop guesses an expire * time accounting for possible wraparound of jiffies. */ - while (time_after_eq(jiffies, extime) extime = jiffies + 1; + while (time_after_eq(jiffies, extime)) extime = jiffies + 1; while ( (time_before(jiffies, extime)) && (((pins & set) != set) || ((pins & clr) != 0)) ) { pins = get_pins(minor); @@ -470,13 +470,14 @@ * mode as this is a reasonable place to clean up from messes made by * ioctls, or other mayhem. */ -static void bpp_release(struct inode *inode, struct file *f) +static int bpp_release(struct inode *inode, struct file *f) { unsigned minor = MINOR(inode->i_rdev); instances[minor].opened = 0; if (instances[minor].mode != COMPATIBILITY) terminate(minor); + return 0; } static long read_nibble(unsigned minor, char *c, unsigned long cnt) @@ -624,11 +625,10 @@ return cnt - remaining; } -static long bpp_read(struct inode *inode, struct file *f, - char *c, unsigned long cnt) +static ssize_t bpp_read(struct file *f, char *c, size_t cnt, loff_t * ppos) { long rc; - const unsigned minor = MINOR(inode->i_rdev); + const unsigned minor = MINOR(f->f_dentry->d_inode->i_rdev); if (minor >= BPP_NO) return -ENODEV; if (!instances[minor].present) return -ENODEV; @@ -694,10 +694,12 @@ unsigned long remaining = cnt; + while (remaining > 0) { unsigned char byte; - c += 1; + get_user_ret(byte, c, -EFAULT); + c += 1; rc = wait_for(BPP_GP_nAck, BPP_GP_Busy, TIME_IDLE_LIMIT, minor); if (rc == -1) return -ETIMEDOUT; @@ -774,11 +776,10 @@ * that. Otherwise, terminate and do my writing in compat mode. This * is the safest course as any device can handle it. */ -static long bpp_write(struct inode *inode, struct file *f, - const char *c, unsigned long cnt) +static ssize_t bpp_write(struct file *f, const char *c, size_t cnt, loff_t * ppos) { long errno = 0; - unsigned minor = MINOR(inode->i_rdev); + const unsigned minor = MINOR(f->f_dentry->d_inode->i_rdev); if (minor >= BPP_NO) return -ENODEV; if (!instances[minor].present) return -ENODEV; @@ -861,6 +862,11 @@ bpp_open, NULL, /* flush */ bpp_release, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check media change */ + NULL, /* revalidate */ + NULL, /* lock */ }; #if defined(__i386__) diff -u --recursive --new-file v2.2.3/linux/drivers/sbus/char/flash.c linux/drivers/sbus/char/flash.c --- v2.2.3/linux/drivers/sbus/char/flash.c Wed Mar 10 15:29:46 1999 +++ linux/drivers/sbus/char/flash.c Mon Mar 15 16:11:30 1999 @@ -1,4 +1,4 @@ -/* $Id: flash.c,v 1.10 1998/08/26 10:29:41 davem Exp $ +/* $Id: flash.c,v 1.11 1999/03/09 14:06:45 davem Exp $ * flash.c: Allow mmap access to the OBP Flash, for OBP updates. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) diff -u --recursive --new-file v2.2.3/linux/drivers/sbus/char/pcikbd.c linux/drivers/sbus/char/pcikbd.c --- v2.2.3/linux/drivers/sbus/char/pcikbd.c Thu Nov 19 09:56:28 1998 +++ linux/drivers/sbus/char/pcikbd.c Mon Mar 15 16:11:30 1999 @@ -1,4 +1,4 @@ -/* $Id: pcikbd.c,v 1.24 1998/11/08 11:15:24 davem Exp $ +/* $Id: pcikbd.c,v 1.25 1999/02/08 07:01:48 ecd Exp $ * pcikbd.c: Ultra/AX PC keyboard support. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) @@ -985,9 +985,11 @@ } queue = (struct aux_queue *) kmalloc(sizeof(*queue), GFP_KERNEL); + if (!queue) { + printk("pcimouse_init: kmalloc(aux_queue) failed.\n"); + return -ENOMEM; + } memset(queue, 0, sizeof(*queue)); - queue->head = queue->tail = 0; - queue->proc_list = NULL; if (request_irq(pcimouse_irq, &pcimouse_interrupt, SA_SHIRQ, "mouse", (void *)pcimouse_iobase)) { diff -u --recursive --new-file v2.2.3/linux/drivers/sbus/char/sab82532.c linux/drivers/sbus/char/sab82532.c --- v2.2.3/linux/drivers/sbus/char/sab82532.c Mon Dec 28 15:00:52 1998 +++ linux/drivers/sbus/char/sab82532.c Mon Mar 15 16:11:30 1999 @@ -1,4 +1,4 @@ -/* $Id: sab82532.c,v 1.27 1998/11/08 11:15:25 davem Exp $ +/* $Id: sab82532.c,v 1.28 1999/01/02 16:47:35 davem Exp $ * sab82532.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) @@ -2136,7 +2136,7 @@ __initfunc(static inline void show_serial_version(void)) { - char *revision = "$Revision: 1.27 $"; + char *revision = "$Revision: 1.28 $"; char *version, *p; version = strchr(revision, ' '); diff -u --recursive --new-file v2.2.3/linux/drivers/sbus/char/su.c linux/drivers/sbus/char/su.c --- v2.2.3/linux/drivers/sbus/char/su.c Mon Dec 28 15:00:52 1998 +++ linux/drivers/sbus/char/su.c Mon Mar 15 16:11:30 1999 @@ -1,4 +1,4 @@ -/* $Id: su.c,v 1.16 1998/11/14 23:02:54 ecd Exp $ +/* $Id: su.c,v 1.18 1999/01/02 16:47:37 davem Exp $ * su.c: Small serial driver for keyboard/mouse interface on sparc32/PCI * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) @@ -157,7 +157,7 @@ #define WAKEUP_CHARS 256 static void autoconfig(struct su_struct *info); -static void change_speed(struct su_struct *info); +static void change_speed(struct su_struct *info, struct termios *old); static void su_wait_until_sent(struct tty_struct *tty, int timeout); /* @@ -845,7 +845,7 @@ /* * and set the speed of the serial port */ - change_speed(info); + change_speed(info, 0); info->flags |= ASYNC_INITIALIZED; restore_flags(flags); @@ -948,7 +948,8 @@ * the specified baud rate for a serial port. */ static void -change_speed(struct su_struct *info) +change_speed(struct su_struct *info, + struct termios *old_termios) { int quot = 0, baud; unsigned int cval, fcr = 0; @@ -999,7 +1000,25 @@ else if (baud) quot = info->baud_base / baud; } - /* If the quotient is ever zero, default to 9600 bps */ + /* If the quotient is zero refuse the change */ + if (!quot && old_termios) { + info->tty->termios->c_cflag &= ~CBAUD; + info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD); + baud = tty_get_baud_rate(info->tty); + if (!baud) + baud = 9600; + if (baud == 38400 && + ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) + quot = info->custom_divisor; + else { + if (baud == 134) + /* Special case since 134 is really 134.5 */ + quot = (2*info->baud_base / 269); + else if (baud) + quot = info->baud_base / baud; + } + } + /* As a last resort, if the quotient is zero, default to 9600 bps */ if (!quot) quot = info->baud_base / 9600; info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / info->baud_base); @@ -1157,7 +1176,7 @@ info->cflag |= 1200; break; } - change_speed(info); + change_speed(info, 0); } static void @@ -1303,6 +1322,9 @@ if (serial_paranoia_check(info, tty->device, "su_send_char")) return; + if (!(info->flags & ASYNC_INITIALIZED)) + return; + info->x_char = ch; if (ch) { /* Make sure transmit interrupts are on */ @@ -1618,7 +1640,7 @@ == RELEVANT_IFLAG(old_termios->c_iflag))) return; - change_speed(info); + change_speed(info, old_termios); /* Handle transition to B0 status */ if ((old_termios->c_cflag & CBAUD) && @@ -2067,13 +2089,13 @@ *tty->termios = info->normal_termios; else *tty->termios = info->callout_termios; - change_speed(info); + change_speed(info, 0); } #ifdef CONFIG_SERIAL_CONSOLE if (sercons.cflag && sercons.index == line) { tty->termios->c_cflag = sercons.cflag; sercons.cflag = 0; - change_speed(info); + change_speed(info, 0); } #endif info->session = current->session; @@ -2193,7 +2215,7 @@ */ __initfunc(static __inline__ void show_su_version(void)) { - char *revision = "$Revision: 1.16 $"; + char *revision = "$Revision: 1.18 $"; char *version, *p; version = strchr(revision, ' '); @@ -2207,8 +2229,8 @@ * This routine is called by su_init() to initialize a specific serial * port. It determines what type of UART chip this serial port is * using: 8250, 16450, 16550, 16550A. The important question is - * whether or not this UART is a 16550A or not, since this will - * determine whether or not we can use its FIFO features or not. + * whether or not this UART is a 16550A, since this will determine + * whether or not we can use its FIFO features. */ static void autoconfig(struct su_struct *info) diff -u --recursive --new-file v2.2.3/linux/drivers/sbus/char/sunkbd.c linux/drivers/sbus/char/sunkbd.c --- v2.2.3/linux/drivers/sbus/char/sunkbd.c Mon Oct 5 13:13:40 1998 +++ linux/drivers/sbus/char/sunkbd.c Mon Mar 15 16:11:30 1999 @@ -514,8 +514,13 @@ mark_bh(CONSOLE_BH); add_keyboard_randomness(keycode); + tty = ttytab? ttytab[fg_console]: NULL; + if (tty && (!tty->driver_data)) { + /* This is to workaround ugly bug in tty_io.c, which + does not do locking when it should */ + tty = NULL; + } kbd = kbd_table + fg_console; - tty = ttytab[fg_console]; if((raw_mode = (kbd->kbdmode == VC_RAW))) { if (kbd_redirected == fg_console+1) push_kbd (keycode); diff -u --recursive --new-file v2.2.3/linux/drivers/sbus/char/sunserial.c linux/drivers/sbus/char/sunserial.c --- v2.2.3/linux/drivers/sbus/char/sunserial.c Sun Nov 8 14:03:01 1998 +++ linux/drivers/sbus/char/sunserial.c Mon Mar 15 16:11:30 1999 @@ -1,4 +1,4 @@ -/* $Id: sunserial.c,v 1.67 1998/10/25 03:22:46 jj Exp $ +/* $Id: sunserial.c,v 1.68 1998/12/09 18:53:51 davem Exp $ * serial.c: Serial port driver infrastructure for the Sparc. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) @@ -401,10 +401,10 @@ return memory_start; #ifdef __sparc_v9__ - ret = prom_finddevice("/ssp-serial"); - if (ret && ret != -1) { + { extern int this_is_starfire; /* Hello, Starfire. Pleased to meet you :) */ - return memory_start; + if(this_is_starfire != 0) + return memory_start; } #endif diff -u --recursive --new-file v2.2.3/linux/drivers/sbus/char/zs.c linux/drivers/sbus/char/zs.c --- v2.2.3/linux/drivers/sbus/char/zs.c Thu Nov 19 09:56:28 1998 +++ linux/drivers/sbus/char/zs.c Mon Mar 15 16:11:30 1999 @@ -1,4 +1,4 @@ -/* $Id: zs.c,v 1.32 1998/11/08 11:15:29 davem Exp $ +/* $Id: zs.c,v 1.40 1999/02/23 15:14:45 jj Exp $ * zs.c: Zilog serial port driver for the Sparc. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -53,6 +53,22 @@ #define KEYBOARD_LINE 0x2 #define MOUSE_LINE 0x3 +/* On 32-bit sparcs we need to delay after register accesses + * to accomodate sun4 systems, but we do not need to flush writes. + * On 64-bit sparc we only need to flush single writes to ensure + * completion. + */ +#ifndef __sparc_v9__ +#define ZSDELAY() udelay(5) +#define ZSDELAY_LONG() udelay(20) +#define ZS_WSYNC(channel) do { } while(0) +#else +#define ZSDELAY() +#define ZSDELAY_LONG() +#define ZS_WSYNC(channel) \ + ((void) *((volatile unsigned char *)(&((channel)->control)))) +#endif + struct sun_zslayout **zs_chips; struct sun_zschannel **zs_channels; struct sun_zschannel *zs_mousechan; @@ -200,9 +216,9 @@ unsigned char retval; channel->control = reg; - udelay(5); + ZSDELAY(); retval = channel->control; - udelay(5); + ZSDELAY(); return retval; } @@ -210,9 +226,9 @@ unsigned char reg, unsigned char value) { channel->control = reg; - udelay(5); + ZSDELAY(); channel->control = value; - udelay(5); + ZSDELAY(); } static inline void load_zsregs(struct sun_serial *info, unsigned char *regs) @@ -239,7 +255,7 @@ write_zsreg(channel, R9, CHRA); else write_zsreg(channel, R9, CHRB); - udelay(20); /* wait for some old sun4's */ + ZSDELAY_LONG(); write_zsreg(channel, R4, regs[R4]); write_zsreg(channel, R3, regs[R3] & ~RxENAB); write_zsreg(channel, R5, regs[R5] & ~TxENAB); @@ -267,10 +283,16 @@ { int loops = ZS_PUT_CHAR_MAX_DELAY; + /* Do not change this to use ZSDELAY as this is + * a timed polling loop and on sparc64 ZSDELAY + * is a nop. -DaveM + */ while((channel->control & Tx_BUF_EMP) == 0 && --loops) udelay(5); + channel->data = ch; - udelay(5); + ZSDELAY(); + ZS_WSYNC(channel); } /* Sets or clears DTR/RTS on the requested line */ @@ -420,7 +442,7 @@ do { ch = (info->zs_channel->data) & info->parity_mask; - udelay(5); + ZSDELAY(); /* If this is the console keyboard, we need to handle * L1-A's here. @@ -482,7 +504,7 @@ /* Check if we have another character... */ stat = info->zs_channel->control; - udelay(5); + ZSDELAY(); if (!(stat & Rx_CH_AV)) break; @@ -507,7 +529,8 @@ if((info->xmit_cnt <= 0) || (tty != 0 && tty->stopped)) { /* That's peculiar... */ info->zs_channel->control = RES_Tx_P; - udelay(5); + ZSDELAY(); + ZS_WSYNC(info->zs_channel); return; } @@ -521,7 +544,8 @@ if(info->xmit_cnt <= 0) { info->zs_channel->control = RES_Tx_P; - udelay(5); + ZSDELAY(); + ZS_WSYNC(info->zs_channel); } } @@ -531,10 +555,11 @@ /* Get status from Read Register 0 */ status = info->zs_channel->control; - udelay(5); + ZSDELAY(); /* Clear status condition... */ info->zs_channel->control = RES_EXT_INT; - udelay(5); + ZSDELAY(); + ZS_WSYNC(info->zs_channel); #if 0 if(status & DCD) { @@ -571,7 +596,7 @@ stat = read_zsreg(info->zs_channel, R1); if (stat & (PAR_ERR | Rx_OVR | CRC_ERR)) { ch = info->zs_channel->data; - udelay(5); + ZSDELAY(); } if (!tty) @@ -592,7 +617,8 @@ queue_task(&tty->flip.tqueue, &tq_timer); clear: info->zs_channel->control = ERR_RES; - udelay(5); + ZSDELAY(); + ZS_WSYNC(info->zs_channel); } @@ -772,9 +798,12 @@ * Clear the interrupt registers. */ info->zs_channel->control = ERR_RES; - udelay(5); + ZSDELAY(); + ZS_WSYNC(info->zs_channel); + info->zs_channel->control = RES_H_IUS; - udelay(5); + ZSDELAY(); + ZS_WSYNC(info->zs_channel); /* * Now, initialize the Zilog @@ -798,9 +827,12 @@ * And clear the interrupt registers again for luck. */ info->zs_channel->control = ERR_RES; - udelay(5); + ZSDELAY(); + ZS_WSYNC(info->zs_channel); + info->zs_channel->control = RES_H_IUS; - udelay(5); + ZSDELAY(); + ZS_WSYNC(info->zs_channel); if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags); @@ -1006,6 +1038,7 @@ while((chan->control & Tx_BUF_EMP)==0) udelay(5); chan->data = kgdb_char; + ZS_WSYNC(chan); } char getDebugChar(void) @@ -1013,7 +1046,7 @@ struct sun_zschannel *chan = zs_kgdbchan; while((chan->control & Rx_CH_AV)==0) - barrier(); + udelay(5); return chan->data; } @@ -1254,6 +1287,9 @@ goto check_and_exit; } + if(new_serial.baud_base < 9600) + return -EINVAL; + if (info->count > 1) return -EBUSY; @@ -1291,6 +1327,7 @@ cli(); status = info->zs_channel->control; + ZSDELAY(); sti(); put_user_ret(status,value, -EFAULT); return 0; @@ -1303,6 +1340,7 @@ cli(); status = info->zs_channel->control; + ZSDELAY(); sti(); result = ((info->curregs[5] & RTS) ? TIOCM_RTS : 0) | ((info->curregs[5] & DTR) ? TIOCM_DTR : 0) @@ -1806,7 +1844,7 @@ static void show_serial_version(void) { - char *revision = "$Revision: 1.32 $"; + char *revision = "$Revision: 1.40 $"; char *version, *p; version = strchr(revision, ' '); @@ -1853,8 +1891,8 @@ int len = prom_getproperty(zsnode, "address", (void *) vaddr, sizeof(vaddr)); - if(len == -1) { - struct linux_sbus *sbus; + if(len == -1 || central_bus != NULL) { + struct linux_sbus *sbus = NULL; struct linux_sbus_device *sdev = NULL; /* "address" property is not guarenteed, @@ -1862,20 +1900,40 @@ * anyways by our clever TLB miss handling * scheme, so don't fail here. -DaveM */ - for_each_sbus(sbus) { - for_each_sbusdev(sdev, sbus) { - if (sdev->prom_node == zsnode) - goto found; + if (central_bus == NULL) { + for_each_sbus(sbus) { + for_each_sbusdev(sdev, sbus) { + if (sdev->prom_node == zsnode) + goto found; + } } } found: - if (sdev == NULL) + if (sdev == NULL && central_bus == NULL) prom_halt(); - prom_apply_sbus_ranges(sbus, sdev->reg_addrs, 1, sdev); - mapped_addr = (unsigned long) - sparc_alloc_io(sdev->reg_addrs[0].phys_addr, 0, - PAGE_SIZE, "Zilog Registers", - sdev->reg_addrs[0].which_io, 0x0); + if (central_bus == NULL) { + prom_apply_sbus_ranges(sbus, sdev->reg_addrs, 1, sdev); + mapped_addr = (unsigned long) + sparc_alloc_io(sdev->reg_addrs[0].phys_addr, 0, + PAGE_SIZE, "Zilog Registers", + sdev->reg_addrs[0].which_io, 0x0); + } else { + struct linux_prom_registers zsregs[1]; + int err; + + err = prom_getproperty(zsnode, "reg", + (char *)&zsregs[0], + sizeof(zsregs)); + if (err == -1) { + prom_printf("ZS: Cannot map Zilog regs.\n"); + prom_halt(); + } + prom_apply_fhc_ranges(central_bus->child, &zsregs[0], 1); + prom_apply_central_ranges(central_bus, &zsregs[0], 1); + mapped_addr = (unsigned long) + __va((((unsigned long)zsregs[0].which_io)<<32) | + (((unsigned long)zsregs[0].phys_addr))); + } } else if(len % sizeof(unsigned int)) { prom_printf("WHOOPS: proplen for %s " "was %d, need multiple of " @@ -1954,25 +2012,27 @@ /* Can use the prom for other machine types */ zsnode = prom_getchild(prom_root_node); if (sparc_cpu_model == sun4d) { - int board, node; + int node; + int no = 0; tmpnode = zsnode; + zsnode = 0; + bbnode = 0; while (tmpnode && (tmpnode = prom_searchsiblings(tmpnode, "cpu-unit"))) { - board = prom_getintdefault (tmpnode, "board#", -1); - if (board == (chip >> 1)) { - node = prom_getchild(tmpnode); - if (node && (node = prom_searchsiblings(node, "bootbus"))) { + bbnode = prom_getchild(tmpnode); + if (bbnode && (bbnode = prom_searchsiblings(bbnode, "bootbus"))) { + if (no == (chip >> 1)) { cpunode = tmpnode; - bbnode = node; - zsnode = prom_getchild(node); + zsnode = prom_getchild(bbnode); chipid = (chip & 1); break; } + no++; } tmpnode = prom_getsibling(tmpnode); } if (!tmpnode) - panic ("get_zs: couldn't find board%d's bootbus\n", chip >> 1); + panic ("get_zs: couldn't find %dth bootbus\n", chip >> 1); } else { tmpnode = prom_searchsiblings(zsnode, "obio"); if(tmpnode) @@ -2066,13 +2126,12 @@ node = prom_getchild(prom_root_node); if (sparc_cpu_model == sun4d) { - node = prom_searchsiblings(node, "boards"); - if (!node) - panic ("Cannot find out count of boards"); - else - node = prom_getchild(node); - while (node && (node = prom_searchsiblings(node, "bif"))) { - NUM_SERIAL += 2; + int bbnode; + + while (node && (node = prom_searchsiblings(node, "cpu-unit"))) { + bbnode = prom_getchild(node); + if (bbnode && prom_searchsiblings(bbnode, "bootbus")) + NUM_SERIAL += 2; node = prom_getsibling(node); } goto no_probe; @@ -2082,7 +2141,7 @@ int central_node; /* Central bus zilogs must be checked for first, - * since Enterprise boxes have SBUS as well. + * since Enterprise boxes might have SBUSes as well. */ central_node = prom_finddevice("/central"); if(central_node != 0 && central_node != -1) @@ -2290,13 +2349,21 @@ /* Initialize Softinfo */ zs_prepare(); + /* Grab IRQ line before poking the chips so we do + * not lose any interrupts. + */ + if (request_irq(zilog_irq, zs_interrupt, + (SA_INTERRUPT | SA_STATIC_ALLOC), + "Zilog8530", zs_chain)) + panic("Unable to attach zs intr\n"); + /* Initialize Hardware */ for(channel = 0; channel < NUM_CHANNELS; channel++) { /* Hardware reset each chip */ if (!(channel & 1)) { write_zsreg(zs_soft[channel].zs_channel, R9, FHWRES); - udelay(20); /* wait for some old sun4's */ + ZSDELAY_LONG(); dummy = read_zsreg(zs_soft[channel].zs_channel, R0); } @@ -2462,10 +2529,6 @@ printk(" is a Zilog8530\n"); } - if (request_irq(zilog_irq, zs_interrupt, - (SA_INTERRUPT | SA_STATIC_ALLOC), - "Zilog8530", zs_chain)) - panic("Unable to attach zs intr\n"); restore_flags(flags); keyboard_zsinit(kbd_put_char); @@ -2549,7 +2612,7 @@ /* Last character is being transmitted now (hopefully). */ info->zs_channel->control = RES_Tx_P; - udelay(5); + ZSDELAY(); restore_flags(flags); return; diff -u --recursive --new-file v2.2.3/linux/drivers/sbus/sbus.c linux/drivers/sbus/sbus.c --- v2.2.3/linux/drivers/sbus/sbus.c Sun Nov 8 14:03:01 1998 +++ linux/drivers/sbus/sbus.c Mon Mar 15 16:11:30 1999 @@ -1,4 +1,4 @@ -/* $Id: sbus.c,v 1.73 1998/10/07 11:35:50 jj Exp $ +/* $Id: sbus.c,v 1.76 1998/12/17 11:11:26 davem Exp $ * sbus.c: SBus support routines. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -71,7 +71,7 @@ base = (unsigned long) sbus_dev->reg_addrs[0].phys_addr; if(base>=SUN_SBUS_BVADDR || (sparc_cpu_model != sun4c && - sparc_cpu_model != sun4)) { + sparc_cpu_model != sun4)) { /* Ahh, we can determine the slot and offset */ if(sparc_cpu_model == sun4u) { /* A bit tricky on the SYSIO. */ @@ -261,6 +261,11 @@ if (!pcibios_present()) { prom_printf("Neither SBUS nor PCI found.\n"); prom_halt(); + } else { +#ifdef __sparc_v9__ + extern void firetruck_init(void); + firetruck_init(); +#endif } return; #else @@ -296,6 +301,7 @@ */ sbus = SBus_chain = kmalloc(sizeof(struct linux_sbus), GFP_ATOMIC); sbus->next = 0; + sbus->prom_node = nd; this_sbus=nd; if(iommund && sparc_cpu_model != sun4u && sparc_cpu_model != sun4d) @@ -319,7 +325,6 @@ prom_getstring(this_sbus, "name", lbuf, sizeof(lbuf)); lbuf[sizeof(sbus->prom_name) - 1] = 0; - sbus->prom_node = this_sbus; strcpy(sbus->prom_name, lbuf); sbus->clock_freq = sbus_clock; #ifndef __sparc_v9__ @@ -406,6 +411,7 @@ sbus->next = kmalloc(sizeof(struct linux_sbus), GFP_ATOMIC); sbus = sbus->next; sbus->next = 0; + sbus->prom_node = this_sbus; } else { break; } @@ -415,6 +421,13 @@ sun4d_init_sbi_irq(); } +#ifdef __sparc_v9__ + if (sparc_cpu_model == sun4u) { + extern void firetruck_init(void); + + firetruck_init(); + } +#endif #ifdef CONFIG_SUN_OPENPROMIO openprom_init(); #endif diff -u --recursive --new-file v2.2.3/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile --- v2.2.3/linux/drivers/scsi/Makefile Wed Mar 10 15:29:46 1999 +++ linux/drivers/scsi/Makefile Mon Mar 15 16:11:30 1999 @@ -560,6 +560,14 @@ endif endif +ifeq ($(CONFIG_SCSI_FCAL),y) +L_OBJS += fcal.o +else + ifeq ($(CONFIG_SCSI_FCAL),m) + M_OBJS += fcal.o + endif +endif + ifeq ($(CONFIG_SCSI_EATA),y) L_OBJS += eata.o else diff -u --recursive --new-file v2.2.3/linux/drivers/scsi/esp.c linux/drivers/scsi/esp.c --- v2.2.3/linux/drivers/scsi/esp.c Thu Nov 19 09:56:28 1998 +++ linux/drivers/scsi/esp.c Mon Mar 15 16:11:31 1999 @@ -1,6 +1,6 @@ /* esp.c: EnhancedScsiProcessor Sun SCSI driver code. * - * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1995, 1998 David S. Miller (davem@caip.rutgers.edu) */ /* TODO: @@ -162,6 +162,17 @@ in_tgterror = 0x84, /* Target did something stupid */ }; +enum { + /* Zero has special meaning, see skipahead[12]. */ +/*0*/ do_never, + +/*1*/ do_phase_determine, +/*2*/ do_reset_bus, +/*3*/ do_reset_complete, +/*4*/ do_work_bus, +/*5*/ do_intr_end +}; + struct proc_dir_entry proc_scsi_esp = { PROC_SCSI_ESP, 3, "esp", S_IFDIR | S_IRUGO | S_IXUGO, 2 @@ -354,24 +365,30 @@ }; } +#ifdef DEBUG_STATE_MACHINE static inline void esp_advance_phase(Scsi_Cmnd *s, int newphase) { -#ifdef DEBUG_STATE_MACHINE ESPLOG(("<%s>", phase_string(newphase))); -#endif s->SCp.sent_command = s->SCp.phase; s->SCp.phase = newphase; } +#else +#define esp_advance_phase(__s, __newphase) \ + (__s)->SCp.sent_command = (__s)->SCp.phase; \ + (__s)->SCp.phase = (__newphase); +#endif +#ifdef DEBUG_ESP_CMDS extern inline void esp_cmd(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs, unchar cmd) { -#ifdef DEBUG_ESP_CMDS esp->espcmdlog[esp->espcmdent] = cmd; esp->espcmdent = (esp->espcmdent + 1) & 31; -#endif eregs->esp_cmd = cmd; } +#else +#define esp_cmd(__esp, __eregs, __cmd) (__eregs)->esp_cmd = (__cmd) +#endif /* How we use the various Linux SCSI data structures for operation. * @@ -442,14 +459,23 @@ } /* Resetting various pieces of the ESP scsi driver chipset/buses. */ -static inline void esp_reset_dma(struct Sparc_ESP *esp) +static void esp_reset_dma(struct Sparc_ESP *esp) { struct sparc_dma_registers *dregs = esp->dregs; - unsigned long tmp, flags; - int can_do_burst16, can_do_burst32; + unsigned long flags; + int can_do_burst16, can_do_burst32, can_do_burst64; + int can_do_sbus64; can_do_burst16 = esp->bursts & DMA_BURST16; can_do_burst32 = esp->bursts & DMA_BURST32; + can_do_burst64 = 0; + can_do_sbus64 = 0; +#ifdef __sparc_v9__ + /* XXX Can sun4d do these too? */ + can_do_burst64 = esp->bursts & DMA_BURST64; + can_do_sbus64 = 1; + mmu_set_sbus64(esp->edev, esp->bursts); +#endif /* Punt the DVMA into a known state. */ if(esp->dma->revision != dvmahme) { @@ -462,14 +488,19 @@ save_flags(flags); cli(); /* I really hate this chip. */ - dregs->cond_reg = 0x08000000; /* Reset interface to FAS */ - dregs->cond_reg = DMA_RST_SCSI; /* Reset DVMA itself */ + dregs->cond_reg = DMA_RESET_FAS366; /* Reset interface to FAS */ + dregs->cond_reg = DMA_RST_SCSI; /* Reset DVMA itself */ - tmp = (DMA_PARITY_OFF|DMA_2CLKS|DMA_SCSI_DISAB|DMA_INT_ENAB); - tmp &= ~(DMA_ENABLE|DMA_ST_WRITE|DMA_BRST_SZ); + esp->prev_hme_dmacsr = (DMA_PARITY_OFF|DMA_2CLKS|DMA_SCSI_DISAB|DMA_INT_ENAB); + esp->prev_hme_dmacsr &= ~(DMA_ENABLE|DMA_ST_WRITE|DMA_BRST_SZ); if(can_do_burst32) - tmp |= DMA_BRST32; + esp->prev_hme_dmacsr |= DMA_BRST32; + else if(can_do_burst64) + esp->prev_hme_dmacsr |= DMA_BRST64; + + if(can_do_sbus64) + esp->prev_hme_dmacsr |= DMA_SCSI_SBUS64; /* This chip is horrible. */ while(dregs->cond_reg & DMA_PEND_READ) @@ -477,8 +508,14 @@ dregs->cond_reg = 0; - dregs->cond_reg = tmp; /* bite me */ - restore_flags(flags); /* ugh... */ + dregs->cond_reg = esp->prev_hme_dmacsr; + + /* This is necessary to avoid having the SCSI channel + * engine lock up on us. + */ + dregs->st_addr = 0; + + restore_flags(flags); break; case dvmarev2: /* This is the gate array found in the sun4m @@ -512,7 +549,7 @@ } /* Reset the ESP chip, _not_ the SCSI bus. */ -static inline void esp_reset_esp(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs) +static void esp_reset_esp(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs) { int family_code, version, i; volatile int trash; @@ -524,8 +561,8 @@ /* Reload the configuration registers */ eregs->esp_cfact = esp->cfact; - eregs->esp_stp = 0; - eregs->esp_soff = 0; + eregs->esp_stp = esp->prev_stp = 0; + eregs->esp_soff = esp->prev_soff = 0; eregs->esp_timeo = esp->neg_defp; /* This is the only point at which it is reliable to read @@ -541,11 +578,11 @@ esp->erev = fashme; /* Version is usually '5'. */ else esp->erev = fas100a; - printk("esp%d: FAST chip is %s (family=%d, version=%d)\n", - esp->esp_id, - (esp->erev == fas236) ? "fas236" : - ((esp->erev == fas100a) ? "fas100a" : - "fasHME"), family_code, (version & 7)); + ESPMISC(("esp%d: FAST chip is %s (family=%d, version=%d)\n", + esp->esp_id, + (esp->erev == fas236) ? "fas236" : + ((esp->erev == fas100a) ? "fas100a" : + "fasHME"), family_code, (version & 7))); esp->min_period = ((4 * esp->ccycle) / 1000); } else { @@ -565,7 +602,7 @@ case esp236: /* Slow 236 */ eregs->esp_cfg2 = esp->config2; - eregs->esp_cfg3 = esp->config3[0]; + eregs->esp_cfg3 = esp->prev_cfg3 = esp->config3[0]; break; case fashme: esp->config2 |= (ESP_CONFIG2_HME32 | ESP_CONFIG2_HMEFENAB); @@ -574,13 +611,18 @@ /* Fast 236 or HME */ eregs->esp_cfg2 = esp->config2; for(i=0; i<8; i++) { - if(esp->erev == fashme) - esp->config3[i] |= - (ESP_CONFIG3_FCLOCK | ESP_CONFIG3_BIGID | ESP_CONFIG3_OBPUSH); - else + if(esp->erev == fashme) { + unsigned char cfg3; + + cfg3 = ESP_CONFIG3_FCLOCK | ESP_CONFIG3_OBPUSH; + if (esp->scsi_id >= 8) + cfg3 |= ESP_CONFIG3_IDBIT3; + esp->config3[i] |= cfg3; + } else { esp->config3[i] |= ESP_CONFIG3_FCLK; + } } - eregs->esp_cfg3 = esp->config3[0]; + eregs->esp_cfg3 = esp->prev_cfg3 = esp->config3[0]; if(esp->erev == fashme) { esp->radelay = 80; } else { @@ -595,7 +637,7 @@ eregs->esp_cfg2 = esp->config2; for(i=0; i<8; i++) esp->config3[i] |= ESP_CONFIG3_FCLOCK; - eregs->esp_cfg3 = esp->config3[0]; + eregs->esp_cfg3 = esp->prev_cfg3 = esp->config3[0]; esp->radelay = 32; break; default: @@ -609,7 +651,7 @@ } /* This places the ESP into a known state at boot time. */ -static inline void esp_bootup_reset(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs) +static void esp_bootup_reset(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs) { volatile unchar trash; @@ -654,6 +696,10 @@ esp->edev = esp_dev; esp->esp_id = id; +#ifdef __sparc_v9__ + esp_host->unchecked_isa_dma = 1; +#endif + /* Put into the chain of esp chips detected */ if(espchain) { elink = espchain; @@ -877,7 +923,7 @@ esp->ctick = ESP_TICK(ccf, esp->ccycle); esp->neg_defp = ESP_NEG_DEFP(fmhz, ccf); esp->sync_defp = SYNC_DEFP_SLOW; - printk("SCSI ID %d Clock %d MHz CCF=%d Time-Out %d ", + printk("SCSI ID %d Clk %dMHz CCF=%d TOut %d ", esp->scsi_id, (fmhz / 1000000), ccf, (int) esp->neg_defp); @@ -910,28 +956,28 @@ eregs->esp_cfg2 = esp->config2; if((eregs->esp_cfg2 & ~(ESP_CONFIG2_MAGIC)) != (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY)) { - printk("NCR53C90(esp100) detected\n"); + printk("NCR53C90(esp100)\n"); esp->erev = esp100; } else { eregs->esp_cfg2 = esp->config2 = 0; eregs->esp_cfg3 = 0; - eregs->esp_cfg3 = esp->config3[0] = 5; + eregs->esp_cfg3 = esp->prev_cfg3 = esp->config3[0] = 5; if(eregs->esp_cfg3 != 5) { - printk("NCR53C90A(esp100a) detected\n"); + printk("NCR53C90A(esp100a)\n"); esp->erev = esp100a; } else { int target; for(target=0; target<8; target++) esp->config3[target] = 0; - eregs->esp_cfg3 = 0; + eregs->esp_cfg3 = esp->prev_cfg3 = 0; if(ccf > ESP_CCF_F5) { - printk("NCR53C9XF(espfast) detected\n"); + printk("NCR53C9XF(espfast)\n"); esp->erev = fast; eregs->esp_cfg2 = esp->config2 = 0; esp->sync_defp = SYNC_DEFP_FAST; } else { - printk("NCR53C9x(esp236) detected\n"); + printk("NCR53C9x(esp236)\n"); esp->erev = esp236; eregs->esp_cfg2 = esp->config2 = 0; } @@ -958,6 +1004,10 @@ esp->prevmsgout = esp->prevmsgin = 0; esp->msgout_len = esp->msgin_len = 0; + /* Clear the one behind caches to hold unmatchable values. */ + esp->prev_soff = esp->prev_stp = esp->prev_cfg3 = 0xff; + esp->prev_hme_dmacsr = 0xffffffff; + /* Reset the thing before we try anything... */ esp_bootup_reset(esp, eregs); @@ -1238,6 +1288,59 @@ return esp_host_info(esp, buffer, offset, length); } +static void esp_get_dmabufs(struct Sparc_ESP *esp, Scsi_Cmnd *sp) +{ + if(sp->use_sg == 0) { + sp->SCp.this_residual = sp->request_bufflen; + sp->SCp.buffer = (struct scatterlist *) sp->request_buffer; + sp->SCp.buffers_residual = 0; + sp->SCp.have_data_in = mmu_get_scsi_one((char *)sp->SCp.buffer, + sp->SCp.this_residual, + esp->edev->my_bus); + sp->SCp.ptr = (char *) ((unsigned long)sp->SCp.have_data_in); + } else { + sp->SCp.buffer = (struct scatterlist *) sp->buffer; + sp->SCp.buffers_residual = sp->use_sg - 1; + sp->SCp.this_residual = sp->SCp.buffer->length; + mmu_get_scsi_sgl((struct mmu_sglist *) sp->SCp.buffer, + sp->SCp.buffers_residual, + esp->edev->my_bus); + sp->SCp.ptr = (char *) ((unsigned long)sp->SCp.buffer->dvma_address); + } +} + +static void esp_release_dmabufs(struct Sparc_ESP *esp, Scsi_Cmnd *sp) +{ + if(sp->use_sg == 0) + mmu_release_scsi_one(sp->SCp.have_data_in, + sp->request_bufflen, + esp->edev->my_bus); + else + mmu_release_scsi_sgl((struct mmu_sglist *) + sp->buffer, sp->use_sg - 1, + esp->edev->my_bus); +} + +static void esp_restore_pointers(struct Sparc_ESP *esp, Scsi_Cmnd *sp) +{ + struct esp_pointers *ep = &esp->data_pointers[sp->target]; + + sp->SCp.ptr = ep->saved_ptr; + sp->SCp.buffer = ep->saved_buffer; + sp->SCp.this_residual = ep->saved_this_residual; + sp->SCp.buffers_residual = ep->saved_buffers_residual; +} + +static void esp_save_pointers(struct Sparc_ESP *esp, Scsi_Cmnd *sp) +{ + struct esp_pointers *ep = &esp->data_pointers[sp->target]; + + ep->saved_ptr = sp->SCp.ptr; + ep->saved_buffer = sp->SCp.buffer; + ep->saved_this_residual = sp->SCp.this_residual; + ep->saved_buffers_residual = sp->SCp.buffers_residual; +} + /* Some rules: * * 1) Never ever panic while something is live on the bus. @@ -1307,7 +1410,7 @@ esp->msgout_len = 4; } -static inline void esp_exec_cmd(struct Sparc_ESP *esp) +static void esp_exec_cmd(struct Sparc_ESP *esp) { struct sparc_dma_registers *dregs = esp->dregs; struct Sparc_ESP_regs *eregs = esp->eregs; @@ -1318,15 +1421,18 @@ int lun, target; int i; - /* Hold off if we've been reselected or an IRQ is showing... */ - if(esp->disconnected_SC || DMA_IRQ_P(dregs)) + /* Hold off if we have disconnected commands and + * an IRQ is showing... + */ + if(esp->disconnected_SC && DMA_IRQ_P(dregs)) return; /* Grab first member of the issue queue. */ SCptr = esp->current_SC = remove_first_SC(&esp->issue_SC); /* Safe to panic here because current_SC is null. */ - if(!SCptr) panic("esp: esp_exec_cmd and issue queue is NULL"); + if(!SCptr) + panic("esp: esp_exec_cmd and issue queue is NULL"); SDptr = SCptr->device; lun = SCptr->lun; @@ -1402,7 +1508,10 @@ SDptr->sync_max_offset = 0; SDptr->sync_min_period = 0; } else { - int toshiba_cdrom_hwbug_wkaround = 0; + /* Sorry, I have had way too many problems with + * various CDROM devices on ESP. -DaveM + */ + int cdrom_hwbug_wkaround = 0; /* Never allow disconnects or synchronous transfers on * SparcStation1 and SparcStation1+. Allowing those @@ -1429,8 +1538,7 @@ */ if(esp->erev == fashme && !SDptr->wide) { if(!SDptr->borken && - (SDptr->type != TYPE_ROM || - strncmp(SDptr->vendor, "TOSHIBA", 7))) { + SDptr->type != TYPE_ROM) { build_wide_nego_msg(esp, 16); SDptr->wide = 1; esp->wnip = 1; @@ -1442,12 +1550,11 @@ } if(!SDptr->borken) { - if((SDptr->type == TYPE_ROM) && - (!strncmp(SDptr->vendor, "TOSHIBA", 7))) { + if((SDptr->type == TYPE_ROM)) { /* Nice try sucker... */ - printk(KERN_INFO "esp%d: Disabling sync for buggy " - "Toshiba CDROM.\n", esp->esp_id); - toshiba_cdrom_hwbug_wkaround = 1; + ESPMISC(("esp%d: Disabling sync for buggy " + "CDROM.\n", esp->esp_id)); + cdrom_hwbug_wkaround = 1; build_sync_nego_msg(esp, 0, 0); } else { build_sync_nego_msg(esp, esp->sync_defp, 15); @@ -1483,13 +1590,9 @@ * thank you very much. ;-) */ if(((SDptr->scsi_level < 3) && (SDptr->type != TYPE_TAPE)) || -#if 1 /* Until I find out why HME barfs with disconnects enabled... */ - toshiba_cdrom_hwbug_wkaround || SDptr->borken || esp->erev == fashme) { -#else - toshiba_cdrom_hwbug_wkaround || SDptr->borken) { -#endif - printk(KERN_INFO "esp%d: Disabling DISCONNECT for target %d " - "lun %d\n", esp->esp_id, SCptr->target, SCptr->lun); + cdrom_hwbug_wkaround || SDptr->borken) { + ESPMISC((KERN_INFO "esp%d: Disabling DISCONNECT for target %d " + "lun %d\n", esp->esp_id, SCptr->target, SCptr->lun)); SDptr->disconnect = 0; *cmdp++ = IDENTIFY(0, lun); } else { @@ -1517,16 +1620,20 @@ (ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT); else eregs->esp_busid = (target & 7); - eregs->esp_soff = SDptr->sync_max_offset; - eregs->esp_stp = SDptr->sync_min_period; - if(esp->erev > esp100a) - eregs->esp_cfg3 = esp->config3[target]; - + if (esp->prev_soff != SDptr->sync_max_offset || + esp->prev_stp != SDptr->sync_min_period || + (esp->erev > esp100a && + esp->prev_cfg3 != esp->config3[target])) { + eregs->esp_soff = esp->prev_soff = SDptr->sync_max_offset; + eregs->esp_stp = esp->prev_stp = SDptr->sync_min_period; + if(esp->erev > esp100a) + eregs->esp_cfg3 = + esp->prev_cfg3 = + esp->config3[target]; + } i = (cmdp - esp->esp_command); if(esp->erev == fashme) { - unsigned long tmp; - esp_cmd(esp, eregs, ESP_CMD_FLUSH); /* Grrr! */ /* Set up the DMA and HME counters */ @@ -1537,12 +1644,12 @@ esp_cmd(esp, eregs, the_esp_command); /* Talk about touchy hardware... */ - tmp = dregs->cond_reg; - tmp |= (DMA_SCSI_DISAB | DMA_ENABLE); - tmp &= ~(DMA_ST_WRITE); + esp->prev_hme_dmacsr = ((esp->prev_hme_dmacsr | + (DMA_SCSI_DISAB | DMA_ENABLE)) & + ~(DMA_ST_WRITE)); dregs->cnt = 16; dregs->st_addr = esp->esp_command_dvma; - dregs->cond_reg = tmp; + dregs->cond_reg = esp->prev_hme_dmacsr; } else { /* Set up the DMA and ESP counters */ eregs->esp_tclow = i; @@ -1575,35 +1682,10 @@ /* We use the scratch area. */ ESPQUEUE(("esp_queue: target=%d lun=%d ", SCpnt->target, SCpnt->lun)); ESPDISC(("N<%02x,%02x>", SCpnt->target, SCpnt->lun)); - if(!SCpnt->use_sg) { - ESPQUEUE(("!use_sg\n")); - SCpnt->SCp.this_residual = SCpnt->request_bufflen; - SCpnt->SCp.buffer = - (struct scatterlist *) SCpnt->request_buffer; - SCpnt->SCp.buffers_residual = 0; - /* Sneaky. */ - SCpnt->SCp.have_data_in = mmu_get_scsi_one((char *)SCpnt->SCp.buffer, - SCpnt->SCp.this_residual, - esp->edev->my_bus); - /* XXX The casts are extremely gross, but with 64-bit kernel - * XXX and 32-bit SBUS what am I to do? -DaveM - */ - SCpnt->SCp.ptr = (char *)((unsigned long)SCpnt->SCp.have_data_in); - } else { - ESPQUEUE(("use_sg ")); -#ifdef DEBUG_ESP_SG - printk("esp%d: sglist at %p with %d buffers\n", - esp->esp_id, SCpnt->buffer, SCpnt->use_sg); -#endif - SCpnt->SCp.buffer = (struct scatterlist *) SCpnt->buffer; - SCpnt->SCp.buffers_residual = SCpnt->use_sg - 1; - SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length; - mmu_get_scsi_sgl((struct mmu_sglist *) SCpnt->SCp.buffer, - SCpnt->SCp.buffers_residual, - esp->edev->my_bus); - /* XXX Again these casts are sick... -DaveM */ - SCpnt->SCp.ptr=(char *)((unsigned long)SCpnt->SCp.buffer->dvma_address); - } + + esp_get_dmabufs(esp, SCpnt); + esp_save_pointers(esp, SCpnt); /* FIXME for tag queueing */ + SCpnt->SCp.Status = CHECK_CONDITION; SCpnt->SCp.Message = 0xff; SCpnt->SCp.sent_command = 0; @@ -1634,7 +1716,7 @@ } /* Dump driver state. */ -static inline void esp_dump_cmd(Scsi_Cmnd *SCptr) +static void esp_dump_cmd(Scsi_Cmnd *SCptr) { ESPLOG(("[tgt<%02x> lun<%02x> " "pphase<%s> cphase<%s>]", @@ -1643,8 +1725,8 @@ phase_string(SCptr->SCp.phase))); } -static inline void esp_dump_state(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs, - struct sparc_dma_registers *dregs) +static void esp_dump_state(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs, + struct sparc_dma_registers *dregs) { Scsi_Cmnd *SCptr = esp->current_SC; #ifdef DEBUG_ESP_CMDS @@ -1735,6 +1817,7 @@ if(this == SCptr) { *prev = (Scsi_Cmnd *) this->host_scribble; this->host_scribble = NULL; + esp_release_dmabufs(esp, this); this->result = DID_ABORT << 16; this->done(this); if(don) @@ -1749,8 +1832,11 @@ * on the bus at this time. So, we let the SCSI code wait * a little bit and try again later. */ - if(esp->current_SC) + if(esp->current_SC) { + if(don) + DMA_INTSON(dregs); return SCSI_ABORT_BUSY; + } /* It's disconnected, we have to reconnect to re-establish * the nexus and tell the device to abort. However, we really @@ -1759,20 +1845,70 @@ * happens, we are really hung so reset the bus. */ + if(don) + DMA_INTSON(dregs); return SCSI_ABORT_SNOOZE; } +/* We've sent ESP_CMD_RS to the ESP, the interrupt had just + * arrived indicating the end of the SCSI bus reset. Our job + * is to clean out the command queues and begin re-execution + * of SCSI commands once more. + */ +static int esp_finish_reset(struct Sparc_ESP *esp, + struct Sparc_ESP_regs *eregs, + struct sparc_dma_registers *dregs) +{ + Scsi_Cmnd *sp = esp->current_SC; + + /* Clean up currently executing command, if any. */ + if (sp != NULL) { + esp_release_dmabufs(esp, sp); + sp->result = (DID_RESET << 16); + sp->scsi_done(sp); + esp->current_SC = NULL; + } + + /* Clean up disconnected queue, they have been invalidated + * by the bus reset. + */ + if (esp->disconnected_SC) { + while((sp = remove_first_SC(&esp->disconnected_SC)) != NULL) { + esp_release_dmabufs(esp, sp); + sp->result = (DID_RESET << 16); + sp->scsi_done(sp); + } + } + + /* SCSI bus reset is complete. */ + esp->resetting_bus = 0; + + /* Ok, now it is safe to get commands going once more. */ + if(esp->issue_SC) + esp_exec_cmd(esp); + + return do_intr_end; +} + +static int esp_do_resetbus(struct Sparc_ESP *esp, + struct Sparc_ESP_regs *eregs, + struct sparc_dma_registers *dregs) +{ + ESPLOG(("esp%d: Resetting scsi bus\n", esp->esp_id)); + esp->resetting_bus = 1; + esp_cmd(esp, eregs, ESP_CMD_RS); + + return do_intr_end; +} + /* Reset ESP chip, reset hanging bus, then kill active and * disconnected commands for targets without soft reset. */ int esp_reset(Scsi_Cmnd *SCptr, unsigned int how) { struct Sparc_ESP *esp = (struct Sparc_ESP *) SCptr->host->hostdata; - struct Sparc_ESP_regs *eregs = esp->eregs; - ESPLOG(("esp%d: Resetting scsi bus\n", esp->esp_id)); - esp->resetting_bus = 1; - esp_cmd(esp, eregs, ESP_CMD_RS); + (void) esp_do_resetbus(esp, esp->eregs, esp->dregs); return SCSI_RESET_PENDING; } @@ -1784,25 +1920,7 @@ if(esp->current_SC) { done_SC = esp->current_SC; esp->current_SC = NULL; - - /* Free dvma entry. */ - if(!done_SC->use_sg) { - /* Sneaky. */ - mmu_release_scsi_one(done_SC->SCp.have_data_in, - done_SC->request_bufflen, - esp->edev->my_bus); - } else { -#ifdef DEBUG_ESP_SG - printk("esp%d: unmapping sg ", esp->esp_id); -#endif - mmu_release_scsi_sgl((struct mmu_sglist *) done_SC->buffer, - done_SC->use_sg - 1, - esp->edev->my_bus); -#ifdef DEBUG_ESP_SG - printk("done.\n"); -#endif - } - + esp_release_dmabufs(esp, done_SC); done_SC->result = error; done_SC->scsi_done(done_SC); @@ -1820,11 +1938,6 @@ /* Wheee, ESP interrupt engine. */ -enum { - do_phase_determine, do_reset_bus, do_reset_complete, - do_work_bus, do_intr_end, -}; - /* Forward declarations. */ static int esp_do_phase_determine(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs, struct sparc_dma_registers *dregs); @@ -1843,29 +1956,29 @@ static int esp_do_cmdbegin(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs, struct sparc_dma_registers *dregs); -static inline int sreg_datainp(unchar sreg) -{ - return (sreg & ESP_STAT_PMASK) == ESP_DIP; -} - -static inline int sreg_dataoutp(unchar sreg) -{ - return (sreg & ESP_STAT_PMASK) == ESP_DOP; -} +#define sreg_datainp(__sreg) (((__sreg) & ESP_STAT_PMASK) == ESP_DIP) +#define sreg_dataoutp(__sreg) (((__sreg) & ESP_STAT_PMASK) == ESP_DOP) -/* Did they drop these fabs on the floor or what?!?!! */ -static inline void hme_fifo_hwbug_workaround(struct Sparc_ESP *esp, - struct Sparc_ESP_regs *eregs) +/* Read any bytes found in the FAS366 fifo, storing them into + * the ESP driver software state structure. + */ +static void hme_fifo_read(struct Sparc_ESP *esp, + struct Sparc_ESP_regs *eregs) { + unsigned long count = 0; unchar status = esp->sreg; - /* Cannot safely frob the fifo for these following cases. */ - if(sreg_datainp(status) || sreg_dataoutp(status) || - (esp->current_SC && esp->current_SC->SCp.phase == in_data_done)) { + /* Cannot safely frob the fifo for these following cases, but + * we must always read the fifo when the reselect interrupt + * is pending. + */ + if(((esp->ireg & ESP_INTR_RSEL) == 0) && + (sreg_datainp(status) || + sreg_dataoutp(status) || + (esp->current_SC && + esp->current_SC->SCp.phase == in_data_done))) { ESPHME(("")); - return; } else { - unsigned long count = 0; unsigned long fcnt = eregs->esp_fflags & ESP_FF_FBYTES; /* The HME stores bytes in multiples of 2 in the fifo. */ @@ -1886,9 +1999,9 @@ } else { ESPHME(("no_xtra_byte")); } - esp->hme_fifo_workaround_count = count; - ESPHME(("wkarnd_cnt=%d]", (int)count)); } + ESPHME(("wkarnd_cnt=%d]", (int)count)); + esp->hme_fifo_workaround_count = count; } static inline void hme_fifo_push(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs, @@ -1913,7 +2026,10 @@ return 0; if(DMA_IRQ_P(dregs)) { /* Yes, we are able to save an interrupt. */ - esp->sreg = eregs->esp_status; + if (esp->erev == fashme) + esp->sreg2 = eregs->esp_status2; + esp->sreg = (eregs->esp_status & ~(ESP_STAT_INTR)); + esp->ireg = eregs->esp_intrpt; if(esp->erev == fashme) { /* This chip is really losing. */ ESPHME(("HME[")); @@ -1923,10 +2039,10 @@ * Happy Meal indeed.... */ ESPHME(("fifo_workaround]")); - hme_fifo_hwbug_workaround(esp, eregs); + if(!(esp->sreg2 & ESP_STAT2_FEMPTY) || + (esp->sreg2 & ESP_STAT2_F1BYTE)) + hme_fifo_read(esp, eregs); } - esp->ireg = eregs->esp_intrpt; - esp->sreg &= ~(ESP_STAT_INTR); if(!(esp->ireg & ESP_INTR_SR)) return 0; else @@ -1947,7 +2063,10 @@ return 0; if(DMA_IRQ_P(dregs)) { /* Yes, we are able to save an interrupt. */ - esp->sreg = eregs->esp_status; + if (esp->erev == fashme) + esp->sreg2 = eregs->esp_status2; + esp->sreg = (eregs->esp_status & ~(ESP_STAT_INTR)); + esp->ireg = eregs->esp_intrpt; if(esp->erev == fashme) { /* This chip is really losing. */ ESPHME(("HME[")); @@ -1958,10 +2077,10 @@ * Happy Meal indeed.... */ ESPHME(("fifo_workaround]")); - hme_fifo_hwbug_workaround(esp, eregs); + if(!(esp->sreg2 & ESP_STAT2_FEMPTY) || + (esp->sreg2 & ESP_STAT2_F1BYTE)) + hme_fifo_read(esp, eregs); } - esp->ireg = eregs->esp_intrpt; - esp->sreg &= ~(ESP_STAT_INTR); if(!(esp->ireg & ESP_INTR_SR)) return 0; else @@ -2012,21 +2131,27 @@ } } -static inline void dma_invalidate(struct sparc_dma_registers *dregs, enum dvma_rev drev) +static inline void dma_invalidate(struct Sparc_ESP *esp, + struct sparc_dma_registers *dregs, + enum dvma_rev drev) { unsigned int tmp; if(drev == dvmahme) { - /* SMCC can bite me. */ - tmp = dregs->cond_reg; dregs->cond_reg = DMA_RST_SCSI; - /* This would explain a lot. */ - tmp |= (DMA_PARITY_OFF|DMA_2CLKS|DMA_SCSI_DISAB); + esp->prev_hme_dmacsr = ((esp->prev_hme_dmacsr | + (DMA_PARITY_OFF | DMA_2CLKS | + DMA_SCSI_DISAB | DMA_INT_ENAB)) & + ~(DMA_ST_WRITE | DMA_ENABLE)); - tmp &= ~(DMA_ENABLE|DMA_ST_WRITE); dregs->cond_reg = 0; - dregs->cond_reg = tmp; + dregs->cond_reg = esp->prev_hme_dmacsr; + + /* This is necessary to avoid having the SCSI channel + * engine lock up on us. + */ + dregs->st_addr = 0; } else { while(dregs->cond_reg & DMA_PEND_READ) udelay(1); @@ -2039,10 +2164,12 @@ } } -static inline void dma_flashclear(struct sparc_dma_registers *dregs, enum dvma_rev drev) +static inline void dma_flashclear(struct Sparc_ESP *esp, + struct sparc_dma_registers *dregs, + enum dvma_rev drev) { dma_drain(dregs, drev); - dma_invalidate(dregs, drev); + dma_invalidate(esp, dregs, drev); } static inline int dma_can_transfer(Scsi_Cmnd *sp, enum dvma_rev drev) @@ -2064,56 +2191,38 @@ return sz; } -/* Misc. esp helper routines. */ -static inline void esp_setcount(struct Sparc_ESP_regs *eregs, int cnt, int hme) -{ - eregs->esp_tclow = (cnt & 0xff); - eregs->esp_tcmed = ((cnt >> 8) & 0xff); - if(hme) { - eregs->fas_rlo = 0; - eregs->fas_rhi = 0; - } -} - -static inline int esp_getcount(struct Sparc_ESP_regs *eregs) -{ - return (((eregs->esp_tclow)&0xff) | - (((eregs->esp_tcmed)&0xff) << 8)); -} - -static inline int fcount(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs) -{ - if(esp->erev == fashme) - return esp->hme_fifo_workaround_count; - else - return eregs->esp_fflags & ESP_FF_FBYTES; -} - -static inline int fnzero(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs) -{ - if(esp->erev == fashme) - return 0; - else - return eregs->esp_fflags & ESP_FF_ONOTZERO; -} +/* Misc. esp helper macros. */ +#define esp_setcount(__eregs, __cnt, __hme) \ + (__eregs)->esp_tclow = ((__cnt) & 0xff); \ + (__eregs)->esp_tcmed = (((__cnt) >> 8) & 0xff); \ + if(__hme) { \ + (__eregs)->fas_rlo = 0; \ + (__eregs)->fas_rhi = 0; \ + } + +#define esp_getcount(__eregs) \ + ((((__eregs)->esp_tclow)&0xff) | \ + ((((__eregs)->esp_tcmed)&0xff) << 8)) + +#define fcount(__esp, __eregs) \ + (((__esp)->erev == fashme) ? \ + (__esp)->hme_fifo_workaround_count : \ + (__eregs)->esp_fflags & ESP_FF_FBYTES) + +#define fnzero(__esp, __eregs) \ + (((__esp)->erev == fashme) ? 0 : \ + (__eregs)->esp_fflags & ESP_FF_ONOTZERO) /* XXX speculative nops unnecessary when continuing amidst a data phase * XXX even on esp100!!! another case of flooding the bus with I/O reg * XXX writes... */ -static inline void esp_maybe_nop(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs) -{ - if(esp->erev == esp100) - esp_cmd(esp, eregs, ESP_CMD_NULL); -} +#define esp_maybe_nop(__esp, __eregs) \ + if((__esp)->erev == esp100) \ + esp_cmd((__esp), (__eregs), ESP_CMD_NULL) -static inline int sreg_to_dataphase(unchar sreg) -{ - if((sreg & ESP_STAT_PMASK) == ESP_DOP) - return in_dataout; - else - return in_datain; -} +#define sreg_to_dataphase(__sreg) \ + ((((__sreg) & ESP_STAT_PMASK) == ESP_DOP) ? in_dataout : in_datain) /* The ESP100 when in synchronous data phase, can mistake a long final * REQ pulse from the target as an extra byte, it places whatever is on @@ -2205,10 +2314,19 @@ lun = esp->hme_fifo_workaround_buffer[1]; else lun = eregs->esp_fdata; + + /* Yes, you read this correctly. We report lun of zero + * if we see parity error. ESP reports parity error for + * the lun byte, and this is the only way to hope to recover + * because the target is connected. + */ if(esp->sreg & ESP_STAT_PERR) return 0; + + /* Check for illegal bits being set in the lun. */ if((lun & 0x40) || !(lun & 0x80)) return -1; + return lun & 7; } @@ -2219,13 +2337,18 @@ Scsi_Cmnd *sp) { Scsi_Device *dp = sp->device; - eregs->esp_soff = dp->sync_max_offset; - eregs->esp_stp = dp->sync_min_period; - if(esp->erev > esp100a) - eregs->esp_cfg3 = esp->config3[sp->target]; - if(esp->erev == fashme) - eregs->esp_busid = (sp->target & 0xf) | - (ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT); + + if(esp->prev_soff != dp->sync_max_offset || + esp->prev_stp != dp->sync_min_period || + (esp->erev > esp100a && + esp->prev_cfg3 != esp->config3[sp->target])) { + eregs->esp_soff = esp->prev_soff = dp->sync_max_offset; + eregs->esp_stp = esp->prev_stp = dp->sync_min_period; + if(esp->erev > esp100a) + eregs->esp_cfg3 = + esp->prev_cfg3 = + esp->config3[sp->target]; + } esp->current_SC = sp; } @@ -2235,8 +2358,8 @@ static inline void esp_reconnect(struct Sparc_ESP *esp, Scsi_Cmnd *sp) { if(!esp->disconnected_SC) - printk("esp%d: Weird, being reselected but disconnected " - "command queue is empty.\n", esp->esp_id); + ESPLOG(("esp%d: Weird, being reselected but disconnected " + "command queue is empty.\n", esp->esp_id)); esp->snip = 0; esp->current_SC = 0; sp->SCp.phase = not_issued; @@ -2244,8 +2367,8 @@ } /* Begin message in phase. */ -static inline int esp_do_msgin(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs, - struct sparc_dma_registers *dregs) +static int esp_do_msgin(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs, + struct sparc_dma_registers *dregs) { /* Must be very careful with the fifo on the HME */ if((esp->erev != fashme) || !(eregs->esp_status2 & ESP_STAT2_FEMPTY)) @@ -2298,8 +2421,8 @@ * within a buffer or sub-buffer should not upset us at all no matter * how bad the target and/or ESP fucks things up. */ -static inline int esp_do_data(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs, - struct sparc_dma_registers *dregs) +static int esp_do_data(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs, + struct sparc_dma_registers *dregs) { Scsi_Cmnd *SCptr = esp->current_SC; int thisphase, hmuch; @@ -2313,19 +2436,28 @@ ESPDATA(("hmuch<%d> ", hmuch)); esp->current_transfer_size = hmuch; if(esp->erev == fashme) { - unsigned long tmp = dregs->cond_reg; + unsigned long tmp = esp->prev_hme_dmacsr; - /* Touchy chip, this stupid HME scsi adapter... */ + /* Always set the ESP count registers first. */ esp_setcount(eregs, hmuch, 1); - esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI); - dregs->cnt = hmuch; + + /* Get the DMA csr computed. */ tmp |= (DMA_SCSI_DISAB | DMA_ENABLE); - if(thisphase == in_datain) + if (thisphase == in_datain) tmp |= DMA_ST_WRITE; else tmp &= ~(DMA_ST_WRITE); + esp->prev_hme_dmacsr = tmp; + + if (thisphase == in_datain) { + dregs->cnt = hmuch; + esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI); + } else { + esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI); + dregs->cnt = hmuch; + } dregs->st_addr = ((__u32)((unsigned long)SCptr->SCp.ptr)); - dregs->cond_reg = tmp; + dregs->cond_reg = esp->prev_hme_dmacsr; } else { esp_setcount(eregs, hmuch, 0); dma_setup(dregs, esp->dma->revision, @@ -2338,9 +2470,9 @@ } /* See how successful the data transfer was. */ -static inline int esp_do_data_finale(struct Sparc_ESP *esp, - struct Sparc_ESP_regs *eregs, - struct sparc_dma_registers *dregs) +static int esp_do_data_finale(struct Sparc_ESP *esp, + struct Sparc_ESP_regs *eregs, + struct sparc_dma_registers *dregs) { Scsi_Cmnd *SCptr = esp->current_SC; int bogus_data = 0, bytes_sent = 0, fifocnt, ecount = 0; @@ -2361,7 +2493,7 @@ } dma_drain(dregs, esp->dma->revision); } - dma_invalidate(dregs, esp->dma->revision); + dma_invalidate(esp, dregs, esp->dma->revision); /* This could happen for the above parity error case. */ if(!(esp->ireg == ESP_INTR_BSERV)) { @@ -2387,26 +2519,43 @@ ecount = esp_getcount(eregs); bytes_sent = esp->current_transfer_size; - /* Uhhh, might not want both of these conditionals to run - * at once on HME due to the fifo problems it has. Consider - * changing it to: - * - * if(!(esp->sreg & ESP_STAT_TCNT)) { - * bytes_sent -= ecount; - * } else if(SCptr->SCp.phase == in_dataout) { - * bytes_sent -= fifocnt; - * } - * - * But only for the HME case, leave the current code alone - * for all other ESP revisions as we know the existing code - * works just fine for them. - */ ESPDATA(("trans_sz=%d, ", bytes_sent)); if(esp->erev == fashme) { if(!(esp->sreg & ESP_STAT_TCNT)) { - bytes_sent -= esp_getcount(eregs); - } else if(SCptr->SCp.phase == in_dataout) { + ecount = esp_getcount(eregs); + bytes_sent -= ecount; + } + + /* Always subtract any cruft remaining in the FIFO. */ + if(esp->prev_cfg3 & ESP_CONFIG3_EWIDE) + fifocnt <<= 1; + if(SCptr->SCp.phase == in_dataout) bytes_sent -= fifocnt; + + /* I have an IBM disk which exhibits the following + * behavior during writes to it. It disconnects in + * the middle of a partial transfer, the current sglist + * buffer is 1024 bytes, the disk stops data transfer + * at 512 bytes. + * + * However the FAS366 reports that 32 more bytes were + * transferred than really were. This is precisely + * the size of a fully loaded FIFO in wide scsi mode. + * The FIFO state recorded indicates that it is empty. + * + * I have no idea if this is a bug in the FAS366 chip + * or a bug in the firmware on this IBM disk. In any + * event the following seems to be a good workaround. -DaveM + */ + if (bytes_sent != esp->current_transfer_size && + SCptr->SCp.phase == in_dataout) { + int mask = (64 - 1); + + if((esp->prev_cfg3 & ESP_CONFIG3_EWIDE) == 0) + mask >>= 1; + + if (bytes_sent & mask) + bytes_sent -= (bytes_sent & mask); } } else { if(!(esp->sreg & ESP_STAT_TCNT)) @@ -2442,6 +2591,8 @@ * driver. No idea why it happened, but allowing * this value to be negative caused things to * lock up. This allows greater chance of recovery. + * In fact every time I've seen this, it has been + * a driver bug without question. */ ESPLOG(("esp%d: yieee, bytes_sent < 0!\n", esp->esp_id)); ESPLOG(("esp%d: csz=%d fifocount=%d ecount=%d\n", @@ -2462,7 +2613,7 @@ SCptr->SCp.this_residual -= bytes_sent; if(SCptr->SCp.this_residual < 0) { /* shit */ - printk("esp%d: Data transfer overrun.\n", esp->esp_id); + ESPLOG(("esp%d: Data transfer overrun.\n", esp->esp_id)); SCptr->SCp.this_residual = 0; } @@ -2493,11 +2644,49 @@ return do_intr_end; } +/* We received a non-good status return at the end of + * running a SCSI command. This is used to decide if + * we should clear our synchronous transfer state for + * such a device when that happens. + * + * The idea is that when spinning up a disk or rewinding + * a tape, we don't want to go into a loop re-negotiating + * synchronous capabilities over and over. + */ +static int esp_should_clear_sync(Scsi_Cmnd *sp) +{ + unchar cmd1 = sp->cmnd[0]; + unchar cmd2 = sp->data_cmnd[0]; + + /* These cases are for spinning up a disk and + * waiting for that spinup to complete. + */ + if(cmd1 == START_STOP || + cmd2 == START_STOP) + return 0; + + if(cmd1 == TEST_UNIT_READY || + cmd2 == TEST_UNIT_READY) + return 0; + + /* One more special case for SCSI tape drives, + * this is what is used to probe the device for + * completion of a rewind or tape load operation. + */ + if(sp->device->type == TYPE_TAPE) { + if(cmd1 == MODE_SENSE || + cmd2 == MODE_SENSE) + return 0; + } + + return 1; +} + /* Either a command is completing or a target is dropping off the bus * to continue the command in the background so we can do other work. */ -static inline int esp_do_freebus(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs, - struct sparc_dma_registers *dregs) +static int esp_do_freebus(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs, + struct sparc_dma_registers *dregs) { Scsi_Cmnd *SCptr = esp->current_SC; int rval; @@ -2516,9 +2705,11 @@ if(esp->disconnected_SC || (esp->erev == fashme)) esp_cmd(esp, eregs, ESP_CMD_ESEL); - if(SCptr->SCp.Status != GOOD && SCptr->SCp.Status != CONDITION_GOOD && + if(SCptr->SCp.Status != GOOD && + SCptr->SCp.Status != CONDITION_GOOD && ((1<target) & esp->targets_present) && - SCptr->device->sync && SCptr->device->sync_max_offset) { + SCptr->device->sync && + SCptr->device->sync_max_offset) { /* SCSI standard says that the synchronous capabilities * should be renegotiated at this point. Most likely * we are about to request sense from this target @@ -2535,15 +2726,7 @@ * can report not ready many times right after * loading up a tape. */ - if(SCptr->cmnd[0] != START_STOP && - SCptr->data_cmnd[0] != START_STOP && - SCptr->cmnd[0] != TEST_UNIT_READY && - SCptr->data_cmnd[0] != TEST_UNIT_READY && - !(SCptr->device->type == TYPE_TAPE && - (SCptr->cmnd[0] == TEST_UNIT_READY || - SCptr->data_cmnd[0] == TEST_UNIT_READY || - SCptr->cmnd[0] == MODE_SENSE || - SCptr->data_cmnd[0] == MODE_SENSE))) + if(esp_should_clear_sync(SCptr) != 0) SCptr->device->sync = 0; } ESPDISC(("F<%02x,%02x>", SCptr->target, SCptr->lun)); @@ -2570,9 +2753,43 @@ return do_intr_end; } +/* When a reselect occurs, and we cannot find the command to + * reconnect to in our queues, we do this. + */ +static int esp_bad_reconnect(struct Sparc_ESP *esp) +{ + Scsi_Cmnd *sp; + + ESPLOG(("esp%d: Eieeee, reconnecting unknown command!\n", + esp->esp_id)); + ESPLOG(("QUEUE DUMP\n")); + sp = esp->issue_SC; + ESPLOG(("esp%d: issue_SC[", esp->esp_id)); + while(sp) { + ESPLOG(("<%02x,%02x>", sp->target, sp->lun)); + sp = (Scsi_Cmnd *) sp->host_scribble; + } + ESPLOG(("]\n")); + sp = esp->current_SC; + ESPLOG(("esp%d: current_SC[", esp->esp_id)); + while(sp) { + ESPLOG(("<%02x,%02x>", sp->target, sp->lun)); + sp = (Scsi_Cmnd *) sp->host_scribble; + } + ESPLOG(("]\n")); + sp = esp->disconnected_SC; + ESPLOG(("esp%d: disconnected_SC[", esp->esp_id)); + while(sp) { + ESPLOG(("<%02x,%02x>", sp->target, sp->lun)); + sp = (Scsi_Cmnd *) sp->host_scribble; + } + ESPLOG(("]\n")); + return do_reset_bus; +} + /* Do the needy when a target tries to reconnect to us. */ -static inline int esp_do_reconnect(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs, - struct sparc_dma_registers *dregs) +static int esp_do_reconnect(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs, + struct sparc_dma_registers *dregs) { int lun, target; Scsi_Cmnd *SCptr; @@ -2592,12 +2809,8 @@ /* Things look ok... */ ESPDISC(("R<%02x,%02x>", target, lun)); - /* Must flush both FIFO and the DVMA on HME. */ - if(esp->erev == fashme) { - /* XXX this still doesn't fix the problem... */ - esp_cmd(esp, eregs, ESP_CMD_FLUSH); - dma_invalidate(esp->dregs, dvmahme); - } else { + /* Must not flush FIFO or DVMA on HME. */ + if(esp->erev != fashme) { esp_cmd(esp, eregs, ESP_CMD_FLUSH); if(esp100_reconnect_hwbug(esp, eregs)) return do_reset_bus; @@ -2605,39 +2818,19 @@ } SCptr = remove_SC(&esp->disconnected_SC, (unchar) target, (unchar) lun); - if(!SCptr) { - Scsi_Cmnd *sp; + if(!SCptr) + return esp_bad_reconnect(esp); - ESPLOG(("esp%d: Eieeee, reconnecting unknown command!\n", - esp->esp_id)); - ESPLOG(("QUEUE DUMP\n")); - sp = esp->issue_SC; - ESPLOG(("esp%d: issue_SC[", esp->esp_id)); - while(sp) { - ESPLOG(("<%02x,%02x>", sp->target, sp->lun)); - sp = (Scsi_Cmnd *) sp->host_scribble; - } - ESPLOG(("]\n")); - sp = esp->current_SC; - ESPLOG(("esp%d: current_SC[", esp->esp_id)); - while(sp) { - ESPLOG(("<%02x,%02x>", sp->target, sp->lun)); - sp = (Scsi_Cmnd *) sp->host_scribble; - } - ESPLOG(("]\n")); - sp = esp->disconnected_SC; - ESPLOG(("esp%d: disconnected_SC[", esp->esp_id)); - while(sp) { - ESPLOG(("<%02x,%02x>", sp->target, sp->lun)); - sp = (Scsi_Cmnd *) sp->host_scribble; - } - ESPLOG(("]\n")); - return do_reset_bus; - } esp_connect(esp, eregs, SCptr); esp_cmd(esp, eregs, ESP_CMD_MOK); - /* No need for explicit restore pointers operation. */ + if(esp->erev == fashme) + eregs->esp_busid = (SCptr->target & 0xf) | + (ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT); + + /* Reconnect implies a restore pointers operation. */ + esp_restore_pointers(esp, SCptr); + esp->snip = 0; esp_advance_phase(SCptr, in_the_dark); return do_intr_end; @@ -2665,7 +2858,7 @@ esp_cmd(esp, eregs, ESP_CMD_MOK); if(esp->erev != fashme) { - dma_flashclear(dregs, esp->dma->revision); + dma_flashclear(esp, dregs, esp->dma->revision); /* Wait till the first bits settle. */ while(esp->esp_command[0] == 0xff) @@ -2767,112 +2960,135 @@ } } -/* The target has control of the bus and we have to see where it has - * taken us. - */ -static int esp_do_phase_determine(struct Sparc_ESP *esp, - struct Sparc_ESP_regs *eregs, - struct sparc_dma_registers *dregs) +static int esp_enter_status(struct Sparc_ESP *esp, + struct Sparc_ESP_regs *eregs, + struct sparc_dma_registers *dregs) { - Scsi_Cmnd *SCptr = esp->current_SC; + unchar thecmd = ESP_CMD_ICCSEQ; - ESPPHASE(("esp_do_phase_determine: ")); - if(!(esp->ireg & ESP_INTR_DC)) { - switch(esp->sreg & ESP_STAT_PMASK) { - case ESP_DOP: - case ESP_DIP: - ESPPHASE(("to data phase\n")); - return esp_do_data(esp, eregs, dregs); + esp_cmd(esp, eregs, ESP_CMD_FLUSH); + if(esp->erev != fashme) { + esp->esp_command[0] = esp->esp_command[1] = 0xff; + eregs->esp_tclow = 2; + eregs->esp_tcmed = 0; + dregs->cond_reg |= (DMA_ST_WRITE | DMA_ENABLE); + if(esp->dma->revision == dvmaesc1) + dregs->cnt = 0x100; + dregs->st_addr = esp->esp_command_dvma; + thecmd |= ESP_CMD_DMA; + } + esp_cmd(esp, eregs, thecmd); + esp_advance_phase(esp->current_SC, in_status); - case ESP_STATP: - /* Whee, status phase, finish up the command. */ - ESPPHASE(("to status phase\n")); - esp_cmd(esp, eregs, ESP_CMD_FLUSH); - if(esp->erev != fashme) { - esp->esp_command[0] = 0xff; - esp->esp_command[1] = 0xff; - eregs->esp_tclow = 2; - eregs->esp_tcmed = 0; - dregs->cond_reg |= (DMA_ST_WRITE | DMA_ENABLE); - if(esp->dma->revision == dvmaesc1) - dregs->cnt = 0x1000; - dregs->st_addr = esp->esp_command_dvma; - esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_ICCSEQ); - } else { - /* Using DVMA for status/message bytes is - * unreliable on HME, nice job QLogic. - * Happy Meal indeed.... - */ - esp_cmd(esp, eregs, ESP_CMD_ICCSEQ); - } - esp_advance_phase(SCptr, in_status); - return esp_do_status(esp, eregs, dregs); + return esp_do_status(esp, eregs, dregs); +} - case ESP_MOP: - ESPPHASE(("to msgout phase\n")); - esp_advance_phase(SCptr, in_msgout); - return esp_do_msgout(esp, eregs, dregs); - - case ESP_MIP: - ESPPHASE(("to msgin phase\n")); - esp_advance_phase(SCptr, in_msgin); - return esp_do_msgin(esp, eregs, dregs); - - case ESP_CMDP: - /* Ugh, we're running a non-standard command the - * ESP doesn't understand, one byte at a time. - */ - ESPPHASE(("to cmd phase\n")); - esp_advance_phase(SCptr, in_cmdbegin); - return esp_do_cmdbegin(esp, eregs, dregs); - }; - } else { - Scsi_Device *dp = SCptr->device; +static int esp_disconnect_amidst_phases(struct Sparc_ESP *esp, + struct Sparc_ESP_regs *eregs, + struct sparc_dma_registers *dregs) +{ + Scsi_Cmnd *sp = esp->current_SC; + Scsi_Device *dp = sp->device; - /* This means real problems if we see this - * here. Unless we were actually trying - * to force the device to abort/reset. - */ - ESPLOG(("esp%d Disconnect amidst phases, ", esp->esp_id)); - ESPLOG(("pphase<%s> cphase<%s>, ", - phase_string(SCptr->SCp.phase), - phase_string(SCptr->SCp.sent_command))); - if(esp->disconnected_SC || (esp->erev == fashme)) - esp_cmd(esp, eregs, ESP_CMD_ESEL); + /* This means real problems if we see this + * here. Unless we were actually trying + * to force the device to abort/reset. + */ + ESPLOG(("esp%d Disconnect amidst phases, ", esp->esp_id)); + ESPLOG(("pphase<%s> cphase<%s>, ", + phase_string(sp->SCp.phase), + phase_string(sp->SCp.sent_command))); - switch(esp->cur_msgout[0]) { - default: - /* We didn't expect this to happen at all. */ - ESPLOG(("device is bolixed\n")); - esp_advance_phase(SCptr, in_tgterror); - esp_done(esp, (DID_ERROR << 16)); - break; + if(esp->disconnected_SC || (esp->erev == fashme)) + esp_cmd(esp, eregs, ESP_CMD_ESEL); - case BUS_DEVICE_RESET: - ESPLOG(("device reset successful\n")); - dp->sync_max_offset = 0; - dp->sync_min_period = 0; - dp->sync = 0; - esp_advance_phase(SCptr, in_resetdev); - esp_done(esp, (DID_RESET << 16)); - break; + switch(esp->cur_msgout[0]) { + default: + /* We didn't expect this to happen at all. */ + ESPLOG(("device is bolixed\n")); + esp_advance_phase(sp, in_tgterror); + esp_done(esp, (DID_ERROR << 16)); + break; - case ABORT: - ESPLOG(("device abort successful\n")); - esp_advance_phase(SCptr, in_abortone); - esp_done(esp, (DID_ABORT << 16)); - break; + case BUS_DEVICE_RESET: + ESPLOG(("device reset successful\n")); + dp->sync_max_offset = 0; + dp->sync_min_period = 0; + dp->sync = 0; + esp_advance_phase(sp, in_resetdev); + esp_done(esp, (DID_RESET << 16)); + break; - }; - return do_intr_end; - } + case ABORT: + ESPLOG(("device abort successful\n")); + esp_advance_phase(sp, in_abortone); + esp_done(esp, (DID_ABORT << 16)); + break; + + }; + return do_intr_end; +} + +static int esp_enter_msgout(struct Sparc_ESP *esp, + struct Sparc_ESP_regs *eregs, + struct sparc_dma_registers *dregs) +{ + esp_advance_phase(esp->current_SC, in_msgout); + return esp_do_msgout(esp, eregs, dregs); +} + +static int esp_enter_msgin(struct Sparc_ESP *esp, + struct Sparc_ESP_regs *eregs, + struct sparc_dma_registers *dregs) +{ + esp_advance_phase(esp->current_SC, in_msgin); + return esp_do_msgin(esp, eregs, dregs); +} - ESPLOG(("esp%d: to unknown phase\n", esp->esp_id)); - printk("esp%d: Bizarre bus phase %2x.\n", esp->esp_id, - esp->sreg & ESP_STAT_PMASK); +static int esp_enter_cmd(struct Sparc_ESP *esp, + struct Sparc_ESP_regs *eregs, + struct sparc_dma_registers *dregs) +{ + esp_advance_phase(esp->current_SC, in_cmdbegin); + return esp_do_cmdbegin(esp, eregs, dregs); +} + +static int esp_enter_badphase(struct Sparc_ESP *esp, + struct Sparc_ESP_regs *eregs, + struct sparc_dma_registers *dregs) +{ + ESPLOG(("esp%d: Bizarre bus phase %2x.\n", esp->esp_id, + esp->sreg & ESP_STAT_PMASK)); return do_reset_bus; } +typedef int (*espfunc_t)(struct Sparc_ESP *, + struct Sparc_ESP_regs *, + struct sparc_dma_registers *); + +static espfunc_t phase_vector[] = { + esp_do_data, /* ESP_DOP */ + esp_do_data, /* ESP_DIP */ + esp_enter_cmd, /* ESP_CMDP */ + esp_enter_status, /* ESP_STATP */ + esp_enter_badphase, /* ESP_STAT_PMSG */ + esp_enter_badphase, /* ESP_STAT_PMSG | ESP_STAT_PIO */ + esp_enter_msgout, /* ESP_MOP */ + esp_enter_msgin, /* ESP_MIP */ +}; + +/* The target has control of the bus and we have to see where it has + * taken us. + */ +static int esp_do_phase_determine(struct Sparc_ESP *esp, + struct Sparc_ESP_regs *eregs, + struct sparc_dma_registers *dregs) +{ + if ((esp->ireg & ESP_INTR_DC) != 0) + return esp_disconnect_amidst_phases(esp, eregs, dregs); + return phase_vector[esp->sreg & ESP_STAT_PMASK](esp, eregs, dregs); +} + /* First interrupt after exec'ing a cmd comes here. */ static int esp_select_complete(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs, struct sparc_dma_registers *dregs) @@ -2888,7 +3104,7 @@ else fcnt = (eregs->esp_fflags & ESP_FF_FBYTES); cmd_bytes_sent = esp_bytes_sent(esp, dregs, fcnt); - dma_invalidate(dregs, esp->dma->revision); + dma_invalidate(esp, dregs, esp->dma->revision); /* Let's check to see if a reselect happened * while we we're trying to select. This must @@ -3061,9 +3277,8 @@ SCptr->SCp.phase == in_slct_stop)) { /* shit */ esp->snip = 0; - printk("esp%d: Failed synchronous negotiation for target %d " - "lun %d\n", - esp->esp_id, SCptr->target, SCptr->lun); + ESPLOG(("esp%d: Failed synchronous negotiation for target %d " + "lun %d\n", esp->esp_id, SCptr->target, SCptr->lun)); SDptr->sync_max_offset = 0; SDptr->sync_min_period = 0; SDptr->sync = 1; /* so we don't negotiate again */ @@ -3090,8 +3305,8 @@ * But first make sure that is really what is happening. */ if(((1<target) & esp->targets_present)) { - printk("esp%d: Warning, live target %d not responding to " - "selection.\n", esp->esp_id, SCptr->target); + ESPLOG(("esp%d: Warning, live target %d not responding to " + "selection.\n", esp->esp_id, SCptr->target)); /* This _CAN_ happen. The SCSI standard states that * the target is to _not_ respond to selection if @@ -3154,15 +3369,15 @@ * have miscoded something..... so, try to * recover as best we can. */ - printk("esp%d: message in mis-carriage.\n", esp->esp_id); + ESPLOG(("esp%d: message in mis-carriage.\n", esp->esp_id)); } esp_advance_phase(esp->current_SC, in_the_dark); return do_phase_determine; } -static inline int check_singlebyte_msg(struct Sparc_ESP *esp, - struct Sparc_ESP_regs *eregs, - struct sparc_dma_registers *dregs) +static int check_singlebyte_msg(struct Sparc_ESP *esp, + struct Sparc_ESP_regs *eregs, + struct sparc_dma_registers *dregs) { esp->prevmsgin = esp->cur_msgin[0]; if(esp->cur_msgin[0] & 0x80) { @@ -3191,8 +3406,20 @@ return 0; case RESTORE_POINTERS: + /* In this case we might also have to backup the + * "slow command" pointer. It is rare to get such + * a save/restore pointer sequence so early in the + * bus transition sequences, but cover it. + */ + if(esp->esp_slowcmd) { + esp->esp_scmdleft = esp->current_SC->cmd_len; + esp->esp_scmdp = &esp->current_SC->cmnd[0]; + } + esp_restore_pointers(esp, esp->current_SC); + return 0; + case SAVE_POINTERS: - /* We handle this all automatically. */ + esp_save_pointers(esp, esp->current_SC); return 0; case COMMAND_COMPLETE: @@ -3227,9 +3454,9 @@ * the SCSI2 standard specifically recommends against targets doing * this because so many initiators cannot cope with this occuring. */ -static inline int target_with_ants_in_pants(struct Sparc_ESP *esp, - Scsi_Cmnd *SCptr, - Scsi_Device *SDptr) +static int target_with_ants_in_pants(struct Sparc_ESP *esp, + Scsi_Cmnd *SCptr, + Scsi_Device *SDptr) { if(SDptr->sync || SDptr->borken) { /* sorry, no can do */ @@ -3246,7 +3473,7 @@ return 0; } -static inline void sync_report(struct Sparc_ESP *esp) +static void sync_report(struct Sparc_ESP *esp) { int msg3, msg4; char *type; @@ -3267,21 +3494,27 @@ } else { type = "synchronous"; } - printk(KERN_INFO "esp%d: target %d [period %dns offset %d %d.%02dMHz %s SCSI%s]\n", - esp->esp_id, esp->current_SC->target, - (int) msg3 * 4, - (int) msg4, - integer, fraction, type, - (((msg3 * 4) < 200) ? "-II" : "")); + + /* Do not transform this back into one big printk + * again, it triggers a bug in our sparc64-gcc272 + * sibling call optimization. -DaveM + */ + ESPLOG((KERN_INFO "esp%d: target %d ", + esp->esp_id, esp->current_SC->target)); + ESPLOG(("[period %dns offset %d %d.%02dMHz ", + (int) msg3 * 4, (int) msg4, + integer, fraction)); + ESPLOG(("%s SCSI%s]\n", type, + (((msg3 * 4) < 200) ? "-II" : ""))); } else { - printk(KERN_INFO "esp%d: target %d asynchronous\n", - esp->esp_id, esp->current_SC->target); + ESPLOG((KERN_INFO "esp%d: target %d asynchronous\n", + esp->esp_id, esp->current_SC->target)); } } -static inline int check_multibyte_msg(struct Sparc_ESP *esp, - struct Sparc_ESP_regs *eregs, - struct sparc_dma_registers *dregs) +static int check_multibyte_msg(struct Sparc_ESP *esp, + struct Sparc_ESP_regs *eregs, + struct sparc_dma_registers *dregs) { Scsi_Cmnd *SCptr = esp->current_SC; Scsi_Device *SDptr = SCptr->device; @@ -3358,10 +3591,10 @@ esp->config3[SCptr->target] |= bit; else esp->config3[SCptr->target] &= ~bit; - eregs->esp_cfg3 = esp->config3[SCptr->target]; + eregs->esp_cfg3 = esp->prev_cfg3 = esp->config3[SCptr->target]; } - eregs->esp_soff = SDptr->sync_min_period; - eregs->esp_stp = SDptr->sync_max_offset; + eregs->esp_soff = esp->prev_soff = SDptr->sync_min_period; + eregs->esp_stp = esp->prev_stp = SDptr->sync_max_offset; ESPSDTR(("soff=%2x stp=%2x cfg3=%2x\n", SDptr->sync_max_offset, @@ -3376,15 +3609,15 @@ ESPSDTR(("unaccaptable sync nego, forcing async\n")); SDptr->sync_max_offset = 0; SDptr->sync_min_period = 0; - eregs->esp_soff = 0; - eregs->esp_stp = 0; + eregs->esp_soff = esp->prev_soff = 0; + eregs->esp_stp = esp->prev_stp = 0; if((esp->erev == fas100a || esp->erev == fas236 || esp->erev == fashme)) { if((esp->erev == fas100a) || (esp->erev == fashme)) bit = ESP_CONFIG3_FAST; else bit = ESP_CONFIG3_FSCSI; esp->config3[SCptr->target] &= ~bit; - eregs->esp_cfg3 = esp->config3[SCptr->target]; + eregs->esp_cfg3 = esp->prev_cfg3 = esp->config3[SCptr->target]; } } @@ -3409,32 +3642,30 @@ esp->wnip = 0; if(esp->erev != fashme) { - printk("esp%d: AIEEE wide msg received and not HME.\n", - esp->esp_id); + ESPLOG(("esp%d: AIEEE wide msg received and not HME.\n", + esp->esp_id)); message_out = MESSAGE_REJECT; } else if(size > 16) { - printk("esp%d: AIEEE wide transfer for %d size not supported.\n", - esp->esp_id, size); + ESPLOG(("esp%d: AIEEE wide transfer for %d size " + "not supported.\n", esp->esp_id, size)); message_out = MESSAGE_REJECT; } else { /* Things look good; let's see what we got. */ if(size == 16) { /* Set config 3 register for this target. */ - printk("esp%d: 16 byte WIDE transfers enabled for target %d.\n", - esp->esp_id, SCptr->target); esp->config3[SCptr->target] |= ESP_CONFIG3_EWIDE; } else { /* Just make sure it was one byte sized. */ if(size != 8) { - printk("esp%d: Aieee, wide nego of %d size.\n", - esp->esp_id, size); + ESPLOG(("esp%d: Aieee, wide nego of %d size.\n", + esp->esp_id, size)); message_out = MESSAGE_REJECT; goto finish; } /* Pure paranoia. */ esp->config3[SCptr->target] &= ~(ESP_CONFIG3_EWIDE); } - eregs->esp_cfg3 = esp->config3[SCptr->target]; + eregs->esp_cfg3 = esp->prev_cfg3 = esp->config3[SCptr->target]; /* Regardless, next try for sync transfers. */ build_sync_nego_msg(esp, esp->sync_defp, 15); @@ -3566,11 +3797,11 @@ return do_intr_end; } -static inline int esp_do_cmddone(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs, - struct sparc_dma_registers *dregs) +static int esp_do_cmddone(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs, + struct sparc_dma_registers *dregs) { if(esp->erev == fashme) - dma_invalidate(dregs, dvmahme); + dma_invalidate(esp, dregs, dvmahme); else esp_cmd(esp, eregs, ESP_CMD_NULL); if(esp->ireg & ESP_INTR_BSERV) { @@ -3583,7 +3814,7 @@ } static int esp_do_msgout(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs, - struct sparc_dma_registers *dregs) + struct sparc_dma_registers *dregs) { esp_cmd(esp, eregs, ESP_CMD_FLUSH); switch(esp->msgout_len) { @@ -3661,8 +3892,8 @@ return do_intr_end; } -static inline int esp_do_msgoutdone(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs, - struct sparc_dma_registers *dregs) +static int esp_do_msgoutdone(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs, + struct sparc_dma_registers *dregs) { if(esp->msgout_len > 1) { /* XXX HME/FAS ATN deassert workaround required, @@ -3673,7 +3904,7 @@ while(dregs->cond_reg & DMA_PEND_READ) udelay(1); dregs->cond_reg &= ~(DMA_ENABLE); - dma_invalidate(dregs, esp->dma->revision); + dma_invalidate(esp, dregs, esp->dma->revision); } else { esp_cmd(esp, eregs, ESP_CMD_FLUSH); } @@ -3722,81 +3953,65 @@ return esp_do_phase_determine(esp, eregs, dregs); } +static int esp_bus_unexpected(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs, + struct sparc_dma_registers *dregs) +{ + ESPLOG(("esp%d: command in weird state %2x\n", + esp->esp_id, esp->current_SC->SCp.phase)); + return do_reset_bus; +} + +static espfunc_t bus_vector[] = { + esp_do_data_finale, + esp_do_data_finale, + esp_bus_unexpected, + esp_do_msgin, + esp_do_msgincont, + esp_do_msgindone, + esp_do_msgout, + esp_do_msgoutdone, + esp_do_cmdbegin, + esp_do_cmddone, + esp_do_status, + esp_do_freebus, + esp_do_phase_determine, + esp_bus_unexpected, + esp_bus_unexpected, + esp_bus_unexpected, +}; + /* This is the second tier in our dual-level SCSI state machine. */ -static inline int esp_work_bus(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs, - struct sparc_dma_registers *dregs) +static int esp_work_bus(struct Sparc_ESP *esp, + struct Sparc_ESP_regs *eregs, + struct sparc_dma_registers *dregs) { Scsi_Cmnd *SCptr = esp->current_SC; + unsigned int phase; ESPBUS(("esp_work_bus: ")); if(!SCptr) { ESPBUS(("reconnect\n")); return esp_do_reconnect(esp, eregs, dregs); } - - switch(SCptr->SCp.phase) { - case in_the_dark: - ESPBUS(("in the dark\n")); - return esp_do_phase_determine(esp, eregs, dregs); - - case in_slct_norm: - case in_slct_stop: - case in_slct_msg: - case in_slct_tag: - case in_slct_sneg: - ESPBUS(("finish selection\n")); + phase = SCptr->SCp.phase; + if ((phase & 0xf0) == in_phases_mask) + return bus_vector[(phase & 0x0f)](esp, eregs, dregs); + else if((phase & 0xf0) == in_slct_mask) return esp_select_complete(esp, eregs, dregs); - - case in_datain: - case in_dataout: - ESPBUS(("finish data\n")); - return esp_do_data_finale(esp, eregs, dregs); - - case in_msgout: - ESPBUS(("message out ")); - return esp_do_msgout(esp, eregs, dregs); - - case in_msgoutdone: - ESPBUS(("finish message out ")); - return esp_do_msgoutdone(esp, eregs, dregs); - - case in_msgin: - ESPBUS(("message in ")); - return esp_do_msgin(esp, eregs, dregs); - - case in_msgincont: - ESPBUS(("continue message in ")); - return esp_do_msgincont(esp, eregs, dregs); - - case in_msgindone: - ESPBUS(("finish message in ")); - return esp_do_msgindone(esp, eregs, dregs); - - case in_status: - ESPBUS(("status phase ")); - return esp_do_status(esp, eregs, dregs); - - case in_freeing: - ESPBUS(("freeing the bus ")); - return esp_do_freebus(esp, eregs, dregs); - - case in_cmdbegin: - ESPBUS(("begin slow cmd ")); - return esp_do_cmdbegin(esp, eregs, dregs); - - case in_cmdend: - ESPBUS(("end slow cmd ")); - return esp_do_cmddone(esp, eregs, dregs); - - default: - printk("esp%d: command in weird state %2x\n", - esp->esp_id, esp->current_SC->SCp.phase); - return do_reset_bus; - }; + else + return esp_bus_unexpected(esp, eregs, dregs); } +static espfunc_t isvc_vector[] = { + 0, + esp_do_phase_determine, + esp_do_resetbus, + esp_finish_reset, + esp_work_bus +}; + /* Main interrupt handler for an esp adapter. */ -static inline void esp_handle(struct Sparc_ESP *esp) +static void esp_handle(struct Sparc_ESP *esp) { struct sparc_dma_registers *dregs; struct Sparc_ESP_regs *eregs; @@ -3841,7 +4056,7 @@ ESPLOG(("esp%d: No current cmd during gross error, " "resetting bus\n", esp->esp_id)); what_next = do_reset_bus; - goto again; + goto state_machine; } } @@ -3862,9 +4077,11 @@ esp_reset_dma(esp); what_next = do_reset_bus; - goto again; + goto state_machine; } + esp->ireg = eregs->esp_intrpt; /* Unlatch intr and stat regs */ + if(esp->erev == fashme) { /* This chip is really losing. */ ESPHME(("HME[")); @@ -3877,17 +4094,12 @@ if(!(esp->sreg2 & ESP_STAT2_FEMPTY) || (esp->sreg2 & ESP_STAT2_F1BYTE)) { ESPHME(("fifo_workaround]")); - hme_fifo_hwbug_workaround(esp, eregs); + hme_fifo_read(esp, eregs); } else { ESPHME(("no_fifo_workaround]")); } } - esp->ireg = eregs->esp_intrpt; /* Unlatch intr and stat regs */ - - /* This cannot be done until this very moment. -DaveM */ - synchronize_irq(); - /* No current cmd is only valid at this point when there are * commands off the bus or we are trying a reset. */ @@ -3926,10 +4138,7 @@ } what_next = do_reset_bus; - goto again; - } - - if(!(esp->ireg & ~(ESP_INTR_FDONE | ESP_INTR_BSERV | ESP_INTR_DC))) { + } else if(!(esp->ireg & ~(ESP_INTR_FDONE | ESP_INTR_BSERV | ESP_INTR_DC))) { int phase; if(SCptr) { @@ -3941,13 +4150,12 @@ } else { ESPLOG(("esp%d: interrupt for no good reason...\n", esp->esp_id)); - goto esp_handle_done; + what_next = do_intr_end; } } else { ESPLOG(("esp%d: BSERV or FDONE or DC while SCptr==NULL\n", esp->esp_id)); what_next = do_reset_bus; - goto again; } } else if(esp->ireg & ESP_INTR_SR) { ESPLOG(("esp%d: SCSI bus reset interrupt\n", esp->esp_id)); @@ -3956,7 +4164,6 @@ ESPLOG(("esp%d: AIEEE we have been selected by another initiator!\n", esp->esp_id)); what_next = do_reset_bus; - goto again; } else if(esp->ireg & ESP_INTR_RSEL) { if(!SCptr) { /* This is ok. */ @@ -3971,95 +4178,22 @@ ESPLOG(("esp%d: Reselected while bus is busy\n", esp->esp_id)); what_next = do_reset_bus; - goto again; } } - /* We're trying to fight stack problems, and inline as much as - * possible without making this driver a mess. hate hate hate - * This is tier-one in our dual level SCSI state machine. - */ -again: - switch(what_next) { - case do_intr_end: - goto esp_handle_done; - - case do_work_bus: - what_next = esp_work_bus(esp, eregs, dregs); - break; - - case do_phase_determine: - what_next = esp_do_phase_determine(esp, eregs, dregs); - break; - - case do_reset_bus: - ESPLOG(("esp%d: resetting bus...\n", esp->esp_id)); - esp->resetting_bus = 1; - esp_cmd(esp, eregs, ESP_CMD_RS); - goto esp_handle_done; - - case do_reset_complete: - /* Tricky, we don't want to cause any more commands to - * go out until we clear all the live cmds by hand. - */ - if(esp->current_SC) { - Scsi_Cmnd *SCptr = esp->current_SC; - if(!SCptr->use_sg) - mmu_release_scsi_one(SCptr->SCp.have_data_in, - SCptr->request_bufflen, - esp->edev->my_bus); - else - mmu_release_scsi_sgl((struct mmu_sglist *) - SCptr->buffer, - SCptr->use_sg - 1, - esp->edev->my_bus); - SCptr->result = (DID_RESET << 16); - - SCptr->scsi_done(SCptr); - } - esp->current_SC = NULL; - if(esp->disconnected_SC) { - Scsi_Cmnd *SCptr; - while((SCptr = remove_first_SC(&esp->disconnected_SC))) { - if(!SCptr->use_sg) - mmu_release_scsi_one(SCptr->SCp.have_data_in, - SCptr->request_bufflen, - esp->edev->my_bus); - else - mmu_release_scsi_sgl((struct mmu_sglist *) - SCptr->buffer, - SCptr->use_sg - 1, - esp->edev->my_bus); - SCptr->result = (DID_RESET << 16); - - SCptr->scsi_done(SCptr); - } - } - esp->resetting_bus = 0; - - if(esp->current_SC) { - printk("esp%d: weird weird weird, current_SC not NULL after " - "SCSI bus reset.\n", esp->esp_id); - goto esp_handle_done; + /* This is tier-one in our dual level SCSI state machine. */ +state_machine: + while(what_next != do_intr_end) { + if (what_next >= do_phase_determine && + what_next < do_intr_end) + what_next = isvc_vector[what_next](esp, eregs, dregs); + else { + /* state is completely lost ;-( */ + ESPLOG(("esp%d: interrupt engine loses state, resetting bus\n", + esp->esp_id)); + what_next = do_reset_bus; } - - /* Now it is safe to execute more things. */ - if(esp->issue_SC) - esp_exec_cmd(esp); - goto esp_handle_done; - - default: - /* state is completely lost ;-( */ - ESPLOG(("esp%d: interrupt engine loses state, resetting bus\n", - esp->esp_id)); - what_next = do_reset_bus; - break; - - }; - goto again; - -esp_handle_done: - return; + } } #ifndef __sparc_v9__ diff -u --recursive --new-file v2.2.3/linux/drivers/scsi/esp.h linux/drivers/scsi/esp.h --- v2.2.3/linux/drivers/scsi/esp.h Thu Aug 6 14:06:33 1998 +++ linux/drivers/scsi/esp.h Sun Mar 21 14:19:07 1999 @@ -123,10 +123,31 @@ unchar seqreg; /* The ESP sequence register */ unchar sreg2; /* Copy of HME status2 register */ + /* To save register writes to the ESP, which can be expensive, we + * keep track of the previous value that various registers had for + * the last target we connected to. If they are the same for the + * current target, we skip the register writes as they are not needed. + */ + unchar prev_soff, prev_stp, prev_cfg3, __cache_pad; + + /* We also keep a cache of the previous FAS/HME DMA CSR register value. */ + unsigned int prev_hme_dmacsr; + /* The HME is the biggest piece of shit I have ever seen. */ unchar hme_fifo_workaround_buffer[16 * 2]; /* 16-bit/entry fifo for wide scsi */ unchar hme_fifo_workaround_count; + /* For each target we keep track of save/restore data + * pointer information. This needs to be updated majorly + * when we add support for tagged queueing. -DaveM + */ + struct esp_pointers { + char *saved_ptr; + struct scatterlist *saved_buffer; + int saved_this_residual; + int saved_buffers_residual; + } data_pointers[16] /*XXX [MAX_TAGS_PER_TARGET]*/; + /* Clock periods, frequencies, synchronization, etc. */ unsigned int cfreq; /* Clock frequency in HZ */ unsigned int cfact; /* Clock conversion factor */ @@ -217,7 +238,7 @@ #define ESP_CONFIG3_IDMSG 0x10 /* ID message checking (esp100a/hme) */ #define ESP_CONFIG3_FSCSI 0x10 /* Enable FAST SCSI (esp/fas236) */ #define ESP_CONFIG3_GTM 0x20 /* group2 SCSI2 support (esp/fas236) */ -#define ESP_CONFIG3_BIGID 0x20 /* SCSI-ID's are 4bits (hme) */ +#define ESP_CONFIG3_IDBIT3 0x20 /* Bit 3 of HME SCSI-ID (hme) */ #define ESP_CONFIG3_TBMS 0x40 /* Three-byte msg's ok (esp/fas236) */ #define ESP_CONFIG3_EWIDE 0x40 /* Enable Wide-SCSI (hme) */ #define ESP_CONFIG3_IMS 0x80 /* ID msg chk'ng (esp/fas236) */ diff -u --recursive --new-file v2.2.3/linux/drivers/scsi/fcal.c linux/drivers/scsi/fcal.c --- v2.2.3/linux/drivers/scsi/fcal.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/fcal.c Mon Mar 15 16:11:31 1999 @@ -0,0 +1,307 @@ +/* fcal.c: Fibre Channel Arbitrated Loop SCSI host adapter driver. + * + * Copyright (C) 1998,1999 Jakub Jelinek (jj@ultra.linux.cz) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_KMOD +#include +#endif + +#include + +#include "scsi.h" +#include "hosts.h" +#include "../fc4/fcp_impl.h" +#include "fcal.h" + +#include + +/* #define FCAL_DEBUG */ + +#define fcal_printk printk ("FCAL %s: ", fc->name); printk + +#ifdef FCAL_DEBUG +#define FCALD(x) fcal_printk x; +#define FCALND(x) printk ("FCAL: "); printk x; +#else +#define FCALD(x) +#define FCALND(x) +#endif + +static unsigned char alpa2target[] = { +0x7e, 0x7d, 0x7c, 0xff, 0x7b, 0xff, 0xff, 0xff, 0x7a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x79, +0x78, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x77, 0x76, 0xff, 0xff, 0x75, 0xff, 0x74, 0x73, 0x72, +0xff, 0xff, 0xff, 0x71, 0xff, 0x70, 0x6f, 0x6e, 0xff, 0x6d, 0x6c, 0x6b, 0x6a, 0x69, 0x68, 0xff, +0xff, 0x67, 0x66, 0x65, 0x64, 0x63, 0x62, 0xff, 0xff, 0x61, 0x60, 0xff, 0x5f, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0x5e, 0xff, 0x5d, 0x5c, 0x5b, 0xff, 0x5a, 0x59, 0x58, 0x57, 0x56, 0x55, 0xff, +0xff, 0x54, 0x53, 0x52, 0x51, 0x50, 0x4f, 0xff, 0xff, 0x4e, 0x4d, 0xff, 0x4c, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0x4b, 0xff, 0x4a, 0x49, 0x48, 0xff, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0xff, +0xff, 0x41, 0x40, 0x3f, 0x3e, 0x3d, 0x3c, 0xff, 0xff, 0x3b, 0x3a, 0xff, 0x39, 0xff, 0xff, 0xff, +0x38, 0x37, 0x36, 0xff, 0x35, 0xff, 0xff, 0xff, 0x34, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x33, +0x32, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x31, 0x30, 0xff, 0xff, 0x2f, 0xff, 0x2e, 0x2d, 0x2c, +0xff, 0xff, 0xff, 0x2b, 0xff, 0x2a, 0x29, 0x28, 0xff, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0xff, +0xff, 0x21, 0x20, 0x1f, 0x1e, 0x1d, 0x1c, 0xff, 0xff, 0x1b, 0x1a, 0xff, 0x19, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0x18, 0xff, 0x17, 0x16, 0x15, 0xff, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0xff, +0xff, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0xff, 0xff, 0x08, 0x07, 0xff, 0x06, 0xff, 0xff, 0xff, +0x05, 0x04, 0x03, 0xff, 0x02, 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 +}; + +static unsigned char target2alpa[] = { +0xef, 0xe8, 0xe4, 0xe2, 0xe1, 0xe0, 0xdc, 0xda, 0xd9, 0xd6, 0xd5, 0xd4, 0xd3, 0xd2, 0xd1, 0xce, +0xcd, 0xcc, 0xcb, 0xca, 0xc9, 0xc7, 0xc6, 0xc5, 0xc3, 0xbc, 0xba, 0xb9, 0xb6, 0xb5, 0xb4, 0xb3, +0xb2, 0xb1, 0xae, 0xad, 0xac, 0xab, 0xaa, 0xa9, 0xa7, 0xa6, 0xa5, 0xa3, 0x9f, 0x9e, 0x9d, 0x9b, +0x98, 0x97, 0x90, 0x8f, 0x88, 0x84, 0x82, 0x81, 0x80, 0x7c, 0x7a, 0x79, 0x76, 0x75, 0x74, 0x73, +0x72, 0x71, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x69, 0x67, 0x66, 0x65, 0x63, 0x5c, 0x5a, 0x59, 0x56, +0x55, 0x54, 0x53, 0x52, 0x51, 0x4e, 0x4d, 0x4c, 0x4b, 0x4a, 0x49, 0x47, 0x46, 0x45, 0x43, 0x3c, +0x3a, 0x39, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x27, 0x26, +0x25, 0x23, 0x1f, 0x1e, 0x1d, 0x1b, 0x18, 0x17, 0x10, 0x0f, 0x08, 0x04, 0x02, 0x01, 0x00 +}; + +struct proc_dir_entry proc_scsi_fcal = { + PROC_SCSI_FCAL, 4, "fcal", + S_IFDIR | S_IRUGO | S_IXUGO, 2 +}; + +static int fcal_encode_addr(Scsi_Cmnd *SCpnt, u16 *addr, fc_channel *fc, fcp_cmnd *fcmd); + +static void fcal_select_queue_depths(struct Scsi_Host *host, Scsi_Device *devlist) +{ + Scsi_Device *device; + + for (device = devlist; device; device = device->next) { + if (device->host != host) continue; + if (device->tagged_supported) + device->queue_depth = /* 254 */ 8; + else + device->queue_depth = 2; + } +} + +/* Detect all FC Arbitrated Loops attached to the machine. + fc4 module has done all the work for us... */ +__initfunc(int fcal_detect(Scsi_Host_Template *tpnt)) +{ + int nfcals = 0; + fc_channel *fc; + int fcalcount; + int i; + + tpnt->proc_dir = &proc_scsi_fcal; + fcalcount = 0; + for_each_online_fc_channel(fc) + if (fc->posmap) + fcalcount++; + FCALND(("%d channels online\n", fcalcount)) + if (!fcalcount) { +#if defined(MODULE) && defined(CONFIG_FC4_SOCAL_MODULE) && defined(CONFIG_KMOD) + request_module("socal"); + + for_each_online_fc_channel(fc) + if (fc->posmap) + fcalcount++; + if (!fcalcount) +#endif + return 0; + } + for_each_online_fc_channel(fc) { + struct Scsi_Host *host; + long *ages; + struct fcal *fcal; + + if (!fc->posmap) continue; + + /* Strange, this is already registered to some other SCSI host, then it cannot be fcal */ + if (fc->scsi_name[0]) continue; + memcpy (fc->scsi_name, "FCAL", 4); + + fc->can_queue = FCAL_CAN_QUEUE; + fc->rsp_size = 64; + fc->encode_addr = fcal_encode_addr; + + ages = kmalloc (128 * sizeof(long), GFP_KERNEL); + if (!ages) continue; + + host = scsi_register (tpnt, sizeof (struct fcal)); + if (!host) panic ("Cannot register FCAL host\n"); + + nfcals++; + + if (fc->module) __MOD_INC_USE_COUNT(fc->module); + + fcal = (struct fcal *)host->hostdata; + + fc->fcp_register(fc, TYPE_SCSI_FCP, 0); + + for (i = 0; i < fc->posmap->len; i++) { + int status, target, alpa; + + alpa = fc->posmap->list[i]; + FCALD(("Sending PLOGI to %02x\n", alpa)) + target = alpa2target[alpa]; + status = fc_do_plogi(fc, alpa, fcal->node_wwn + target, + fcal->nport_wwn + target); + FCALD(("PLOGI returned with status %d\n", status)) + if (status != FC_STATUS_OK) + continue; + FCALD(("Sending PRLI to %02x\n", alpa)) + status = fc_do_prli(fc, alpa); + FCALD(("PRLI returned with status %d\n", status)) + if (status == FC_STATUS_OK) + fcal->map[target] = 1; + } + + host->max_id = 127; + host->irq = fc->irq; +#ifdef __sparc_v9__ + host->unchecked_isa_dma = 1; +#endif + host->select_queue_depths = fcal_select_queue_depths; + + fc->channels = 1; + fc->targets = 127; + fc->ages = ages; + memset (ages, 0, 128 * sizeof(long)); + + fcal->fc = fc; + + FCALD(("Found FCAL\n")) + } + if (nfcals) +#ifdef __sparc__ + printk ("FCAL: Total of %d Sun Enterprise Network Array (A5000 or EX500) channels found\n", nfcals); +#else + printk ("FCAL: Total of %d Fibre Channel Arbitrated Loops found\n", nfcals); +#endif + return nfcals; +} + +int fcal_release(struct Scsi_Host *host) +{ + struct fcal *fcal = (struct fcal *)host->hostdata; + fc_channel *fc = fcal->fc; + + if (fc->module) __MOD_DEC_USE_COUNT(fc->module); + + fc->fcp_register(fc, TYPE_SCSI_FCP, 1); + FCALND((" releasing fcal.\n")); + kfree (fc->ages); + FCALND(("released fcal!\n")); + return 0; +} + +#undef SPRINTF +#define SPRINTF(args...) { if (pos < (buffer + length)) pos += sprintf (pos, ## args); } + +int fcal_proc_info (char *buffer, char **start, off_t offset, int length, int hostno, int inout) +{ + struct Scsi_Host *host = NULL; + struct fcal *fcal; + fc_channel *fc; + char *pos = buffer; + int i, j; + + for (host=scsi_hostlist; host; host=host->next) + if (host->host_no == hostno) + break; + + if (!host) return -ESRCH; + + if (inout) return length; + + fcal = (struct fcal *)host->hostdata; + fc = fcal->fc; + +#ifdef __sparc__ + SPRINTF ("Sun Enterprise Network Array (A5000 or E?500) on %s\n", fc->name); +#else + SPRINTF ("Fibre Channel Arbitrated Loop on %s\n", fc->name); +#endif + SPRINTF ("Initiator AL-PA: %02x\n", fc->sid); + + SPRINTF ("\nAttached devices: %s\n", host->host_queue ? "" : "none"); + + for (i = 0; i < fc->posmap->len; i++) { + unsigned char alpa = fc->posmap->list[i]; + unsigned char target; + u32 *u1, *u2; + + target = alpa2target[alpa]; + u1 = (u32 *)&fcal->nport_wwn[target]; + u2 = (u32 *)&fcal->node_wwn[target]; + if (!u1[0] && !u1[1]) { + SPRINTF (" [AL-PA: %02x] Not responded to PLOGI\n", alpa); + } else if (!fcal->map[target]) { + SPRINTF (" [AL-PA: %02x, Port WWN: %08x%08x, Node WWN: %08x%08x] Not responded to PRLI\n", + alpa, u1[0], u1[1], u2[0], u2[1]); + } else { + Scsi_Device *scd; + for (scd = host->host_queue ; scd; scd = scd->next) + if (scd->host->host_no == hostno && scd->id == target) { + SPRINTF (" [AL-PA: %02x, Id: %02d, Port WWN: %08x%08x, Node WWN: %08x%08x] ", + alpa, target, u1[0], u1[1], u2[0], u2[1]); + SPRINTF ("%s ", (scd->type < MAX_SCSI_DEVICE_CODE) ? + scsi_device_types[(short) scd->type] : "Unknown device"); + + for (j = 0; (j < 8) && (scd->vendor[j] >= 0x20); j++) + SPRINTF ("%c", scd->vendor[j]); + SPRINTF (" "); + + for (j = 0; (j < 16) && (scd->model[j] >= 0x20); j++) + SPRINTF ("%c", scd->model[j]); + + SPRINTF ("\n"); + } + } + } + SPRINTF ("\n"); + + *start = buffer + offset; + + if ((pos - buffer) < offset) + return 0; + else if (pos - buffer - offset < length) + return pos - buffer - offset; + else + return length; +} + +/* + For FC-AL, we use a simple addressing: we have just one channel 0, + and all AL-PAs are mapped to targets 0..0x7e + */ +static int fcal_encode_addr(Scsi_Cmnd *SCpnt, u16 *addr, fc_channel *fc, fcp_cmnd *fcmd) +{ + struct fcal *f; + + /* We don't support LUNs yet - I'm not sure if LUN should be in SCSI fcp_cdb, or in second byte of addr[0] */ + if (SCpnt->cmnd[1] & 0xe0) return -EINVAL; + /* FC-PLDA tells us... */ + memset(addr, 0, 8); + f = (struct fcal *)SCpnt->host->hostdata; + if (!f->map[SCpnt->target]) return -EINVAL; + /* Now, determine DID: It will be Native Identifier, so we zero upper + 2 bytes of the 3 byte DID, lowest byte will be AL-PA */ + fcmd->did = target2alpa[SCpnt->target]; + FCALD(("trying DID %06x\n", fcmd->did)) + return 0; +} + +#ifdef MODULE + +Scsi_Host_Template driver_template = FCAL; + +#include "scsi_module.c" + +EXPORT_NO_SYMBOLS; +#endif /* MODULE */ diff -u --recursive --new-file v2.2.3/linux/drivers/scsi/fcal.h linux/drivers/scsi/fcal.h --- v2.2.3/linux/drivers/scsi/fcal.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/fcal.h Mon Mar 15 16:11:31 1999 @@ -0,0 +1,46 @@ +/* fcal.h: Generic Fibre Channel Arbitrated Loop SCSI host adapter driver definitions. + * + * Copyright (C) 1998,1999 Jakub Jelinek (jj@ultra.linux.cz) + */ + +#ifndef _FCAL_H +#define _FCAL_H + +#include "../fc4/fcp_impl.h" + +struct fcal { + /* fc must be first */ + fc_channel *fc; + unsigned char map[128]; + fc_wwn nport_wwn[128]; + fc_wwn node_wwn[128]; +}; + +/* Arbitrary constant. Cannot be too large, as fc4 layer has limitations + for a particular channel */ +#define FCAL_CAN_QUEUE 512 + +int fcal_detect(Scsi_Host_Template *); +int fcal_release(struct Scsi_Host *); +int fcal_proc_info (char *, char **, off_t, int, int, int); + +#define FCAL { \ + name: "Fibre Channel Arbitrated Loop",\ + detect: fcal_detect, \ + release: fcal_release, \ + proc_info: fcal_proc_info, \ + queuecommand: fcp_scsi_queuecommand, \ + can_queue: FCAL_CAN_QUEUE, \ + this_id: -1, \ + sg_tablesize: 1, \ + cmd_per_lun: 1, \ + use_clustering: ENABLE_CLUSTERING, \ + use_new_eh_code: FCP_SCSI_USE_NEW_EH_CODE, \ + abort: fcp_old_abort, \ + eh_abort_handler: fcp_scsi_abort, \ + eh_device_reset_handler:fcp_scsi_dev_reset, \ + eh_bus_reset_handler: fcp_scsi_bus_reset, \ + eh_host_reset_handler: fcp_scsi_host_reset, \ +} + +#endif /* !(_FCAL_H) */ diff -u --recursive --new-file v2.2.3/linux/drivers/scsi/pluto.c linux/drivers/scsi/pluto.c --- v2.2.3/linux/drivers/scsi/pluto.c Wed Jul 1 19:38:55 1998 +++ linux/drivers/scsi/pluto.c Mon Mar 15 16:11:31 1999 @@ -1,6 +1,6 @@ /* pluto.c: SparcSTORAGE Array SCSI host adapter driver. * - * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997,1998,1999 Jakub Jelinek (jj@sunsite.mff.cuni.cz) * */ @@ -22,7 +22,7 @@ #include "scsi.h" #include "hosts.h" -#include "../fc4/fcp_scsi.h" +#include "../fc4/fcp_impl.h" #include "pluto.h" #include @@ -56,7 +56,7 @@ static struct timer_list fc_timer __initdata = { 0 }; struct semaphore fc_sem __initdata = MUTEX_LOCKED; -static int pluto_encode_addr(Scsi_Cmnd *SCpnt, u16 *addr); +static int pluto_encode_addr(Scsi_Cmnd *SCpnt, u16 *addr, fc_channel *fc, fcp_cmnd *fcmd); __initfunc(static void pluto_detect_timeout(unsigned long data)) { @@ -100,15 +100,19 @@ tpnt->proc_dir = &proc_scsi_pluto; fcscount = 0; - for_each_online_fc_channel(fc) - fcscount++; + for_each_online_fc_channel(fc) { + if (!fc->posmap) + fcscount++; + } PLND(("%d channels online\n", fcscount)) if (!fcscount) { #if defined(MODULE) && defined(CONFIG_FC4_SOC_MODULE) && defined(CONFIG_KMOD) request_module("soc"); - for_each_online_fc_channel(fc) - fcscount++; + for_each_online_fc_channel(fc) { + if (!fc->posmap) + fcscount++; + } if (!fcscount) #endif return 0; @@ -129,8 +133,9 @@ Scsi_Cmnd *SCpnt; struct Scsi_Host *host; struct pluto *pluto; - + if (i == fcscount) break; + if (fc->posmap) continue; PLD(("trying to find SSA\n")) @@ -233,9 +238,13 @@ host->max_id = inq->targets; host->max_channel = inq->channels; host->irq = fc->irq; - + +#ifdef __sparc_v9__ + host->unchecked_isa_dma = 1; +#endif + host->select_queue_depths = pluto_select_queue_depths; - + fc->channels = inq->channels + 1; fc->targets = inq->targets; fc->ages = ages; @@ -304,7 +313,7 @@ channel 0 id 0 lun 0 for CONTROLLER and channels 1 .. max_channel are normal single disks. */ -static int pluto_encode_addr(Scsi_Cmnd *SCpnt, u16 *addr) +static int pluto_encode_addr(Scsi_Cmnd *SCpnt, u16 *addr, fc_channel *fc, fcp_cmnd *fcmd) { PLND(("encode addr %d %d %d\n", SCpnt->channel, SCpnt->target, SCpnt->cmnd[1] & 0xe0)) /* We don't support LUNs - neither does SSA :) */ @@ -318,6 +327,8 @@ addr[2] = SCpnt->target; addr[3] = 0; } + /* We're Point-to-Point, so target it to the default DID */ + fcmd->did = fc->did; PLND(("trying %04x%04x%04x%04x\n", addr[0], addr[1], addr[2], addr[3])) return 0; } diff -u --recursive --new-file v2.2.3/linux/drivers/scsi/pluto.h linux/drivers/scsi/pluto.h --- v2.2.3/linux/drivers/scsi/pluto.h Thu Apr 23 20:21:35 1998 +++ linux/drivers/scsi/pluto.h Mon Mar 15 16:11:31 1999 @@ -6,7 +6,7 @@ #ifndef _PLUTO_H #define _PLUTO_H -#include "../fc4/fcp_scsi.h" +#include "../fc4/fcp_impl.h" struct pluto { /* This must be first */ diff -u --recursive --new-file v2.2.3/linux/drivers/scsi/qlogicpti.c linux/drivers/scsi/qlogicpti.c --- v2.2.3/linux/drivers/scsi/qlogicpti.c Mon Oct 5 13:13:40 1998 +++ linux/drivers/scsi/qlogicpti.c Mon Mar 15 16:11:31 1999 @@ -223,9 +223,15 @@ struct qlogicpti *qpti = (struct qlogicpti *) host->hostdata; struct qlogicpti_regs *qregs = qpti->qregs; u_short param[6]; + unsigned short risc_code_addr; int loop_count, i; unsigned long flags; + if(qpti->is_pti != 0) + risc_code_addr = pti_risc_code_addr01; + else + risc_code_addr = sbus_risc_code_addr01; + save_flags(flags); cli(); qregs->hcctrl = HCCTRL_PAUSE; @@ -264,7 +270,7 @@ /* Get RISC to start executing the firmware code. */ param[0] = MBOX_EXEC_FIRMWARE; - param[1] = risc_code_addr01; + param[1] = risc_code_addr; if(qlogicpti_mbox_command(qpti, param, 1)) { printk(KERN_EMERG "qlogicpti%d: Cannot execute ISP firmware.\n", qpti->qpti_id); @@ -352,21 +358,32 @@ struct qlogicpti_regs *qregs = qpti->qregs; unsigned short csum = 0; unsigned short param[6]; + unsigned short *risc_code, risc_code_addr, risc_code_length; unsigned long flags; #if !defined(MODULE) && !defined(__sparc_v9__) unsigned long dvma_addr; #endif int i, timeout; + if(qpti->is_pti != 0) { + risc_code = &pti_risc_code01[0]; + risc_code_addr = pti_risc_code_addr01; + risc_code_length = pti_risc_code_length01; + } else { + risc_code = &sbus_risc_code01[0]; + risc_code_addr = sbus_risc_code_addr01; + risc_code_length = sbus_risc_code_length01; + } + save_flags(flags); cli(); /* Verify the checksum twice, one before loading it, and once * afterwards via the mailbox commands. */ - for(i = 0; i < risc_code_length01; i++) - csum += risc_code01[i]; + for(i = 0; i < risc_code_length; i++) + csum += risc_code[i]; if(csum) { - printk(KERN_EMERG "qlogicpti%d: AIeee, firmware checksum failed!", + printk(KERN_EMERG "qlogicpti%d: Aieee, firmware checksum failed!", qpti->qpti_id); return 1; } @@ -406,11 +423,6 @@ qpti->differential = 0; qregs->hcctrl = HCCTRL_REL; - /* XXX Talk to PTI engineer about the following, ISP always - * XXX returns 0x4001 return status for stop firmware command, - * XXX documentation claims this means the cmd is unsupported - * XXX on this ISP. I think something fishy is going on. - */ param[0] = MBOX_STOP_FIRMWARE; param[1] = param[2] = param[3] = param[4] = param[5] = 0; if(qlogicpti_mbox_command(qpti, param, 1)) { @@ -423,13 +435,13 @@ /* Load the firmware. */ #if !defined(MODULE) && !defined(__sparc_v9__) if (sparc_cpu_model != sun4d) { - dvma_addr = (unsigned long) mmu_lockarea((char *)&risc_code01[0], - (sizeof(u_short) * risc_code_length01)); + dvma_addr = (unsigned long) mmu_lockarea((char *)&risc_code[0], + (sizeof(u_short) * risc_code_length)); param[0] = MBOX_LOAD_RAM; - param[1] = risc_code_addr01; + param[1] = risc_code_addr; param[2] = (dvma_addr >> 16); param[3] = (dvma_addr & 0xffff); - param[4] = (sizeof(u_short) * risc_code_length01); + param[4] = (sizeof(u_short) * risc_code_length); if(qlogicpti_mbox_command(qpti, param, 1) || (param[0] != MBOX_COMMAND_COMPLETE)) { printk(KERN_EMERG "qlogicpti%d: Firmware dload failed, I'm bolixed!\n", @@ -437,14 +449,14 @@ restore_flags(flags); return 1; } - mmu_unlockarea((char *)dvma_addr, (sizeof(u_short) * risc_code_length01)); + mmu_unlockarea((char *)dvma_addr, (sizeof(u_short) * risc_code_length)); } else #endif /* We need to do it this slow way always on Ultra, SS[12]000. */ - for(i = 0; i < risc_code_length01; i++) { + for(i = 0; i < risc_code_length; i++) { param[0] = MBOX_WRITE_RAM_WORD; - param[1] = risc_code_addr01 + i; - param[2] = risc_code01[i]; + param[1] = risc_code_addr + i; + param[2] = risc_code[i]; if(qlogicpti_mbox_command(qpti, param, 1) || param[0] != MBOX_COMMAND_COMPLETE) { printk("qlogicpti%d: Firmware dload failed, I'm bolixed!\n", @@ -464,7 +476,7 @@ /* Ask ISP to verify the checksum of the new code. */ param[0] = MBOX_VERIFY_CHECKSUM; - param[1] = risc_code_addr01; + param[1] = risc_code_addr; if(qlogicpti_mbox_command(qpti, param, 1) || (param[0] != MBOX_COMMAND_COMPLETE)) { printk(KERN_EMERG "qlogicpti%d: New firmware csum failure!\n", @@ -475,7 +487,7 @@ /* Start using newly downloaded firmware. */ param[0] = MBOX_EXEC_FIRMWARE; - param[1] = risc_code_addr01; + param[1] = risc_code_addr; qlogicpti_mbox_command(qpti, param, 1); param[0] = MBOX_ABOUT_FIRMWARE; @@ -491,16 +503,18 @@ qpti->fware_majrev = param[1]; qpti->fware_minrev = param[2]; - /* Load scsi initiator ID and interrupt level into sbus static ram. */ - param[0] = MBOX_WRITE_RAM_WORD; - param[1] = 0xff80; - param[2] = (unsigned short) qpti->scsi_id; - qlogicpti_mbox_command(qpti, param, 1); - - param[0] = MBOX_WRITE_RAM_WORD; - param[1] = 0xff00; - param[2] = (unsigned short) 3; - qlogicpti_mbox_command(qpti, param, 1); + if(qpti->is_pti != 0) { + /* Load scsi initiator ID and interrupt level into sbus static ram. */ + param[0] = MBOX_WRITE_RAM_WORD; + param[1] = 0xff80; + param[2] = (unsigned short) qpti->scsi_id; + qlogicpti_mbox_command(qpti, param, 1); + + param[0] = MBOX_WRITE_RAM_WORD; + param[1] = 0xff00; + param[2] = (unsigned short) 3; + qlogicpti_mbox_command(qpti, param, 1); + } restore_flags(flags); return 0; @@ -617,6 +631,10 @@ /* We are wide capable, 16 targets. */ qpti_host->max_id = MAX_TARGETS; +#ifdef __sparc_v9__ + qpti_host->unchecked_isa_dma = 1; +#endif + /* Setup back pointers and misc. state. */ qpti->qhost = qpti_host; qpti->qdev = qpti_dev; @@ -638,7 +656,8 @@ sizeof(qpti->prom_name)); qpti->prom_node = qpti_node; - is_pti = strcmp (qpti->prom_name, "QLGC,isp"); + qpti->is_pti = is_pti = + (strcmp (qpti->prom_name, "QLGC,isp") != 0); /* Setup the reg property for this device. */ prom_apply_sbus_ranges(qpti->qdev->my_bus, @@ -728,6 +747,9 @@ bsizes = (DMA_BURST32 - 1); qpti->bursts = bsizes; + /* Clear out Scsi_Cmnd array. */ + memset(qpti->cmd_slots, 0, sizeof(qpti->cmd_slots)); + /* The request and response queues must each be aligned * on a page boundry. */ @@ -747,11 +769,13 @@ /* Set adapter and per-device default values. */ qlogicpti_set_hostdev_defaults(qpti); - if (is_pti) { - /* Load the firmware. */ - if(qlogicpti_load_firmware(qpti)) - panic("PTI Qlogic/ISP firmware load failed"); + /* Load the firmware. */ + /* XXX Find out where is it possible to download + our sbus_risc_code on non-PTI ISP1000. */ + if(is_pti && qlogicpti_load_firmware(qpti)) + panic("SBUS Qlogic/ISP firmware load failed"); + if (is_pti) { /* Check the PTI status reg. */ if(qlogicpti_verify_tmon(qpti)) panic("PTI Qlogic/ISP tmon verification failed"); @@ -768,7 +792,10 @@ char buffer[60]; prom_getstring (qpti_node, "isp-fcode", buffer, 60); - printk("(Firmware %s)", buffer); + if (buffer[0]) + printk("(Firmware %s)", buffer); + if (prom_getbool(qpti_node, "differential")) + qpti->differential = 1; } printk (" [%s Wide, using %s interface]\n", @@ -831,11 +858,6 @@ memset(cmd, 0, sizeof(struct Command_Entry)); cmd->hdr.entry_cnt = 1; cmd->hdr.entry_type = ENTRY_COMMAND; -#ifdef __sparc_v9__ - cmd->handle = (u_int) (((unsigned long)Cmnd) - PAGE_OFFSET); /* magic mushroom */ -#else - cmd->handle = (u_int) ((unsigned long)Cmnd); /* magic mushroom */ -#endif cmd->target_id = Cmnd->target; cmd->target_lun = Cmnd->lun; cmd->cdb_length = Cmnd->cmd_len; @@ -924,9 +946,15 @@ cmd->dataseg[0].d_count = Cmnd->request_bufflen; cmd->segment_cnt = 1; } + + /* Committed, record Scsi_Cmd so we can find it later. */ + cmd->handle = in_ptr; + qpti->cmd_slots[in_ptr] = Cmnd; + qpti->cmd_count[Cmnd->target]++; qregs->mbox4 = in_ptr; qpti->req_in_ptr = in_ptr; + return in_ptr; } @@ -1099,9 +1127,18 @@ /* This looks like a network driver! */ out_ptr = qpti->res_out_ptr; while(out_ptr != in_ptr) { + u_int cmd_slot; + sts = (struct Status_Entry *) &qpti->res_cpu[out_ptr]; out_ptr = NEXT_RES_PTR(out_ptr); - Cmnd = (Scsi_Cmnd *) (((unsigned long)sts->handle)+PAGE_OFFSET); + + /* We store an index in the handle, not the pointer in + * some form. This avoids problems due to the fact + * that the handle provided is only 32-bits. -DaveM + */ + cmd_slot = sts->handle; + Cmnd = qpti->cmd_slots[cmd_slot]; + qpti->cmd_slots[cmd_slot] = NULL; if(sts->completion_status == CS_RESET_OCCURRED || sts->completion_status == CS_ABORTED || @@ -1144,8 +1181,8 @@ int again; spin_lock_irqsave(&io_request_lock, flags); - again = 0; do { + again = 0; for_each_qlogicpti(qpti) again |= qlogicpti_intr_handler(qpti); } while (again); diff -u --recursive --new-file v2.2.3/linux/drivers/scsi/qlogicpti.h linux/drivers/scsi/qlogicpti.h --- v2.2.3/linux/drivers/scsi/qlogicpti.h Thu Aug 6 14:06:33 1998 +++ linux/drivers/scsi/qlogicpti.h Mon Mar 15 16:11:31 1999 @@ -455,16 +455,24 @@ /* These are the hot elements in the cache, so they come first. */ struct qlogicpti *next; /* Next active adapter */ struct qlogicpti_regs *qregs; /* Adapter registers */ - u_int req_in_ptr; /* index of next request slot */ - u_int res_out_ptr; /* index of next result slot */ struct pti_queue_entry *res_cpu; /* Ptr to RESPONSE bufs (CPU) */ - __u32 res_dvma; /* Ptr to RESPONSE bufs (DVMA)*/ struct pti_queue_entry *req_cpu; /* Ptr to REQUEST bufs (CPU) */ + + __u32 res_dvma; /* Ptr to RESPONSE bufs (DVMA)*/ __u32 req_dvma; /* Ptr to REQUEST bufs (DVMA) */ + u_int req_in_ptr; /* index of next request slot */ + u_int res_out_ptr; /* index of next result slot */ + int cmd_count[MAX_TARGETS]; unsigned long tag_ages[MAX_TARGETS]; long send_marker; /* must we send a marker? */ + /* The cmd->handler is only 32-bits, so that things work even on monster + * Ex000 sparc64 machines with >4GB of ram we just keep track of the + * scsi command pointers here. This is essentially what Matt Jacob does. -DaveM + */ + Scsi_Cmnd *cmd_slots[QLOGICISP_REQ_QUEUE_LEN + 1]; + /* The rest of the elements are unimportant for performance. */ u_char fware_majrev, fware_minrev; struct Scsi_Host *qhost; @@ -487,11 +495,7 @@ #define SREG_IMASK 0x0c /* Interrupt level */ #define SREG_SPMASK 0x03 /* Mask for switch pack */ unsigned char swsreg; - -#if 0 - char res[RES_QUEUE_LEN+1][QUEUE_ENTRY_LEN]; - char req[QLOGICISP_REQ_QUEUE_LEN+1][QUEUE_ENTRY_LEN]; -#endif + unsigned char is_pti; /* Non-zero if this is a PTI board. */ }; /* How to twiddle them bits... */ diff -u --recursive --new-file v2.2.3/linux/drivers/scsi/qlogicpti_asm.c linux/drivers/scsi/qlogicpti_asm.c --- v2.2.3/linux/drivers/scsi/qlogicpti_asm.c Wed Apr 23 19:01:22 1997 +++ linux/drivers/scsi/qlogicpti_asm.c Mon Mar 15 16:11:31 1999 @@ -1,8 +1,8 @@ /* Version 1.24 Initiator Firmware (Aug 8, 1996) */ -unsigned short risc_code_addr01 = 0x1000; +unsigned short pti_risc_code_addr01 = 0x1000; -unsigned short risc_code01[] __initdata = { +unsigned short pti_risc_code01[] __initdata = { 0x0078, 0x1030, 0x0000, 0x231f, 0x0000, 0x12ff, 0x2043, 0x4f50, 0x5952, 0x4947, 0x4854, 0x2031, 0x3939, 0x312c, 0x3139, 0x3932, 0x2c31, 0x3939, 0x332c, 0x3139, 0x3934, 0x2051, 0x4c4f, 0x4749, @@ -1129,4 +1129,1170 @@ 0x331b, 0x2019, 0x2626, 0x7b22, 0x7b26, 0x007c, 0xae5b }; -unsigned short risc_code_length01 = 0x231f; +unsigned short pti_risc_code_length01 = 0x231f; + +/* Version 1.31.00 ISP1000 Initiator RISC firmware + * We use this for all non-PTI SBUS boards. + */ +unsigned short sbus_risc_code_addr01 = 0x1000; + +unsigned short sbus_risc_code01[] __initdata = { + 0x0078, 0x1030, 0xa5e3, 0x241a, 0x0001, 0x12ff, 0x2043, 0x4f50, + 0x5952, 0x4947, 0x4854, 0x2031, 0x3939, 0x312c, 0x3139, 0x3932, + 0x2c31, 0x3939, 0x332c, 0x3139, 0x3934, 0x2051, 0x4c4f, 0x4749, + 0x4320, 0x434f, 0x5250, 0x4f52, 0x4154, 0x494f, 0x4e00, 0x2049, + 0x5350, 0x3130, 0x3030, 0x2046, 0x6972, 0x6d77, 0x6172, 0x6520, + 0x2056, 0x6572, 0x7369, 0x6f6e, 0x2030, 0x312e, 0x3331, 0x2020, + 0x2071, 0x0010, 0x70c3, 0x0004, 0x20c9, 0x3fff, 0x2089, 0x10ca, + 0x70c7, 0x4953, 0x70cb, 0x5020, 0x70cf, 0x2020, 0x70d3, 0x0001, + 0x3f00, 0x70d6, 0x2031, 0x0030, 0x2079, 0x3500, 0x7863, 0x0000, + 0x2fa0, 0x2009, 0x012b, 0x2011, 0x0000, 0x20a9, 0x0040, 0x42a4, + 0x8109, 0x00c0, 0x104d, 0x789b, 0x0101, 0x780b, 0x0002, 0x780f, + 0x0002, 0x784f, 0x0bb8, 0x2069, 0x3540, 0x00a8, 0x106c, 0x681b, + 0x003c, 0x2009, 0x1313, 0xa18c, 0xff00, 0x3700, 0xa084, 0x00ff, + 0xa105, 0x20b8, 0x0078, 0x106e, 0x681b, 0x0028, 0x6807, 0x0007, + 0x680b, 0x00fa, 0x680f, 0x0008, 0x6813, 0x0005, 0x681f, 0x0000, + 0x6823, 0x0006, 0x6817, 0x0008, 0x6827, 0x0000, 0x2069, 0x3600, + 0x2011, 0x0020, 0x2009, 0x0010, 0x680b, 0x0c19, 0x680f, 0x0019, + 0x6803, 0xdd00, 0x6807, 0x001a, 0x6a1a, 0x2d00, 0xa0e8, 0x0008, + 0xa290, 0x0004, 0x8109, 0x00c0, 0x1084, 0x2069, 0x3680, 0x20a9, + 0x0080, 0x6837, 0x0000, 0x680b, 0x0040, 0x6817, 0x0064, 0x681f, + 0x0002, 0xade8, 0x0010, 0x0070, 0x10a7, 0x0078, 0x1099, 0x1078, + 0x1a42, 0x1078, 0x2f3d, 0x1078, 0x168b, 0x1078, 0x33bb, 0x3200, + 0xa085, 0x000d, 0x2090, 0x70c3, 0x0000, 0x0090, 0x10be, 0x70c0, + 0xa086, 0x0002, 0x00c0, 0x10be, 0x1078, 0x11be, 0x1078, 0x10ee, + 0x1078, 0x1821, 0x1078, 0x19b2, 0x1078, 0x3280, 0x1078, 0x1787, + 0x0078, 0x10be, 0x10d2, 0x10d4, 0x1b9f, 0x1b9f, 0x2f9b, 0x2f9b, + 0x1b9f, 0x1b9f, 0x0078, 0x10d2, 0x0078, 0x10d4, 0x0078, 0x10d6, + 0x0078, 0x10d8, 0x7008, 0x800c, 0x00c8, 0x10e9, 0x7007, 0x0002, + 0xa08c, 0x000c, 0x00c0, 0x10ea, 0x8004, 0x8004, 0x00c8, 0x10e9, + 0x087a, 0x097a, 0x70c3, 0x4002, 0x0078, 0x11c1, 0x0068, 0x1133, + 0x7814, 0xa005, 0x00c0, 0x10f8, 0x0010, 0x1134, 0x0078, 0x1133, + 0x2009, 0x3568, 0x2104, 0xa005, 0x00c0, 0x1133, 0x7814, 0xa086, + 0x0001, 0x00c0, 0x1105, 0x1078, 0x1540, 0x7817, 0x0000, 0x2009, + 0x356f, 0x2104, 0xa065, 0x0040, 0x1121, 0x2009, 0x356a, 0x211c, + 0x8108, 0x2114, 0x8108, 0x2104, 0xa210, 0xa399, 0x0000, 0x2009, + 0x001c, 0x6083, 0x0103, 0x1078, 0x161b, 0x00c0, 0x112d, 0x1078, + 0x1682, 0x2009, 0x356f, 0x200b, 0x0000, 0x2009, 0x3569, 0x2104, + 0x200b, 0x0000, 0xa005, 0x0040, 0x1131, 0x2001, 0x4005, 0x0078, + 0x11c0, 0x0078, 0x11be, 0x007c, 0x2061, 0x0000, 0x6018, 0xa084, + 0x0001, 0x0040, 0x113c, 0x007c, 0x70c3, 0x0000, 0x70c7, 0x0000, + 0x70cb, 0x0000, 0x70cf, 0x0000, 0x70c0, 0xa0bc, 0xffc0, 0x00c0, + 0x118c, 0x2038, 0x0079, 0x114c, 0x11be, 0x1209, 0x11d7, 0x1209, + 0x125a, 0x125a, 0x11ce, 0x159a, 0x1265, 0x11ca, 0x11db, 0x11dd, + 0x11df, 0x11e1, 0x159f, 0x11ca, 0x126b, 0x1287, 0x154e, 0x1594, + 0x11e3, 0x1475, 0x1497, 0x14b1, 0x14da, 0x142e, 0x143c, 0x1450, + 0x1464, 0x12f3, 0x11ca, 0x12a3, 0x12aa, 0x12af, 0x12b4, 0x12ba, + 0x12bf, 0x12c4, 0x12c9, 0x12ce, 0x12d2, 0x12e7, 0x11ca, 0x11ca, + 0x11ca, 0x11ca, 0x11ca, 0x12ff, 0x1308, 0x1317, 0x133d, 0x1347, + 0x134e, 0x137a, 0x1389, 0x1398, 0x13aa, 0x1413, 0x11ca, 0x11ca, + 0x11ca, 0x11ca, 0x11ca, 0x1423, 0xa0bc, 0xffa0, 0x00c0, 0x11ca, + 0x2038, 0xa084, 0x001f, 0x0079, 0x1195, 0x11ca, 0x11ca, 0x11ca, + 0x11ca, 0x11ca, 0x11ca, 0x11ca, 0x11ca, 0x11ca, 0x11ca, 0x11ca, + 0x11ca, 0x11ca, 0x11ca, 0x11ca, 0x11ca, 0x11ca, 0x11ca, 0x11ca, + 0x11ca, 0x11ca, 0x11ca, 0x11ca, 0x11ca, 0x11ca, 0x11ca, 0x15f7, + 0x1601, 0x1605, 0x1613, 0x11ca, 0x11ca, 0x72ca, 0x71c6, 0x2001, + 0x4006, 0x0078, 0x11c0, 0x73ce, 0x72ca, 0x71c6, 0x2001, 0x4000, + 0x70c2, 0x2061, 0x0000, 0x601b, 0x0001, 0x2091, 0x5000, 0x2091, + 0x4080, 0x007c, 0x70c3, 0x4001, 0x0078, 0x11c1, 0x2099, 0x0041, + 0x20a1, 0x0041, 0x20a9, 0x0005, 0x53a3, 0x0078, 0x11be, 0x70c4, + 0x70c3, 0x0004, 0x007a, 0x0078, 0x11be, 0x0078, 0x11be, 0x0078, + 0x11be, 0x0078, 0x11be, 0x2091, 0x8000, 0x70c3, 0x0000, 0x70c7, + 0x4953, 0x70cb, 0x5020, 0x70cf, 0x2020, 0x70d3, 0x0001, 0x3f00, + 0x70d6, 0x2079, 0x0000, 0x781b, 0x0001, 0x2031, 0x0030, 0x2059, + 0x1000, 0x2029, 0x0457, 0x2051, 0x0470, 0x2061, 0x0472, 0x20b9, + 0xffff, 0x20c1, 0x0000, 0x2091, 0x5000, 0x2091, 0x4080, 0x0078, + 0x0455, 0x71d0, 0x72c8, 0x73cc, 0x70c4, 0x20a0, 0x2098, 0x2031, + 0x0030, 0x81ff, 0x0040, 0x11be, 0x7007, 0x0004, 0x731a, 0x721e, + 0x2051, 0x0012, 0x2049, 0x1238, 0x2041, 0x11be, 0x7003, 0x0002, + 0xa786, 0x0001, 0x00c0, 0x122a, 0x2049, 0x1246, 0x2041, 0x1252, + 0x7003, 0x0003, 0x7017, 0x0000, 0x810b, 0x7112, 0x00c8, 0x1232, + 0x7017, 0x0001, 0x7007, 0x0001, 0xa786, 0x0001, 0x0040, 0x1246, + 0x700c, 0xa084, 0x007f, 0x8004, 0x2009, 0x0020, 0xa102, 0x0942, + 0x094a, 0x20a8, 0x26a0, 0x53a6, 0x0078, 0x10da, 0x700c, 0xa084, + 0x007f, 0x0040, 0x1246, 0x80ac, 0x0048, 0x1246, 0x2698, 0x53a5, + 0x0078, 0x10da, 0x700c, 0xa084, 0x007f, 0x80ac, 0x2698, 0x53a5, + 0x0078, 0x11be, 0x71c4, 0x70c8, 0x2114, 0xa79e, 0x0004, 0x00c0, + 0x1262, 0x200a, 0x72ca, 0x0078, 0x11bd, 0x70c7, 0x0001, 0x70cb, + 0x001f, 0x0078, 0x11be, 0x70c4, 0x72c8, 0x73cc, 0x74d0, 0x70c6, + 0x72ca, 0x73ce, 0x74d2, 0xa005, 0x0040, 0x1281, 0x8001, 0x7872, + 0x7a7a, 0x7b7e, 0x7c76, 0x7898, 0xa084, 0xfffc, 0x789a, 0x0078, + 0x1285, 0x7898, 0xa085, 0x0001, 0x789a, 0x0078, 0x11be, 0x70c4, + 0x72c8, 0x73cc, 0x74d4, 0x70c6, 0x72ca, 0x73ce, 0x74d6, 0xa005, + 0x0040, 0x129d, 0x8001, 0x7886, 0x7a8e, 0x7b92, 0x7c8a, 0x7898, + 0xa084, 0xfcff, 0x789a, 0x0078, 0x12a1, 0x7898, 0xa085, 0x0100, + 0x789a, 0x0078, 0x11be, 0x2009, 0x3559, 0x210c, 0x2011, 0x015c, + 0x0078, 0x11bc, 0x2009, 0x3541, 0x210c, 0x0078, 0x11bd, 0x2009, + 0x3542, 0x210c, 0x0078, 0x11bd, 0x2061, 0x3540, 0x610c, 0x6210, + 0x0078, 0x11bc, 0x2009, 0x3545, 0x210c, 0x0078, 0x11bd, 0x2009, + 0x3546, 0x210c, 0x0078, 0x11bd, 0x2009, 0x3547, 0x210c, 0x0078, + 0x11bd, 0x2009, 0x3548, 0x210c, 0x0078, 0x11bd, 0x7908, 0x7a0c, + 0x0078, 0x11bc, 0x71c4, 0x8107, 0xa084, 0x000f, 0x8003, 0x8003, + 0x8003, 0xa0e8, 0x3600, 0x6a00, 0x6804, 0xa084, 0x0008, 0x0040, + 0x12e4, 0x6b08, 0x0078, 0x12e5, 0x6b0c, 0x0078, 0x11bb, 0x77c4, + 0x1078, 0x169c, 0x2091, 0x8000, 0x6b1c, 0x6a14, 0x2091, 0x8001, + 0x2708, 0x0078, 0x11bb, 0x77c4, 0x1078, 0x169c, 0x2091, 0x8000, + 0x6908, 0x6a18, 0x6b10, 0x2091, 0x8001, 0x0078, 0x11bb, 0x71c4, + 0xa182, 0x0010, 0x00c8, 0x11b6, 0x1078, 0x1ac6, 0x0078, 0x11bb, + 0x71c4, 0xa182, 0x0010, 0x00c8, 0x11b6, 0x2011, 0x3541, 0x2204, + 0x007e, 0x2112, 0x1078, 0x1a7f, 0x017f, 0x0078, 0x11bd, 0x71c4, + 0x2011, 0x1335, 0x20a9, 0x0008, 0x2204, 0xa106, 0x0040, 0x1327, + 0x8210, 0x0070, 0x1325, 0x0078, 0x131c, 0x0078, 0x11b6, 0xa292, + 0x1335, 0x027e, 0x2011, 0x3542, 0x2204, 0x2112, 0x017f, 0x007e, + 0x1078, 0x1a8b, 0x017f, 0x0078, 0x11bd, 0x03e8, 0x00fa, 0x01f4, + 0x02ee, 0x0064, 0x0019, 0x0032, 0x004b, 0x2061, 0x3540, 0x610c, + 0x6210, 0x70c4, 0x600e, 0x70c8, 0x6012, 0x0078, 0x11bc, 0x2061, + 0x3540, 0x6114, 0x70c4, 0x6016, 0x0078, 0x11bd, 0x71c4, 0x2011, + 0x0004, 0x2019, 0x1212, 0xa186, 0x0028, 0x0040, 0x1367, 0x2011, + 0x0005, 0x2019, 0x1212, 0xa186, 0x0032, 0x0040, 0x1367, 0x2011, + 0x0006, 0x2019, 0x1313, 0xa186, 0x003c, 0x00c0, 0x11b6, 0x2061, + 0x3540, 0x6018, 0x007e, 0x611a, 0xa39c, 0xff00, 0x3700, 0xa084, + 0x00ff, 0xa305, 0x20b8, 0x1078, 0x1a9c, 0x1078, 0x33bb, 0x017f, + 0x0078, 0x11bd, 0x71c4, 0xa184, 0xffcf, 0x00c0, 0x11b6, 0x2011, + 0x3547, 0x2204, 0x2112, 0x007e, 0x1078, 0x1abe, 0x017f, 0x0078, + 0x11bd, 0x71c4, 0xa182, 0x0010, 0x00c8, 0x11b6, 0x2011, 0x3548, + 0x2204, 0x007e, 0x2112, 0x1078, 0x1aad, 0x017f, 0x0078, 0x11bd, + 0x71c4, 0x72c8, 0xa184, 0xfffd, 0x00c0, 0x11b5, 0xa284, 0xfffd, + 0x00c0, 0x11b5, 0x2100, 0x7908, 0x780a, 0x2200, 0x7a0c, 0x780e, + 0x0078, 0x11bc, 0x71c4, 0x8107, 0xa084, 0x000f, 0x8003, 0x8003, + 0x8003, 0xa0e8, 0x3600, 0x2019, 0x0000, 0x72c8, 0x6800, 0x007e, + 0xa226, 0x0040, 0x13d9, 0x6a02, 0xa484, 0x2000, 0x0040, 0x13c2, + 0xa39d, 0x0010, 0xa484, 0x1000, 0x0040, 0x13c8, 0xa39d, 0x0008, + 0xa484, 0x4000, 0x0040, 0x13d9, 0x810f, 0xa284, 0x4000, 0x0040, + 0x13d5, 0x1078, 0x1ae0, 0x0078, 0x13d9, 0x1078, 0x1ad2, 0x0078, + 0x13d9, 0x72cc, 0x82ff, 0x0040, 0x140b, 0x6808, 0xa206, 0x0040, + 0x140b, 0xa2a4, 0x00ff, 0x2061, 0x3540, 0x6118, 0xa186, 0x0028, + 0x0040, 0x13f2, 0xa186, 0x0032, 0x0040, 0x13f8, 0xa186, 0x003c, + 0x0040, 0x13fe, 0xa482, 0x0064, 0x0048, 0x1408, 0x0078, 0x1402, + 0xa482, 0x0050, 0x0048, 0x1408, 0x0078, 0x1402, 0xa482, 0x0043, + 0x0048, 0x1408, 0x71c4, 0x71c6, 0x027f, 0x72ca, 0x0078, 0x11b7, + 0x6a0a, 0xa39d, 0x000a, 0x6804, 0xa305, 0x6806, 0x027f, 0x6b0c, + 0x71c4, 0x0078, 0x11bb, 0x77c4, 0x1078, 0x169c, 0x2091, 0x8000, + 0x6a14, 0x6b1c, 0x2091, 0x8001, 0x70c8, 0x6816, 0x70cc, 0x681e, + 0x2708, 0x0078, 0x11bb, 0x71c4, 0x72c8, 0x73cc, 0xa182, 0x0010, + 0x00c8, 0x11b6, 0x1078, 0x1aee, 0x0078, 0x11bb, 0x77c4, 0x1078, + 0x169c, 0x2091, 0x8000, 0x6a08, 0xa295, 0x0002, 0x6a0a, 0x2091, + 0x8001, 0x2708, 0x0078, 0x11bc, 0x77c4, 0x1078, 0x169c, 0x2091, + 0x8000, 0x6a08, 0xa294, 0xfff9, 0x6a0a, 0x6804, 0xa005, 0x0040, + 0x144b, 0x1078, 0x1a23, 0x2091, 0x8001, 0x2708, 0x0078, 0x11bc, + 0x77c4, 0x1078, 0x169c, 0x2091, 0x8000, 0x6a08, 0xa295, 0x0004, + 0x6a0a, 0x6804, 0xa005, 0x0040, 0x145f, 0x1078, 0x1a23, 0x2091, + 0x8001, 0x2708, 0x0078, 0x11bc, 0x77c4, 0x2041, 0x0001, 0x2049, + 0x0005, 0x2051, 0x0020, 0x2091, 0x8000, 0x1078, 0x16a9, 0x2091, + 0x8001, 0x2708, 0x6a08, 0x0078, 0x11bc, 0x77c4, 0x72c8, 0x73cc, + 0x77c6, 0x72ca, 0x73ce, 0x1078, 0x1722, 0x00c0, 0x1493, 0x6818, + 0xa005, 0x0040, 0x148d, 0x2708, 0x1078, 0x1afe, 0x00c0, 0x148d, + 0x7817, 0xffff, 0x2091, 0x8001, 0x007c, 0x2091, 0x8001, 0x2001, + 0x4005, 0x0078, 0x11c0, 0x2091, 0x8001, 0x0078, 0x11be, 0x77c4, + 0x77c6, 0x2041, 0x0021, 0x2049, 0x0005, 0x2051, 0x0020, 0x2091, + 0x8000, 0x1078, 0x16a9, 0x2061, 0x3540, 0x60a3, 0x0003, 0x67b6, + 0x60a7, 0x0000, 0x7817, 0xffff, 0x2091, 0x8001, 0x1078, 0x1a23, + 0x007c, 0x77c8, 0x77ca, 0x77c4, 0x77c6, 0xa7bc, 0xff00, 0x2091, + 0x8000, 0x2061, 0x3540, 0x60a3, 0x0002, 0x60a7, 0x0000, 0x67b6, + 0x7817, 0xffff, 0x1078, 0x1a23, 0x2091, 0x8001, 0x2041, 0x0021, + 0x2049, 0x0004, 0x2051, 0x0010, 0x2091, 0x8000, 0x1078, 0x16a9, + 0x70c8, 0x6836, 0x8738, 0xa784, 0x0007, 0x00c0, 0x14ce, 0x2091, + 0x8001, 0x007c, 0x7898, 0xa084, 0x0003, 0x00c0, 0x14fe, 0x2039, + 0x0000, 0x2041, 0x0021, 0x2049, 0x0004, 0x2051, 0x0008, 0x1078, + 0x169c, 0x2091, 0x8000, 0x6808, 0xa80d, 0x690a, 0x2091, 0x8001, + 0x8738, 0xa784, 0x0007, 0x00c0, 0x14e7, 0xa7bc, 0xff00, 0x873f, + 0x8738, 0x873f, 0xa784, 0x0f00, 0x00c0, 0x14e7, 0x2091, 0x8000, + 0x2069, 0x0100, 0x6830, 0xa084, 0x0040, 0x0040, 0x1527, 0x684b, + 0x0004, 0x20a9, 0x0014, 0x6848, 0xa084, 0x0004, 0x0040, 0x1514, + 0x0070, 0x1514, 0x0078, 0x150b, 0x684b, 0x0009, 0x20a9, 0x0014, + 0x6848, 0xa084, 0x0001, 0x0040, 0x1521, 0x0070, 0x1521, 0x0078, + 0x1518, 0x20a9, 0x00fa, 0x0070, 0x1527, 0x0078, 0x1523, 0x2079, + 0x3500, 0x7817, 0x0001, 0x2061, 0x3540, 0x60a3, 0x0001, 0x60a7, + 0x0000, 0x60c3, 0x000f, 0x7898, 0xa085, 0x0002, 0x789a, 0x6808, + 0xa084, 0xfffd, 0x680a, 0x681b, 0x0046, 0x2091, 0x8001, 0x007c, + 0x7898, 0xa084, 0xfffd, 0x789a, 0xa084, 0x0001, 0x00c0, 0x154a, + 0x1078, 0x176a, 0x71c4, 0x71c6, 0x794a, 0x007c, 0x74c4, 0x73c8, + 0x72cc, 0x74c6, 0x73ca, 0x72ce, 0x2079, 0x3500, 0x2009, 0x0040, + 0x1078, 0x1679, 0x0040, 0x1590, 0x1078, 0x1649, 0x0040, 0x1564, + 0x1078, 0x1682, 0x0078, 0x1590, 0x6010, 0x2091, 0x8001, 0x7817, + 0xffff, 0x2009, 0x3568, 0x200b, 0x0005, 0x8108, 0x200b, 0x0000, + 0x8108, 0x230a, 0x8108, 0x220a, 0x8108, 0x240a, 0x8108, 0x200a, + 0x8108, 0x200b, 0x0000, 0x8108, 0x2c0a, 0xa02e, 0x2530, 0x0e7e, + 0x1078, 0x2f16, 0x0e7f, 0x6592, 0x65a2, 0x6696, 0x66a6, 0x60ab, + 0x0000, 0x60af, 0x0000, 0x2091, 0x8001, 0x1078, 0x1a23, 0x007c, + 0x70c3, 0x4005, 0x0078, 0x11c1, 0x71c4, 0x70c7, 0x0000, 0x7906, + 0x0078, 0x11be, 0x71c4, 0x71c6, 0x2168, 0x0078, 0x15a1, 0x2069, + 0x1000, 0x690c, 0xa016, 0x2d04, 0xa210, 0x8d68, 0x8109, 0x00c0, + 0x15a3, 0xa285, 0x0000, 0x00c0, 0x15b1, 0x70c3, 0x4000, 0x0078, + 0x15b3, 0x70c3, 0x4003, 0x70ca, 0x0078, 0x11c1, 0x71c4, 0x72c8, + 0x73cc, 0x2100, 0xa184, 0xfffc, 0x00c0, 0x11ca, 0x2100, 0x0079, + 0x15c1, 0x15d8, 0x15ed, 0x15ef, 0x15f1, 0x70c3, 0x4003, 0x71ce, + 0x72d2, 0x73d6, 0x0078, 0x15d4, 0x70c3, 0x4000, 0x70cf, 0x0000, + 0x70d3, 0x0000, 0x70d7, 0x0000, 0x77c6, 0x71ca, 0x0078, 0x11be, + 0x2031, 0x15f3, 0x2624, 0x8630, 0x2412, 0x2204, 0xa446, 0x00c0, + 0x15c5, 0xa484, 0xffff, 0x00c0, 0x15da, 0x2031, 0x15f3, 0x8210, + 0x8319, 0xa384, 0xffff, 0x00c0, 0x15da, 0x0078, 0x15cc, 0x0078, + 0x15cc, 0x0078, 0x15cc, 0x5555, 0xaaaa, 0xffff, 0x0000, 0x7960, + 0x71c6, 0x71c4, 0xa182, 0x0003, 0x00c8, 0x11b6, 0x7962, 0x0078, + 0x11be, 0x7960, 0x71c6, 0x0078, 0x11be, 0x7954, 0x71c6, 0x71c4, + 0x7956, 0x7958, 0x71ca, 0x71c8, 0x795a, 0x795c, 0x71ce, 0x71cc, + 0x795e, 0x0078, 0x11be, 0x7954, 0x71c6, 0x7958, 0x71ca, 0x795c, + 0x71ce, 0x0078, 0x11be, 0x700c, 0xa084, 0x007f, 0x0040, 0x1627, + 0x7007, 0x0004, 0x7004, 0xa084, 0x0004, 0x00c0, 0x1622, 0x7017, + 0x0000, 0x7112, 0x721a, 0x731e, 0x8108, 0x810c, 0x81a9, 0x8c98, + 0x20a1, 0x0030, 0x6080, 0x20a2, 0x53a6, 0x780c, 0xa085, 0x0000, + 0x7002, 0x7007, 0x0001, 0x7108, 0x8104, 0x00c8, 0x163b, 0x7007, + 0x0002, 0xa184, 0x000c, 0x710c, 0xa184, 0x0300, 0x7003, 0x0000, + 0x007c, 0x700c, 0xa084, 0x007f, 0x0040, 0x1655, 0x7007, 0x0004, + 0x7004, 0xa084, 0x0004, 0x00c0, 0x1650, 0x7017, 0x0000, 0x7112, + 0x721a, 0x731e, 0x2099, 0x0030, 0x8108, 0x81ac, 0x780c, 0xa085, + 0x0001, 0x7002, 0x7007, 0x0001, 0x7008, 0x800c, 0x00c8, 0x1664, + 0x7007, 0x0002, 0xa08c, 0x000c, 0x00c0, 0x1676, 0x710c, 0xa184, + 0x0300, 0x00c0, 0x1676, 0x2ca0, 0x53a5, 0xa006, 0x7003, 0x0000, + 0x007c, 0x7850, 0xa065, 0x0040, 0x1681, 0x2c04, 0x7852, 0x2063, + 0x0000, 0x007c, 0x0f7e, 0x2079, 0x3500, 0x7850, 0x2062, 0x2c00, + 0x7852, 0x0f7f, 0x007c, 0x2011, 0x4000, 0x7a52, 0x2019, 0x015c, + 0x8319, 0x0040, 0x1699, 0xa280, 0x002f, 0x2012, 0x2010, 0x0078, + 0x1690, 0x2013, 0x0000, 0x007c, 0xa784, 0x0f00, 0x800c, 0xa784, + 0x0007, 0x8003, 0x8003, 0x8003, 0x8003, 0xa105, 0xa0e8, 0x3680, + 0x007c, 0x1078, 0x169c, 0x2900, 0x682a, 0x2a00, 0x682e, 0x6808, + 0xa084, 0xffef, 0xa80d, 0x690a, 0x2009, 0x354f, 0x210c, 0x6804, + 0xa005, 0x0040, 0x16c6, 0xa116, 0x00c0, 0x16c6, 0x2060, 0x6000, + 0x6806, 0x017e, 0x200b, 0x0000, 0x0078, 0x16c9, 0x2009, 0x0000, + 0x017e, 0x6804, 0xa065, 0x0040, 0x16d8, 0x6000, 0x6806, 0x1078, + 0x16e9, 0x1078, 0x17d5, 0x6810, 0x8001, 0x6812, 0x00c0, 0x16c9, + 0x017f, 0x6902, 0x6906, 0x007c, 0xa065, 0x0040, 0x16e8, 0x6098, + 0x609b, 0x0000, 0x2008, 0x1078, 0x1682, 0x2100, 0x0078, 0x16dc, + 0x007c, 0x6003, 0x0103, 0x20a9, 0x001c, 0xac80, 0x0004, 0x20a0, + 0x2001, 0x0000, 0x40a4, 0x6828, 0x6016, 0x682c, 0x601e, 0x007c, + 0x0e7e, 0x2071, 0x3540, 0x7040, 0xa08c, 0x0080, 0x00c0, 0x1706, + 0xa088, 0x3580, 0x2d0a, 0x8000, 0x7042, 0xa006, 0x0e7f, 0x007c, + 0x0e7e, 0x2071, 0x3540, 0x2009, 0x3580, 0x7240, 0x8221, 0x8211, + 0x0048, 0x1720, 0x2104, 0x8108, 0xad06, 0x00c0, 0x170f, 0x8119, + 0x211e, 0x8108, 0x8318, 0x8211, 0x00c8, 0x1718, 0x7442, 0xa006, + 0x0e7f, 0x007c, 0x1078, 0x169c, 0x2091, 0x8000, 0x6804, 0x781e, + 0xa065, 0x0040, 0x1769, 0x0078, 0x1733, 0x2c00, 0x781e, 0x6000, + 0xa065, 0x0040, 0x1769, 0x600c, 0xa306, 0x00c0, 0x172d, 0x6008, + 0xa206, 0x00c0, 0x172d, 0x2c28, 0x2001, 0x354f, 0x2004, 0xac06, + 0x0040, 0x1769, 0x6804, 0xac06, 0x00c0, 0x1750, 0x6000, 0x2060, + 0x6806, 0xa005, 0x00c0, 0x1750, 0x6803, 0x0000, 0x0078, 0x175a, + 0x6400, 0x781c, 0x2060, 0x6402, 0xa486, 0x0000, 0x00c0, 0x175a, + 0x2c00, 0x6802, 0x2560, 0x1078, 0x16e9, 0x6017, 0x0005, 0x601f, + 0x0020, 0x1078, 0x17d5, 0x6810, 0x8001, 0x6812, 0x2001, 0xffff, + 0xa005, 0x007c, 0x2039, 0x0000, 0x2041, 0x0021, 0x2049, 0x0004, + 0x2051, 0x0008, 0x2091, 0x8000, 0x1078, 0x16a9, 0x8738, 0xa784, + 0x0007, 0x00c0, 0x1774, 0xa7bc, 0xff00, 0x873f, 0x8738, 0x873f, + 0xa784, 0x0f00, 0x00c0, 0x1774, 0x2091, 0x8001, 0x007c, 0x2061, + 0x0000, 0x6018, 0xa084, 0x0001, 0x00c0, 0x1794, 0x78ac, 0x78af, + 0x0000, 0xa005, 0x00c0, 0x1795, 0x007c, 0xa08c, 0xfff0, 0x0040, + 0x179b, 0x1078, 0x1b81, 0x0079, 0x179d, 0x17ad, 0x17af, 0x17b5, + 0x17b9, 0x17ad, 0x17bd, 0x17ad, 0x17ad, 0x17ad, 0x17ad, 0x17c3, + 0x17c7, 0x17ad, 0x17ad, 0x17ad, 0x17ad, 0x1078, 0x1b81, 0x1078, + 0x176a, 0x2001, 0x8001, 0x0078, 0x17cd, 0x2001, 0x8003, 0x0078, + 0x17cd, 0x2001, 0x8004, 0x0078, 0x17cd, 0x1078, 0x176a, 0x2001, + 0x8006, 0x0078, 0x17cd, 0x2001, 0x800c, 0x0078, 0x17cd, 0x1078, + 0x176a, 0x2001, 0x800d, 0x0078, 0x17cd, 0x70c2, 0x2061, 0x0000, + 0x601b, 0x0001, 0x2091, 0x4080, 0x007c, 0x2c04, 0x6082, 0x2c08, + 0x2063, 0x0000, 0x7864, 0x8000, 0x7866, 0x7868, 0xa005, 0x796a, + 0x0040, 0x17e5, 0x2c02, 0x0078, 0x17e6, 0x796e, 0x007c, 0x0c7e, + 0x2061, 0x3500, 0x6883, 0x0103, 0x2d08, 0x206b, 0x0000, 0x6064, + 0x8000, 0x6066, 0x6068, 0xa005, 0x616a, 0x0040, 0x17fa, 0x2d02, + 0x0078, 0x17fb, 0x616e, 0x0c7f, 0x007c, 0x1078, 0x180e, 0x0040, + 0x180d, 0x0c7e, 0x6098, 0xa065, 0x0040, 0x1808, 0x1078, 0x16dc, + 0x0c7f, 0x609b, 0x0000, 0x1078, 0x1682, 0x007c, 0x786c, 0xa065, + 0x0040, 0x1820, 0x2091, 0x8000, 0x7864, 0x8001, 0x7866, 0x2c04, + 0x786e, 0xa005, 0x00c0, 0x181e, 0x786a, 0x8000, 0x2091, 0x8001, + 0x007c, 0x7898, 0xa005, 0x00c0, 0x186f, 0x7974, 0x70d0, 0x0005, + 0x0005, 0x72d0, 0xa206, 0x00c0, 0x1826, 0x2200, 0xa106, 0x00c0, + 0x183d, 0x7804, 0xa005, 0x0040, 0x186f, 0x7807, 0x0000, 0x0068, + 0x186f, 0x2091, 0x4080, 0x0078, 0x186f, 0x1078, 0x1679, 0x0040, + 0x186f, 0x7a7c, 0x7b78, 0x8107, 0x8004, 0x8004, 0xa210, 0xa399, + 0x0000, 0x2009, 0x0040, 0x1078, 0x1649, 0x0040, 0x1866, 0x1078, + 0x1682, 0x7880, 0x8000, 0x7882, 0xa086, 0x0002, 0x00c0, 0x186f, + 0x2091, 0x8000, 0x78af, 0x0002, 0x7883, 0x0000, 0x7898, 0xa085, + 0x0003, 0x789a, 0x2091, 0x8001, 0x0078, 0x186f, 0x7883, 0x0000, + 0x1078, 0x199c, 0x6000, 0xa084, 0x0007, 0x0079, 0x1870, 0x007c, + 0x1878, 0x1887, 0x18a7, 0x1878, 0x18b9, 0x1878, 0x1878, 0x1878, + 0x2039, 0x0400, 0x78a8, 0xa705, 0x78aa, 0x6004, 0xa705, 0x6006, + 0x1078, 0x18f7, 0x6018, 0x78a6, 0x1078, 0x1984, 0x007c, 0x78a8, + 0xa084, 0x0100, 0x0040, 0x188e, 0x0078, 0x1878, 0x78ab, 0x0000, + 0x6000, 0x8007, 0xa084, 0x00ff, 0x789e, 0x8001, 0x609b, 0x0000, + 0x0040, 0x18a4, 0x1078, 0x18f7, 0x0040, 0x18a4, 0x78a8, 0xa085, + 0x0100, 0x78aa, 0x0078, 0x18a6, 0x1078, 0x191b, 0x007c, 0x78a8, + 0xa08c, 0x0e00, 0x00c0, 0x18b0, 0xa084, 0x0100, 0x00c0, 0x18b2, + 0x0078, 0x1878, 0x1078, 0x18f7, 0x00c0, 0x18b8, 0x1078, 0x191b, + 0x007c, 0x78a8, 0xa084, 0x0100, 0x0040, 0x18c0, 0x0078, 0x1878, + 0x78ab, 0x0000, 0x6710, 0x20a9, 0x0001, 0x6014, 0xa084, 0x00ff, + 0xa005, 0x0040, 0x18dd, 0xa7bc, 0xff00, 0x20a9, 0x0008, 0xa08e, + 0x0001, 0x0040, 0x18dd, 0x2039, 0x0000, 0x20a9, 0x0080, 0xa08e, + 0x0002, 0x0040, 0x18dd, 0x0078, 0x18f4, 0x1078, 0x169c, 0x2d00, + 0x2091, 0x8000, 0x682b, 0x0000, 0x682f, 0x0000, 0x6808, 0xa084, + 0xffde, 0x680a, 0x2d00, 0xa080, 0x0010, 0x2068, 0x2091, 0x8001, + 0x0070, 0x18f4, 0x0078, 0x18e0, 0x1078, 0x1682, 0x007c, 0x78a0, + 0xa06d, 0x00c0, 0x1902, 0x2c00, 0x78a2, 0x78a6, 0x609b, 0x0000, + 0x0078, 0x190e, 0x2c00, 0x689a, 0x609b, 0x0000, 0x78a2, 0x2d00, + 0x6002, 0x78a4, 0xad06, 0x00c0, 0x190e, 0x6002, 0x789c, 0x8001, + 0x789e, 0x00c0, 0x191a, 0x78a8, 0xa084, 0x0000, 0x78aa, 0x78a4, + 0x2060, 0xa006, 0x007c, 0xa02e, 0x2530, 0x6118, 0xa184, 0x0060, + 0x619e, 0x0040, 0x1927, 0x0e7e, 0x1078, 0x2f16, 0x0e7f, 0x6592, + 0x65a2, 0x6696, 0x66a6, 0x60ab, 0x0000, 0x60af, 0x0000, 0x6710, + 0x1078, 0x169c, 0x2091, 0x8000, 0x6808, 0xa084, 0x0001, 0x0040, + 0x1949, 0x2091, 0x8001, 0x1078, 0x16e9, 0x2091, 0x8000, 0x1078, + 0x17d5, 0x2091, 0x8001, 0x78a3, 0x0000, 0x78a7, 0x0000, 0x0078, + 0x1983, 0x6020, 0xa096, 0x0001, 0x00c0, 0x1950, 0x8000, 0x6022, + 0x6a10, 0x6814, 0x2091, 0x8001, 0xa202, 0x0048, 0x195f, 0x0040, + 0x195f, 0x2039, 0x0200, 0x1078, 0x1984, 0x0078, 0x1983, 0x2c08, + 0x2091, 0x8000, 0x6800, 0xa065, 0x0040, 0x1967, 0x6102, 0x6902, + 0x00c0, 0x196b, 0x6906, 0x2160, 0x6003, 0x0000, 0x6810, 0x8000, + 0x6812, 0x2091, 0x8001, 0x6808, 0xa08c, 0x0040, 0x0040, 0x197d, + 0xa086, 0x0040, 0x680a, 0x1078, 0x16f8, 0x1078, 0x1a23, 0x78a7, + 0x0000, 0x78a3, 0x0000, 0x007c, 0x6004, 0xa705, 0x6006, 0x2091, + 0x8000, 0x1078, 0x17d5, 0x2091, 0x8001, 0x78a4, 0xa065, 0x0040, + 0x1997, 0x6098, 0x78a6, 0x609b, 0x0000, 0x0078, 0x1987, 0x78a3, + 0x0000, 0x78a7, 0x0000, 0x007c, 0x7970, 0x7874, 0x8000, 0xa10a, + 0x00c8, 0x19a3, 0xa006, 0x7876, 0x70d2, 0x7804, 0xa005, 0x0040, + 0x19b1, 0x8001, 0x7806, 0x00c0, 0x19b1, 0x0068, 0x19b1, 0x2091, + 0x4080, 0x007c, 0x0068, 0x19cc, 0x2029, 0x0000, 0x786c, 0xa065, + 0x0040, 0x19c7, 0x1078, 0x19cd, 0x0040, 0x19c7, 0x057e, 0x1078, + 0x19e3, 0x057f, 0x00c0, 0x19c7, 0x8528, 0x0078, 0x19b6, 0x85ff, + 0x0040, 0x19cc, 0x2091, 0x4080, 0x007c, 0x7b84, 0x7988, 0x72d4, + 0x0005, 0x0005, 0x70d4, 0xa206, 0x00c0, 0x19cf, 0x2200, 0xa102, + 0x00c0, 0x19dd, 0x2300, 0xa005, 0x007c, 0x0048, 0x19e1, 0xa302, + 0x007c, 0x8002, 0x007c, 0x1078, 0x1a15, 0x2009, 0x001c, 0x6024, + 0xa005, 0x0040, 0x19ed, 0x2009, 0x0040, 0x1078, 0x161b, 0x0040, + 0x1a06, 0x7894, 0x8000, 0x7896, 0xa086, 0x0002, 0x00c0, 0x1a14, + 0x2091, 0x8000, 0x78af, 0x0003, 0x7897, 0x0000, 0x7898, 0xa085, + 0x0300, 0x789a, 0x2091, 0x8001, 0x0078, 0x1a14, 0x7897, 0x0000, + 0x1078, 0x17fd, 0x7984, 0x7888, 0x8000, 0xa10a, 0x00c8, 0x1a11, + 0xa006, 0x788a, 0x70d6, 0xa006, 0x007c, 0x8107, 0x8004, 0x8004, + 0x7a90, 0x7b8c, 0xa210, 0xa399, 0x0000, 0x007c, 0x2009, 0x3568, + 0x2091, 0x8000, 0x200a, 0x0f7e, 0x2079, 0x0100, 0x2009, 0x3540, + 0x2091, 0x8000, 0x2104, 0xa086, 0x0000, 0x00c0, 0x1a3e, 0x2009, + 0x3512, 0x2104, 0xa005, 0x00c0, 0x1a3e, 0x7830, 0xa084, 0x00c0, + 0x00c0, 0x1a3e, 0x0018, 0x1a3e, 0x781b, 0x0044, 0x2091, 0x8001, + 0x0f7f, 0x007c, 0x127e, 0x2091, 0x2300, 0x2071, 0x3540, 0x2079, + 0x0100, 0x2019, 0x2ddb, 0x20a1, 0x012b, 0x2304, 0xa005, 0x0040, + 0x1a5a, 0x789a, 0x8318, 0x23ac, 0x8318, 0x2398, 0x53a6, 0x3318, + 0x0078, 0x1a4d, 0x789b, 0x0020, 0x20a9, 0x0010, 0x78af, 0x0000, + 0x78af, 0x0220, 0x0070, 0x1a66, 0x0078, 0x1a5e, 0x7003, 0x0000, + 0x1078, 0x1b65, 0x7004, 0xa084, 0x000f, 0xa085, 0x6280, 0x7806, + 0x780f, 0x9200, 0x7843, 0x00d8, 0x7853, 0x0080, 0x780b, 0x0008, + 0x7047, 0x357f, 0x7043, 0x0000, 0x127f, 0x2000, 0x007c, 0xa18c, + 0x000f, 0x2011, 0x0101, 0x2204, 0xa084, 0xfff0, 0xa105, 0x2012, + 0x1078, 0x1b65, 0x007c, 0x2011, 0x0101, 0x20a9, 0x0009, 0x810b, + 0x0070, 0x1a94, 0x0078, 0x1a8f, 0xa18c, 0x0e00, 0x2204, 0xa084, + 0xf1ff, 0xa105, 0x2012, 0x007c, 0x2009, 0x0101, 0x20a9, 0x0005, + 0x8213, 0x0070, 0x1aa5, 0x0078, 0x1aa0, 0xa294, 0x00e0, 0x2104, + 0xa084, 0xff1f, 0xa205, 0x200a, 0x007c, 0x2011, 0x0101, 0x20a9, + 0x000c, 0x810b, 0x0070, 0x1ab6, 0x0078, 0x1ab1, 0xa18c, 0xf000, + 0x2204, 0xa084, 0x0fff, 0xa105, 0x2012, 0x007c, 0x2011, 0x0102, + 0x2204, 0xa084, 0xffcf, 0xa105, 0x2012, 0x007c, 0x8103, 0x8003, + 0xa080, 0x0020, 0x0c7e, 0x2061, 0x0100, 0x609a, 0x62ac, 0x63ac, + 0x0c7f, 0x007c, 0x8103, 0x8003, 0xa080, 0x0022, 0x0c7e, 0x2061, + 0x0100, 0x609a, 0x60a4, 0xa084, 0xffdf, 0x60ae, 0x0c7f, 0x007c, + 0x8103, 0x8003, 0xa080, 0x0022, 0x0c7e, 0x2061, 0x0100, 0x609a, + 0x60a4, 0xa085, 0x0020, 0x60ae, 0x0c7f, 0x007c, 0x8103, 0x8003, + 0xa080, 0x0020, 0x0c7e, 0x2061, 0x0100, 0x609a, 0x60a4, 0x62ae, + 0x2010, 0x60a4, 0x63ae, 0x2018, 0x0c7f, 0x007c, 0x2091, 0x8000, + 0x0c7e, 0x0e7e, 0x6818, 0xa005, 0x0040, 0x1b43, 0x2061, 0x3f80, + 0x1078, 0x1b4b, 0x0040, 0x1b31, 0x20a9, 0x0000, 0x2061, 0x3e80, + 0x0c7e, 0x1078, 0x1b4b, 0x0040, 0x1b1d, 0x0c7f, 0x8c60, 0x0070, + 0x1b1b, 0x0078, 0x1b10, 0x0078, 0x1b43, 0x007f, 0xa082, 0x3e80, + 0x2071, 0x3540, 0x70ba, 0x601c, 0xa085, 0x0800, 0x601e, 0x71b6, + 0x60a7, 0x0000, 0x2001, 0x0004, 0x70a2, 0x1078, 0x1a1e, 0x0078, + 0x1b3f, 0x2071, 0x3540, 0x601c, 0xa085, 0x0800, 0x601e, 0x71b6, + 0x60a7, 0x0000, 0x2001, 0x0006, 0x70a2, 0x1078, 0x1a1e, 0x2001, + 0x0000, 0x0078, 0x1b45, 0x2001, 0x0001, 0x2091, 0x8001, 0xa005, + 0x0e7f, 0x0c7f, 0x007c, 0x2c04, 0xa005, 0x0040, 0x1b62, 0x2060, + 0x600c, 0xa306, 0x00c0, 0x1b5f, 0x6008, 0xa206, 0x00c0, 0x1b5f, + 0x6010, 0xa106, 0x00c0, 0x1b5f, 0xa006, 0x0078, 0x1b64, 0x6000, + 0x0078, 0x1b4c, 0xa085, 0x0001, 0x007c, 0x2011, 0x3541, 0x220c, + 0xa18c, 0x000f, 0x2011, 0x013b, 0x2204, 0xa084, 0x0100, 0x0040, + 0x1b80, 0x2019, 0x0112, 0x201b, 0x1000, 0x2021, 0x013a, 0x2122, + 0x2013, 0x0080, 0x2013, 0x008c, 0x2013, 0x0000, 0x201b, 0x0000, + 0x007c, 0x0068, 0x1b81, 0x007e, 0x2071, 0x0000, 0x7018, 0xa084, + 0x0001, 0x00c0, 0x1b86, 0x007f, 0x2e08, 0x2071, 0x0010, 0x70ca, + 0x007f, 0x70c6, 0x70c3, 0x8002, 0x2071, 0x0000, 0x701b, 0x0001, + 0x2091, 0x4080, 0x007f, 0x2070, 0x007f, 0x0078, 0x1b9d, 0x107e, + 0x007e, 0x127e, 0x2091, 0x2300, 0x7f3c, 0x7e58, 0x7c30, 0x7d38, + 0xa594, 0x003f, 0xa484, 0x4000, 0x0040, 0x1bb4, 0xa784, 0x007c, + 0x00c0, 0x2da7, 0x1078, 0x1b81, 0xa49c, 0x000f, 0xa382, 0x0004, + 0x0050, 0x1bbc, 0x1078, 0x1b81, 0x8507, 0xa084, 0x000f, 0x0079, + 0x1bc1, 0x1fed, 0x209d, 0x20c3, 0x22e9, 0x2576, 0x25be, 0x25f5, + 0x2670, 0x26ca, 0x274f, 0x1be7, 0x1bd1, 0x1e56, 0x1f20, 0x2555, + 0x1bd1, 0x1078, 0x1b81, 0x0018, 0x1ba4, 0x127f, 0x2091, 0x8001, + 0x007f, 0x107f, 0x007c, 0x7003, 0x0000, 0x703f, 0x0000, 0x7030, + 0xa005, 0x0040, 0x1be5, 0x7033, 0x0000, 0x0018, 0x1ba4, 0x705c, + 0xa005, 0x00c0, 0x1ca5, 0x70a0, 0xa084, 0x0007, 0x0079, 0x1bf0, + 0x1cd1, 0x1bf8, 0x1c06, 0x1c27, 0x1c4d, 0x1c79, 0x1c77, 0x1bf8, + 0x7808, 0xa084, 0xfffd, 0x780a, 0x2009, 0x0046, 0x1078, 0x241b, + 0x00c0, 0x1c04, 0x7003, 0x0004, 0x0078, 0x1bd3, 0x1078, 0x2d69, + 0x00c0, 0x1c25, 0x70b4, 0x8007, 0x789b, 0x007e, 0x78aa, 0x789b, + 0x0010, 0x78ab, 0x000c, 0x789b, 0x0060, 0x78ab, 0x0001, 0x785b, + 0x0004, 0x2009, 0x00f7, 0x1078, 0x2419, 0x00c0, 0x1c25, 0x7003, + 0x0004, 0x70c3, 0x000f, 0x7033, 0x3570, 0x0078, 0x1bd3, 0x1078, + 0x2d69, 0x00c0, 0x1c4b, 0x71b4, 0x8107, 0x789b, 0x007e, 0x78aa, + 0x789b, 0x0010, 0xa18c, 0x0007, 0xa18d, 0x00c0, 0x79aa, 0x78ab, + 0x0006, 0x789b, 0x0060, 0x78ab, 0x0002, 0x785b, 0x0004, 0x2009, + 0x00f7, 0x1078, 0x2419, 0x00c0, 0x1c4b, 0x7003, 0x0004, 0x70c3, + 0x000f, 0x7033, 0x3570, 0x0078, 0x1bd3, 0x1078, 0x2d69, 0x00c0, + 0x1c75, 0x71b4, 0x8107, 0x789b, 0x007e, 0x78aa, 0x789b, 0x0010, + 0xa18c, 0x0007, 0xa18d, 0x00c0, 0x79aa, 0x78ab, 0x0020, 0x71b8, + 0x79aa, 0x78ab, 0x000d, 0x789b, 0x0060, 0x78ab, 0x0004, 0x785b, + 0x0004, 0x2009, 0x00f7, 0x1078, 0x2419, 0x00c0, 0x1c75, 0x7003, + 0x0004, 0x70c3, 0x000f, 0x7033, 0x3570, 0x0078, 0x1bd3, 0x0078, + 0x1c27, 0x1078, 0x2d69, 0x00c0, 0x1bd3, 0x70bc, 0x2068, 0x789b, + 0x0010, 0x6f10, 0x1078, 0x2cac, 0x2c50, 0x6810, 0x007e, 0x8007, + 0xa084, 0x000f, 0x8003, 0x8003, 0x8003, 0xa080, 0x3600, 0x2048, + 0x0c7e, 0x2060, 0x704a, 0x6000, 0x704e, 0x6004, 0x7052, 0x0c7f, + 0x007f, 0xa084, 0x0007, 0xa085, 0x0080, 0x78aa, 0x6e18, 0x2041, + 0x0001, 0x2001, 0x0004, 0x0078, 0x1dcc, 0x1078, 0x2d69, 0x00c0, + 0x1bd3, 0x789b, 0x0010, 0x705c, 0x2068, 0x6f10, 0x1078, 0x2cac, + 0x2c50, 0x6008, 0xa085, 0x0010, 0x600a, 0x6820, 0xa005, 0x0040, + 0x1cc1, 0xa082, 0x0006, 0x0048, 0x1cbf, 0x0078, 0x1cc1, 0x6823, + 0x0005, 0x6810, 0xa084, 0x0007, 0xa085, 0x0080, 0x78aa, 0x2031, + 0x0020, 0x2041, 0x0001, 0x1078, 0x2dc8, 0x2001, 0x0003, 0x0078, + 0x1dcc, 0x0018, 0x1ba4, 0x7440, 0xa485, 0x0000, 0x0040, 0x1ceb, + 0xa080, 0x3580, 0x2030, 0x7144, 0x8108, 0xa12a, 0x0048, 0x1ce2, + 0x2009, 0x3580, 0x2164, 0x6504, 0x85ff, 0x00c0, 0x1cf8, 0x8421, + 0x00c0, 0x1cdc, 0x7146, 0x7003, 0x0000, 0x703f, 0x0000, 0x0078, + 0x1bd3, 0x7640, 0xa6b0, 0x3580, 0x7144, 0x2600, 0x0078, 0x1ce7, + 0x7146, 0x2568, 0x2558, 0x753e, 0x2c50, 0x6034, 0xa085, 0x0000, + 0x00c0, 0x1cf5, 0x6708, 0x7736, 0xa784, 0x013f, 0x0040, 0x1d2a, + 0xa784, 0x0021, 0x00c0, 0x1cf5, 0xa784, 0x0002, 0x0040, 0x1d17, + 0xa784, 0x0004, 0x0040, 0x1cf5, 0xa7bc, 0xfffb, 0x670a, 0xa784, + 0x0008, 0x00c0, 0x1cf5, 0xa784, 0x0010, 0x00c0, 0x1cf5, 0xa784, + 0x0100, 0x0040, 0x1d2a, 0x6018, 0xa005, 0x00c0, 0x1cf5, 0xa7bc, + 0xfeff, 0x670a, 0x681f, 0x0000, 0x6e18, 0xa684, 0x000e, 0x6118, + 0x0040, 0x1d3a, 0x601c, 0xa102, 0x0048, 0x1d3d, 0x0040, 0x1d3d, + 0x0078, 0x1cf1, 0x81ff, 0x00c0, 0x1cf1, 0xa784, 0x0080, 0x00c0, + 0x1d43, 0x700c, 0x6022, 0xa7bc, 0xff7f, 0x670a, 0x6b10, 0x8307, + 0xa084, 0x000f, 0x8003, 0x8003, 0x8003, 0xa080, 0x3600, 0x2060, + 0x2048, 0x704a, 0x6000, 0x704e, 0x6004, 0x7052, 0x2a60, 0x0018, + 0x1ba4, 0x789b, 0x0010, 0xa046, 0x1078, 0x2d69, 0x00c0, 0x1bd3, + 0x6b10, 0xa39c, 0x0007, 0xa39d, 0x00c0, 0x704c, 0xa084, 0x8000, + 0x0040, 0x1d6e, 0xa684, 0x0001, 0x0040, 0x1d74, 0xa39c, 0xffbf, + 0xa684, 0x000e, 0x00c0, 0x2901, 0xa684, 0x0010, 0x0040, 0x1d7e, + 0xa39d, 0x0020, 0xa684, 0x000e, 0x00c0, 0x2907, 0x7baa, 0x8840, + 0xa684, 0x000e, 0x00c0, 0x1d89, 0xa7bd, 0x0010, 0x670a, 0x0078, + 0x1dca, 0x714c, 0xa18c, 0x0800, 0x0040, 0x290d, 0x2011, 0x0021, + 0x8004, 0x8004, 0x0048, 0x1da0, 0x2011, 0x0022, 0x8004, 0x0048, + 0x1da0, 0x2011, 0x0020, 0x8004, 0x0048, 0x1da0, 0x0040, 0x1dca, + 0x7aaa, 0x8840, 0x1078, 0x2d82, 0x6a10, 0x610c, 0x8108, 0xa18c, + 0x00ff, 0xa1e0, 0x3e80, 0x2c64, 0x8cff, 0x0040, 0x1dc1, 0x6010, + 0xa206, 0x00c0, 0x1dab, 0x60b4, 0x8001, 0x60b6, 0x00c0, 0x1da6, + 0x0c7e, 0x2a60, 0x6008, 0xa085, 0x0100, 0x600a, 0x0c7f, 0x0078, + 0x1cd1, 0x1078, 0x2d69, 0x00c0, 0x1bd3, 0x2a60, 0x610e, 0x79aa, + 0x8840, 0x712e, 0x2001, 0x0001, 0x007e, 0x7150, 0xa184, 0x0018, + 0x0040, 0x1de0, 0xa184, 0x0010, 0x0040, 0x1dda, 0x1078, 0x2ad7, + 0x00c0, 0x1de0, 0xa184, 0x0008, 0x0040, 0x1de0, 0x1078, 0x29f1, + 0x007f, 0x7002, 0xa68c, 0x0060, 0x88ff, 0x0040, 0x1de9, 0xa18d, + 0x0004, 0x795a, 0x69b2, 0x789b, 0x0060, 0x2800, 0x78aa, 0x789b, + 0x0061, 0x6814, 0xa085, 0x8000, 0x6816, 0x78aa, 0x157e, 0x137e, + 0x147e, 0x20a1, 0x012c, 0x789b, 0x0000, 0x8000, 0x80ac, 0xad80, + 0x000a, 0x2098, 0x53a6, 0x147f, 0x137f, 0x157f, 0x6810, 0x8007, + 0x789b, 0x007e, 0x78aa, 0x6d90, 0x7dd6, 0x7dde, 0x6e94, 0x7ed2, + 0x7eda, 0x7830, 0xa084, 0x00c0, 0x00c0, 0x1e18, 0x0098, 0x1e20, + 0x6008, 0xa084, 0xffef, 0x600a, 0x1078, 0x2d82, 0x0078, 0x1bdb, + 0x7200, 0xa284, 0x0007, 0xa086, 0x0001, 0x00c0, 0x1e2d, 0x781b, + 0x0049, 0x1078, 0x2d82, 0x0078, 0x1e3e, 0x6ab0, 0xa295, 0x2000, + 0x7a5a, 0x781b, 0x0049, 0x1078, 0x2d82, 0x7200, 0x2500, 0xa605, + 0x0040, 0x1e3e, 0xa284, 0x0007, 0x1079, 0x1e4c, 0xad80, 0x0008, + 0x7032, 0xa284, 0x0007, 0xa086, 0x0001, 0x00c0, 0x1e4a, 0x6018, + 0x8000, 0x601a, 0x0078, 0x1bd3, 0x1e54, 0x30f3, 0x30f3, 0x30e2, + 0x30f3, 0x1e54, 0x1e54, 0x1e54, 0x1078, 0x1b81, 0x7808, 0xa084, + 0xfffd, 0x780a, 0x0f7e, 0x2079, 0x3500, 0x7898, 0x0f7f, 0xa084, + 0x0001, 0x0040, 0x1e7c, 0x70a0, 0xa086, 0x0001, 0x00c0, 0x1e6b, + 0x70a2, 0x0078, 0x1f04, 0x70a0, 0xa086, 0x0005, 0x00c0, 0x1e7a, + 0x70bc, 0x2068, 0x6817, 0x0004, 0x6813, 0x0000, 0x681c, 0xa085, + 0x0008, 0x681e, 0x70a3, 0x0000, 0x157e, 0x2011, 0x0004, 0x71a0, + 0xa186, 0x0001, 0x0040, 0x1e9e, 0xa186, 0x0007, 0x00c0, 0x1e8e, + 0x2009, 0x352b, 0x200b, 0x0005, 0x0078, 0x1e9e, 0x2009, 0x3513, + 0x2104, 0x2009, 0x3512, 0x200a, 0x2009, 0x352b, 0x200b, 0x0001, + 0x70a3, 0x0000, 0x70a7, 0x0001, 0x0078, 0x1ea0, 0x70a3, 0x0000, + 0x1078, 0x2eca, 0x20a9, 0x0010, 0x2039, 0x0000, 0x1078, 0x2bb1, + 0xa7b8, 0x0100, 0x0070, 0x1eae, 0x0078, 0x1ea6, 0x7000, 0x2020, + 0x0079, 0x1eb2, 0x1ee0, 0x1ec9, 0x1ec9, 0x1ebc, 0x1ee0, 0x1ee0, + 0x1eba, 0x1eba, 0x1078, 0x1b81, 0x2021, 0x3557, 0x2404, 0xa005, + 0x0040, 0x1ec9, 0xad06, 0x00c0, 0x1ec9, 0x6800, 0x2022, 0x0078, + 0x1ed9, 0x681c, 0xa084, 0x0001, 0x00c0, 0x1ed5, 0x6f10, 0x1078, + 0x2cac, 0x1078, 0x28e4, 0x0078, 0x1ed9, 0x7054, 0x2060, 0x6800, + 0x6002, 0x6a16, 0x681c, 0xa085, 0x0008, 0x681e, 0x1078, 0x17e7, + 0x2021, 0x3f80, 0x1078, 0x1f0a, 0x2021, 0x3557, 0x1078, 0x1f0a, + 0x20a9, 0x0000, 0x2021, 0x3e80, 0x1078, 0x1f0a, 0x8420, 0x0070, + 0x1ef3, 0x0078, 0x1eec, 0x20a9, 0x0080, 0x2061, 0x3680, 0x6018, + 0x6110, 0xa102, 0x6012, 0x601b, 0x0000, 0xace0, 0x0010, 0x0070, + 0x1f03, 0x0078, 0x1ef7, 0x157f, 0x7003, 0x0000, 0x703f, 0x0000, + 0x0078, 0x1bd3, 0x047e, 0x2404, 0xa005, 0x0040, 0x1f1c, 0x2068, + 0x6800, 0x007e, 0x6a16, 0x681c, 0xa085, 0x0008, 0x681e, 0x1078, + 0x17e7, 0x007f, 0x0078, 0x1f0c, 0x047f, 0x2023, 0x0000, 0x007c, + 0xa282, 0x0003, 0x0050, 0x1f26, 0x1078, 0x1b81, 0x2300, 0x0079, + 0x1f29, 0x1f2c, 0x1f9f, 0x1fad, 0xa282, 0x0002, 0x0040, 0x1f32, + 0x1078, 0x1b81, 0x70a0, 0x70a3, 0x0000, 0x70c3, 0x0000, 0x0079, + 0x1f39, 0x1f41, 0x1f41, 0x1f43, 0x1f77, 0x2913, 0x1f41, 0x1f77, + 0x1f41, 0x1078, 0x1b81, 0x77b4, 0x1078, 0x2bb1, 0x77b4, 0xa7bc, + 0x0f00, 0x1078, 0x2cac, 0x6018, 0xa005, 0x0040, 0x1f6e, 0x2021, + 0x3f80, 0x2009, 0x0004, 0x2011, 0x0010, 0x1078, 0x1fc8, 0x0040, + 0x1f6e, 0x157e, 0x20a9, 0x0000, 0x2021, 0x3e80, 0x047e, 0x2009, + 0x0004, 0x2011, 0x0010, 0x1078, 0x1fc8, 0x047f, 0x0040, 0x1f6d, + 0x8420, 0x0070, 0x1f6d, 0x0078, 0x1f5e, 0x157f, 0x8738, 0xa784, + 0x0007, 0x00c0, 0x1f49, 0x0078, 0x1bdb, 0x0078, 0x1bdb, 0x77b4, + 0x1078, 0x2cac, 0x6018, 0xa005, 0x0040, 0x1f9d, 0x2021, 0x3f80, + 0x2009, 0x0005, 0x2011, 0x0020, 0x1078, 0x1fc8, 0x0040, 0x1f9d, + 0x157e, 0x20a9, 0x0000, 0x2021, 0x3e80, 0x047e, 0x2009, 0x0005, + 0x2011, 0x0020, 0x1078, 0x1fc8, 0x047f, 0x0040, 0x1f9c, 0x8420, + 0x0070, 0x1f9c, 0x0078, 0x1f8d, 0x157f, 0x0078, 0x1bdb, 0x2200, + 0x0079, 0x1fa2, 0x1fa5, 0x1fa7, 0x1fa7, 0x1078, 0x1b81, 0x70a3, + 0x0000, 0x70a7, 0x0001, 0x0078, 0x1bd3, 0x2200, 0x0079, 0x1fb0, + 0x1fb5, 0x1fa7, 0x1fb3, 0x1078, 0x1b81, 0x1078, 0x2428, 0x7000, + 0xa086, 0x0001, 0x00c0, 0x28ba, 0x1078, 0x28fa, 0x6008, 0xa084, + 0xffef, 0x600a, 0x1078, 0x28ad, 0x0040, 0x28ba, 0x0078, 0x1cd1, + 0x2404, 0xa005, 0x0040, 0x1fe9, 0x2068, 0x2d04, 0x007e, 0x6810, + 0xa706, 0x0040, 0x1fd7, 0x2d20, 0x007f, 0x0078, 0x1fc9, 0x007f, + 0x2022, 0x6916, 0x681c, 0xa205, 0x681e, 0x1078, 0x17e7, 0x6010, + 0x8001, 0x6012, 0x6008, 0xa084, 0xffef, 0x600a, 0x1078, 0x28fa, + 0x007c, 0xa085, 0x0001, 0x0078, 0x1fe8, 0x2300, 0x0079, 0x1ff0, + 0x1ff5, 0x1ff3, 0x2038, 0x1078, 0x1b81, 0x78e4, 0xa005, 0x00d0, + 0x2018, 0x0018, 0x2018, 0x2008, 0xa084, 0x0030, 0x00c0, 0x2004, + 0x781b, 0x0049, 0x0078, 0x1bd3, 0x78ec, 0xa084, 0x0003, 0x0040, + 0x2000, 0x2100, 0xa084, 0x0007, 0x0079, 0x200e, 0x2026, 0x202c, + 0x2020, 0x2016, 0x2d63, 0x2d63, 0x2016, 0x2032, 0x1078, 0x1b81, + 0x7000, 0xa005, 0x0040, 0x1bdb, 0x2001, 0x0003, 0x0078, 0x22fd, + 0x1078, 0x2b94, 0x781b, 0x0055, 0x0078, 0x1bd3, 0x1078, 0x2b94, + 0x781b, 0x00dc, 0x0078, 0x1bd3, 0x1078, 0x2b94, 0x781b, 0x00e3, + 0x0078, 0x1bd3, 0x1078, 0x2b94, 0x781b, 0x009d, 0x0078, 0x1bd3, + 0xa584, 0x000f, 0x00c0, 0x2062, 0x1078, 0x2428, 0x7000, 0x0079, + 0x2041, 0x2049, 0x2056, 0x2049, 0x28ba, 0x204b, 0x28ba, 0x2049, + 0x2049, 0x1078, 0x1b81, 0x71a0, 0x70a3, 0x0000, 0xa186, 0x0004, + 0x00c0, 0x2054, 0x0078, 0x2913, 0x0078, 0x28ba, 0x1078, 0x28fa, + 0x6008, 0xa084, 0xffef, 0x600a, 0x1078, 0x28ad, 0x0040, 0x28ba, + 0x0078, 0x1cd1, 0x78e4, 0xa005, 0x00d0, 0x2018, 0x0018, 0x2018, + 0x2008, 0xa084, 0x0030, 0x00c0, 0x2071, 0x781b, 0x0049, 0x0078, + 0x1bd3, 0x78ec, 0xa084, 0x0003, 0x0040, 0x206d, 0x2100, 0xa184, + 0x0007, 0x0079, 0x207b, 0x208b, 0x2091, 0x2085, 0x2083, 0x2d63, + 0x2d63, 0x2083, 0x2d5b, 0x1078, 0x1b81, 0x1078, 0x2b9c, 0x781b, + 0x0055, 0x0078, 0x1bd3, 0x1078, 0x2b9c, 0x781b, 0x00dc, 0x0078, + 0x1bd3, 0x1078, 0x2b9c, 0x781b, 0x00e3, 0x0078, 0x1bd3, 0x1078, + 0x2b9c, 0x781b, 0x009d, 0x0078, 0x1bd3, 0x2300, 0x0079, 0x20a0, + 0x20a5, 0x20a3, 0x20a7, 0x1078, 0x1b81, 0x0078, 0x2670, 0x6817, + 0x0008, 0x78a3, 0x0000, 0x79e4, 0xa184, 0x0030, 0x0040, 0x2670, + 0x78ec, 0xa084, 0x0003, 0x0040, 0x2670, 0xa184, 0x0007, 0x0079, + 0x20b9, 0x2026, 0x202c, 0x2020, 0x2d3b, 0x2d63, 0x2d63, 0x20c1, + 0x2d5b, 0x1078, 0x1b81, 0xa282, 0x0005, 0x0050, 0x20c9, 0x1078, + 0x1b81, 0x2300, 0x0079, 0x20cc, 0x20cf, 0x22d1, 0x22dd, 0x2200, + 0x0079, 0x20d2, 0x20d7, 0x20d9, 0x20ec, 0x20d7, 0x22b6, 0x1078, + 0x1b81, 0x789b, 0x0018, 0x78a8, 0xa084, 0x00ff, 0xa082, 0x0020, + 0x0048, 0x2b75, 0xa08a, 0x0004, 0x00c8, 0x2b75, 0x0079, 0x20e8, + 0x2b75, 0x2b75, 0x2b75, 0x2b17, 0x789b, 0x0018, 0x79a8, 0xa184, + 0x0080, 0x0040, 0x2101, 0xa184, 0x0018, 0x0040, 0x20fd, 0x0078, + 0x2b75, 0x7000, 0xa005, 0x00c0, 0x20f7, 0x2011, 0x0003, 0x0078, + 0x275d, 0xa184, 0x00ff, 0xa08a, 0x0010, 0x00c8, 0x2b75, 0x0079, + 0x2109, 0x211b, 0x2119, 0x2131, 0x2133, 0x21c5, 0x2b75, 0x2b75, + 0x21c7, 0x2b75, 0x2b75, 0x22b2, 0x22b2, 0x2b75, 0x2b75, 0x2b75, + 0x22b4, 0x1078, 0x1b81, 0xa684, 0x1000, 0x0040, 0x2128, 0x2001, + 0x0300, 0x8000, 0x8000, 0x783a, 0x781b, 0x009a, 0x0078, 0x1bd3, + 0x6814, 0xa084, 0x8000, 0x0040, 0x212f, 0x6817, 0x0003, 0x0078, + 0x2d3b, 0x1078, 0x1b81, 0x691c, 0x691e, 0xa684, 0x1800, 0x00c0, + 0x214d, 0x681c, 0xa084, 0x0001, 0x00c0, 0x2155, 0x6814, 0xa086, + 0x0008, 0x00c0, 0x2145, 0x6817, 0x0000, 0xa684, 0x0400, 0x0040, + 0x21c1, 0x781b, 0x0058, 0x0078, 0x1bd3, 0xa684, 0x1000, 0x0040, + 0x2155, 0x781b, 0x0058, 0x0078, 0x1bd3, 0xa684, 0x0060, 0x0040, + 0x21bd, 0xa684, 0x0800, 0x0040, 0x21bd, 0xa684, 0x8000, 0x00c0, + 0x2163, 0x0078, 0x217d, 0xa6b4, 0x7fff, 0x7e5a, 0x6eb2, 0x789b, + 0x0074, 0x7aac, 0x79ac, 0x78ac, 0x801b, 0x00c8, 0x2170, 0x8000, + 0xa084, 0x003f, 0xa108, 0xa291, 0x0000, 0x6b94, 0x2100, 0xa302, + 0x68ae, 0x6b90, 0x2200, 0xa303, 0x68aa, 0xa684, 0x4000, 0x0040, + 0x2185, 0xa6b4, 0xbfff, 0x7e5a, 0x6eb2, 0x7000, 0xa086, 0x0003, + 0x00c0, 0x2192, 0x1078, 0x2f3d, 0x1078, 0x30e2, 0x781b, 0x0067, + 0x0078, 0x1bd3, 0xa006, 0x1078, 0x3197, 0x6aac, 0x69a8, 0x6c94, + 0x6b90, 0x2200, 0xa105, 0x0040, 0x21a1, 0x2200, 0xa422, 0x2100, + 0xa31b, 0x7cd2, 0x7bd6, 0x2300, 0xa405, 0x00c0, 0x21af, 0xa6b5, + 0x4000, 0x7e5a, 0x6eb2, 0x781b, 0x0067, 0x0078, 0x1bd3, 0x781b, + 0x0067, 0x2200, 0xa115, 0x00c0, 0x21b9, 0x1078, 0x30f3, 0x0078, + 0x1bd3, 0x1078, 0x3120, 0x0078, 0x1bd3, 0x781b, 0x006a, 0x0078, + 0x1bd3, 0x781b, 0x0058, 0x0078, 0x1bd3, 0x1078, 0x1b81, 0x0078, + 0x2224, 0x691c, 0xa184, 0x0100, 0x0040, 0x21df, 0xa18c, 0xfeff, + 0x691e, 0x0c7e, 0x7048, 0x2060, 0x6000, 0xa084, 0xefff, 0x6002, + 0x6004, 0xa084, 0xfff5, 0x6006, 0x0c7f, 0x0078, 0x2213, 0xa184, + 0x0200, 0x0040, 0x2213, 0xa18c, 0xfdff, 0x691e, 0x0c7e, 0x7048, + 0x2060, 0x6000, 0xa084, 0xdfff, 0x6002, 0x6004, 0xa084, 0xffef, + 0x6006, 0x2008, 0x2c48, 0x0c7f, 0xa184, 0x0008, 0x0040, 0x2213, + 0x1078, 0x2ca8, 0x1078, 0x29f1, 0x88ff, 0x0040, 0x2213, 0x789b, + 0x0060, 0x2800, 0x78aa, 0x7e58, 0xa6b5, 0x0004, 0x7e5a, 0xa684, + 0x0400, 0x00c0, 0x220f, 0x781b, 0x0055, 0x0078, 0x1bd3, 0x781b, + 0x0069, 0x0078, 0x1bd3, 0x7e58, 0xa684, 0x0400, 0x00c0, 0x221c, + 0x781b, 0x0058, 0x0078, 0x1bd3, 0x781b, 0x006a, 0x0078, 0x1bd3, + 0x0078, 0x2b7b, 0x0078, 0x2b7b, 0x2019, 0x0000, 0x7990, 0xa18c, + 0x0007, 0x0040, 0x2222, 0x789b, 0x0010, 0x78a8, 0xa094, 0x00ff, + 0xa286, 0x0001, 0x00c0, 0x2247, 0x2300, 0x7ca8, 0xa400, 0x2018, + 0xa102, 0x0040, 0x223f, 0x0048, 0x223f, 0x0078, 0x2241, 0x0078, + 0x21c9, 0x24a8, 0x7aa8, 0x00f0, 0x2241, 0x0078, 0x222d, 0xa284, + 0x00f0, 0xa086, 0x0020, 0x00c0, 0x22a3, 0x8318, 0x8318, 0x2300, + 0xa102, 0x0040, 0x2257, 0x0048, 0x2257, 0x0078, 0x22a0, 0xa286, + 0x0023, 0x0040, 0x2222, 0x6818, 0xa084, 0xfff1, 0x681a, 0x7e58, + 0xa684, 0xfff1, 0xa085, 0x0010, 0x2030, 0x7e5a, 0x6008, 0xa085, + 0x0010, 0x600a, 0x0c7e, 0x7048, 0x2060, 0x6004, 0x2008, 0x2c48, + 0x0c7f, 0xa184, 0x0010, 0x0040, 0x227b, 0x1078, 0x2ca8, 0x1078, + 0x2ad7, 0x0078, 0x228a, 0x0c7e, 0x7048, 0x2060, 0x6004, 0x2008, + 0x2c48, 0x0c7f, 0xa184, 0x0008, 0x0040, 0x2213, 0x1078, 0x2ca8, + 0x1078, 0x29f1, 0x88ff, 0x0040, 0x2213, 0x789b, 0x0060, 0x2800, + 0x78aa, 0xa6b5, 0x0004, 0x7e5a, 0xa684, 0x0400, 0x00c0, 0x229c, + 0x781b, 0x0055, 0x0078, 0x1bd3, 0x781b, 0x0069, 0x0078, 0x1bd3, + 0x7aa8, 0x0078, 0x222d, 0x8318, 0x2300, 0xa102, 0x0040, 0x22ac, + 0x0048, 0x22ac, 0x0078, 0x222d, 0xa284, 0x0080, 0x00c0, 0x2b81, + 0x0078, 0x2b7b, 0x0078, 0x2b81, 0x0078, 0x2b75, 0x789b, 0x0018, + 0x78a8, 0xa084, 0x00ff, 0xa08e, 0x0001, 0x0040, 0x22c1, 0x1078, + 0x1b81, 0x7aa8, 0xa294, 0x00ff, 0x78a8, 0xa084, 0x00ff, 0xa08a, + 0x0004, 0x00c8, 0x2b75, 0x0079, 0x22cd, 0x2b75, 0x2944, 0x2b75, + 0x2a72, 0xa282, 0x0000, 0x00c0, 0x22d7, 0x1078, 0x1b81, 0x1078, + 0x2b94, 0x781b, 0x0069, 0x0078, 0x1bd3, 0xa282, 0x0003, 0x00c0, + 0x22e3, 0x1078, 0x1b81, 0x1078, 0x2ba4, 0x781b, 0x0069, 0x0078, + 0x1bd3, 0xa282, 0x0004, 0x0050, 0x22ef, 0x1078, 0x1b81, 0x2300, + 0x0079, 0x22f2, 0x22f5, 0x23d2, 0x2403, 0xa286, 0x0003, 0x0040, + 0x22fb, 0x1078, 0x1b81, 0x2001, 0x0000, 0x703a, 0x7000, 0xa084, + 0x0007, 0x0079, 0x2303, 0x230b, 0x230d, 0x230d, 0x2513, 0x253b, + 0x24dd, 0x230b, 0x230b, 0x1078, 0x1b81, 0xa684, 0x1000, 0x00c0, + 0x2315, 0x1078, 0x2eca, 0x0040, 0x23ac, 0x7868, 0xa08c, 0x00ff, + 0x0040, 0x235d, 0xa186, 0x0008, 0x00c0, 0x232c, 0x1078, 0x28fa, + 0x6008, 0xa084, 0xffef, 0x600a, 0x1078, 0x28ad, 0x0040, 0x235d, + 0x1078, 0x2eca, 0x0078, 0x2344, 0xa186, 0x0028, 0x00c0, 0x235d, + 0x1078, 0x2eca, 0x6008, 0xa084, 0xffef, 0x600a, 0x6018, 0xa005, + 0x0040, 0x2344, 0x8001, 0x601a, 0xa005, 0x0040, 0x2344, 0x8001, + 0xa005, 0x0040, 0x2344, 0x601e, 0x681c, 0xa084, 0x0001, 0x0040, + 0x1bdb, 0x681c, 0xa084, 0xfffe, 0x681e, 0x7054, 0x0c7e, 0x2060, + 0x6800, 0x6002, 0x0c7f, 0x6004, 0x6802, 0xa005, 0x2d00, 0x00c0, + 0x235a, 0x6002, 0x6006, 0x0078, 0x1bdb, 0x017e, 0x1078, 0x2428, + 0x017f, 0xa684, 0xdf00, 0x681a, 0x6827, 0x0000, 0x6f10, 0x81ff, + 0x0040, 0x23ac, 0xa186, 0x0002, 0x00c0, 0x23a4, 0xa684, 0x0800, + 0x00c0, 0x237a, 0xa684, 0x0060, 0x0040, 0x237a, 0x78d8, 0x7adc, + 0x682e, 0x6a2a, 0x8717, 0xa294, 0x000f, 0x8213, 0x8213, 0x8213, + 0xa290, 0x3600, 0xa290, 0x0000, 0x221c, 0xa384, 0x0100, 0x00c0, + 0x238b, 0x0078, 0x2391, 0x8210, 0x2204, 0xa085, 0x0018, 0x2012, + 0x8211, 0xa384, 0x0400, 0x0040, 0x239e, 0x689c, 0xa084, 0x0100, + 0x00c0, 0x239e, 0x1078, 0x249c, 0x0078, 0x1bdb, 0x6008, 0xa085, + 0x0002, 0x600a, 0x0078, 0x23ac, 0xa186, 0x0018, 0x0040, 0x23ac, + 0xa186, 0x0014, 0x0040, 0x1bdb, 0x6912, 0x6814, 0xa084, 0x8000, + 0x0040, 0x23b4, 0x7038, 0x6816, 0xa68c, 0xdf00, 0x691a, 0x1078, + 0x28eb, 0x1078, 0x28fa, 0x00c0, 0x23c1, 0x6008, 0xa084, 0xffef, + 0x600a, 0x681c, 0xa084, 0x0001, 0x00c0, 0x23ca, 0x1078, 0x28e4, + 0x0078, 0x23ce, 0x7054, 0x2060, 0x6800, 0x6002, 0x1078, 0x17e7, + 0x0078, 0x1bdb, 0xa282, 0x0004, 0x0048, 0x23d8, 0x1078, 0x1b81, + 0x2200, 0x0079, 0x23db, 0x23df, 0x23e1, 0x23ee, 0x23e1, 0x1078, + 0x1b81, 0x7000, 0xa086, 0x0005, 0x0040, 0x23ea, 0x1078, 0x2b94, + 0x781b, 0x0069, 0x781b, 0x006a, 0x0078, 0x1bd3, 0x7890, 0x8007, + 0x8001, 0xa084, 0x0007, 0xa080, 0x0018, 0x789a, 0x79a8, 0xa18c, + 0x00ff, 0xa186, 0x0003, 0x0040, 0x23ff, 0x0078, 0x2b75, 0x781b, + 0x006a, 0x0078, 0x1bd3, 0x681c, 0xa085, 0x0004, 0x681e, 0x82ff, + 0x00c0, 0x240e, 0x1078, 0x2b94, 0x0078, 0x2415, 0x8211, 0x0040, + 0x2413, 0x1078, 0x1b81, 0x1078, 0x2ba4, 0x781b, 0x0069, 0x0078, + 0x1bd3, 0x1078, 0x2d82, 0x7830, 0xa084, 0x00c0, 0x00c0, 0x2425, + 0x0018, 0x2425, 0x791a, 0xa006, 0x007c, 0xa085, 0x0001, 0x007c, + 0xa684, 0x0060, 0x00c0, 0x2432, 0x682f, 0x0000, 0x682b, 0x0000, + 0x0078, 0x249b, 0xa684, 0x0800, 0x00c0, 0x2441, 0x68b0, 0xa084, + 0x4800, 0xa635, 0xa684, 0x0800, 0x00c0, 0x2441, 0x1078, 0x2eca, + 0x007c, 0xa684, 0x0020, 0x0040, 0x246d, 0x78d0, 0x8003, 0x00c8, + 0x244f, 0xa006, 0x1078, 0x3197, 0x78d4, 0x1078, 0x31fc, 0xa684, + 0x4000, 0x0040, 0x2459, 0x682f, 0x0000, 0x682b, 0x0000, 0x0078, + 0x243e, 0x68b0, 0xa084, 0x4800, 0xa635, 0xa684, 0x4000, 0x00c0, + 0x2453, 0x7038, 0xa005, 0x00c0, 0x2467, 0x703b, 0x0007, 0x79d8, + 0x7adc, 0x692e, 0x6a2a, 0x0078, 0x243e, 0xa684, 0x4000, 0x0040, + 0x2477, 0x682f, 0x0000, 0x682b, 0x0000, 0x0078, 0x243e, 0x68b0, + 0xa084, 0x4800, 0xa635, 0xa684, 0x4000, 0x00c0, 0x2471, 0x7038, + 0xa005, 0x00c0, 0x2485, 0x703b, 0x0007, 0x79d8, 0x7adc, 0x78d0, + 0x80f3, 0x00c8, 0x248c, 0x8000, 0xa084, 0x003f, 0xa108, 0xa291, + 0x0000, 0x692e, 0x6a2a, 0x2100, 0xa205, 0x00c0, 0x2499, 0x0078, + 0x243e, 0x1078, 0x3197, 0x007c, 0xa384, 0x0200, 0x0040, 0x24a4, + 0x6008, 0xa085, 0x0002, 0x600a, 0x6817, 0x0006, 0x6a28, 0x692c, + 0x6a3a, 0x693e, 0x682b, 0x0300, 0x682f, 0x0000, 0x6833, 0x2000, + 0x6893, 0x0000, 0x6897, 0x0020, 0x7000, 0x0079, 0x24b7, 0x24bf, + 0x24c1, 0x24ca, 0x24bf, 0x24bf, 0x24bf, 0x24bf, 0x24bf, 0x1078, + 0x1b81, 0x681c, 0xa084, 0x0001, 0x00c0, 0x24ca, 0x1078, 0x28e4, + 0x0078, 0x24d0, 0x7054, 0x2c50, 0x2060, 0x6800, 0x6002, 0x2a60, + 0x2021, 0x3557, 0x2404, 0xa005, 0x0040, 0x24d9, 0x2020, 0x0078, + 0x24d2, 0x2d22, 0x206b, 0x0000, 0x007c, 0x77b4, 0x1078, 0x2bb1, + 0xa7bc, 0x0f00, 0x1078, 0x2cac, 0x6018, 0xa005, 0x0040, 0x250c, + 0x0d7e, 0x2001, 0x3f90, 0x2068, 0x0d7f, 0x2021, 0x3f80, 0x2009, + 0x0004, 0x2011, 0x0010, 0x1078, 0x1fc8, 0x0040, 0x250c, 0x157e, + 0x20a9, 0x0000, 0x2021, 0x3e80, 0x047e, 0x2009, 0x0004, 0x2011, + 0x0010, 0x1078, 0x1fc8, 0x047f, 0x0040, 0x250b, 0x8420, 0x0070, + 0x250b, 0x0078, 0x24fc, 0x157f, 0x8738, 0xa784, 0x0007, 0x00c0, + 0x24e2, 0x0078, 0x1bdb, 0x1078, 0x28eb, 0x1078, 0x28fa, 0x6827, + 0x0000, 0x789b, 0x000e, 0x6f10, 0x6813, 0x0002, 0x1078, 0x31cd, + 0xa684, 0x0800, 0x0040, 0x2528, 0x6918, 0xa18d, 0x2000, 0x691a, + 0x6814, 0xa084, 0x8000, 0x0040, 0x252f, 0x6817, 0x0000, 0x2021, + 0x3557, 0x6800, 0x2022, 0x6a38, 0x693c, 0x6a2a, 0x692e, 0x1078, + 0x17e7, 0x0078, 0x1bdb, 0x1078, 0x2428, 0x6827, 0x0000, 0x789b, + 0x000e, 0x6f10, 0x1078, 0x2d87, 0xa08c, 0x00ff, 0x6912, 0x6814, + 0xa084, 0x8000, 0x0040, 0x254e, 0x7038, 0x6816, 0xa68c, 0xdf00, + 0x691a, 0x70a3, 0x0000, 0x0078, 0x1bdb, 0xa006, 0x1078, 0x2eca, + 0x6813, 0x0000, 0x6817, 0x0001, 0xa68c, 0xdf00, 0x691a, 0x6827, + 0x0000, 0x7000, 0x0079, 0x2564, 0x256c, 0x256e, 0x256e, 0x2570, + 0x2570, 0x2570, 0x256c, 0x256c, 0x1078, 0x1b81, 0x1078, 0x28fa, + 0x6008, 0xa084, 0xffef, 0x600a, 0x0078, 0x28c5, 0x2300, 0x0079, + 0x2579, 0x257c, 0x257e, 0x25bc, 0x1078, 0x1b81, 0x7000, 0x0079, + 0x2581, 0x2589, 0x258b, 0x258b, 0x2596, 0x258b, 0x259d, 0x2589, + 0x2589, 0x1078, 0x1b81, 0xa684, 0x2000, 0x00c0, 0x2596, 0xa6b5, + 0x2000, 0x7e5a, 0x1078, 0x30f3, 0x0078, 0x2d3b, 0x6814, 0xa084, + 0x8000, 0x0040, 0x259d, 0x6817, 0x0007, 0x2009, 0x3518, 0x210c, + 0xa186, 0x0000, 0x0040, 0x25b2, 0xa186, 0x0001, 0x0040, 0x25b6, + 0x2009, 0x352b, 0x200b, 0x000b, 0x70a3, 0x0001, 0x781b, 0x0046, + 0x0078, 0x1bd3, 0x781b, 0x00dd, 0x0078, 0x1bd3, 0x2009, 0x352b, + 0x200b, 0x000a, 0x0078, 0x1bd3, 0x1078, 0x1b81, 0x2300, 0x0079, + 0x25c1, 0x25c4, 0x25c6, 0x25e9, 0x1078, 0x1b81, 0x7000, 0x0079, + 0x25c9, 0x25d1, 0x25d3, 0x25d3, 0x25de, 0x25d3, 0x25e5, 0x25d1, + 0x25d1, 0x1078, 0x1b81, 0xa684, 0x2000, 0x00c0, 0x25de, 0xa6b5, + 0x2000, 0x7e5a, 0x1078, 0x30f3, 0x0078, 0x2d3b, 0x6814, 0xa084, + 0x8000, 0x0040, 0x25e5, 0x6817, 0x0007, 0x781b, 0x00e4, 0x0078, + 0x1bd3, 0x681c, 0xa085, 0x0004, 0x681e, 0xa6b5, 0x0800, 0x1078, + 0x2b94, 0x781b, 0x0069, 0x0078, 0x1bd3, 0x2300, 0x0079, 0x25f8, + 0x25fb, 0x25fd, 0x25ff, 0x1078, 0x1b81, 0x1078, 0x1b81, 0xa684, + 0x0400, 0x00c0, 0x261e, 0x782b, 0x3009, 0x789b, 0x0060, 0x78ab, + 0x0000, 0xa684, 0xfffb, 0x785a, 0x79e4, 0xa184, 0x0020, 0x0040, + 0x2616, 0x78ec, 0xa084, 0x0003, 0x00c0, 0x261a, 0x2001, 0x0014, + 0x0078, 0x22fd, 0xa184, 0x0007, 0x0079, 0x2656, 0x7a90, 0xa294, + 0x0007, 0x789b, 0x0060, 0x79a8, 0x81ff, 0x0040, 0x2654, 0x789b, + 0x0010, 0x7ba8, 0xa384, 0x0001, 0x00c0, 0x2645, 0x7ba8, 0x7ba8, + 0xa386, 0x0001, 0x00c0, 0x2638, 0x2009, 0xfff7, 0x0078, 0x263e, + 0xa386, 0x0003, 0x00c0, 0x2645, 0x2009, 0xffef, 0x0c7e, 0x7048, + 0x2060, 0x6004, 0xa104, 0x6006, 0x0c7f, 0x789b, 0x0060, 0x78ab, + 0x0000, 0xa684, 0xfffb, 0x785a, 0x782b, 0x3009, 0x691c, 0xa18c, + 0xfdff, 0xa18c, 0xfeff, 0x691e, 0x0078, 0x2d3b, 0x2026, 0x202c, + 0x2660, 0x2668, 0x265e, 0x265e, 0x265e, 0x2d3b, 0x1078, 0x1b81, + 0x691c, 0xa18c, 0xfdff, 0xa18c, 0xfeff, 0x691e, 0x0078, 0x2d43, + 0x691c, 0xa18c, 0xfdff, 0xa18c, 0xfeff, 0x691e, 0x0078, 0x2d3b, + 0x79e4, 0xa184, 0x0030, 0x0040, 0x267a, 0x78ec, 0xa084, 0x0003, + 0x00c0, 0x2682, 0x6814, 0xa085, 0x8000, 0x6816, 0x2001, 0x0014, + 0x0078, 0x22fd, 0xa184, 0x0007, 0x0079, 0x2686, 0x2d3b, 0x2d3b, + 0x268e, 0x2d3b, 0x2d63, 0x2d63, 0x2d3b, 0x2d3b, 0xa684, 0x0400, + 0x00c0, 0x26bf, 0x681c, 0xa084, 0x0001, 0x0040, 0x2d43, 0xa68c, + 0x2060, 0xa18c, 0xfffb, 0x795a, 0x69b2, 0x789b, 0x0060, 0x78ab, + 0x0000, 0x789b, 0x0061, 0x6814, 0xa085, 0x8000, 0x6816, 0x78aa, + 0x157e, 0x137e, 0x147e, 0x20a1, 0x012c, 0x789b, 0x0000, 0x8000, + 0x80ac, 0xad80, 0x000a, 0x2098, 0x53a6, 0x147f, 0x137f, 0x157f, + 0x6810, 0x8007, 0x789b, 0x007e, 0x78aa, 0x0078, 0x2d43, 0x6814, + 0xa084, 0x8000, 0x0040, 0x26c6, 0x6817, 0x0008, 0x781b, 0x00d8, + 0x0078, 0x1bd3, 0x2300, 0x0079, 0x26cd, 0x26d2, 0x274d, 0x26d0, + 0x1078, 0x1b81, 0x7000, 0xa084, 0x0007, 0x0079, 0x26d7, 0x26df, + 0x26e1, 0x26fd, 0x26df, 0x26df, 0x24dd, 0x26df, 0x26df, 0x1078, + 0x1b81, 0x691c, 0xa18d, 0x0001, 0x691e, 0x6800, 0x6006, 0xa005, + 0x00c0, 0x26eb, 0x6002, 0x6818, 0xa084, 0x000e, 0x0040, 0x26f7, + 0x7014, 0x68b6, 0x712c, 0xa188, 0x3e80, 0x0078, 0x26f9, 0x2009, + 0x3f80, 0x2104, 0x6802, 0x2d0a, 0x7156, 0x6eb2, 0xa684, 0x0060, + 0x0040, 0x274b, 0xa684, 0x0800, 0x00c0, 0x270f, 0xa684, 0x7fff, + 0x68b2, 0x6890, 0x6894, 0x1078, 0x2eca, 0x0078, 0x274b, 0xa684, + 0x0020, 0x0040, 0x2721, 0xa006, 0x1078, 0x3197, 0x78d0, 0x8003, + 0x00c8, 0x271d, 0x78d4, 0x1078, 0x31fc, 0x79d8, 0x7adc, 0x0078, + 0x2725, 0x1078, 0x2cb9, 0x1078, 0x3197, 0xa684, 0x8000, 0x0040, + 0x274b, 0xa684, 0x7fff, 0x68b2, 0x789b, 0x0074, 0x1078, 0x2d87, + 0x2010, 0x1078, 0x2d87, 0x2008, 0xa684, 0x0020, 0x00c0, 0x2743, + 0x1078, 0x2d87, 0x801b, 0x00c8, 0x273e, 0x8000, 0xa084, 0x003f, + 0xa108, 0xa291, 0x0000, 0x6b94, 0x2100, 0xa302, 0x68ae, 0x6b90, + 0x2200, 0xa303, 0x68aa, 0x0078, 0x1bdb, 0x0078, 0x2b81, 0x7033, + 0x0000, 0xa282, 0x0005, 0x0050, 0x2757, 0x1078, 0x1b81, 0x2300, + 0x0079, 0x275a, 0x275d, 0x2767, 0x278a, 0x2200, 0x0079, 0x2760, + 0x2765, 0x2b81, 0x2765, 0x27b3, 0x2804, 0x1078, 0x1b81, 0x7000, + 0xa086, 0x0001, 0x00c0, 0x2774, 0x1078, 0x28fa, 0x1078, 0x2eca, + 0x7034, 0x600a, 0x0078, 0x2779, 0x7000, 0xa086, 0x0003, 0x0040, + 0x276e, 0x7003, 0x0005, 0x2001, 0x3f90, 0x2068, 0x703e, 0x7032, + 0x2200, 0x0079, 0x2783, 0x2b81, 0x2788, 0x27b3, 0x2788, 0x2b81, + 0x1078, 0x1b81, 0x7000, 0xa086, 0x0001, 0x00c0, 0x2797, 0x1078, + 0x28fa, 0x1078, 0x2eca, 0x7034, 0x600a, 0x0078, 0x279c, 0x7000, + 0xa086, 0x0003, 0x0040, 0x2791, 0x7003, 0x0005, 0x2001, 0x3f90, + 0x2068, 0x703e, 0x7032, 0x2200, 0x0079, 0x27a6, 0x27ad, 0x27ab, + 0x27ad, 0x27ab, 0x27ad, 0x1078, 0x1b81, 0x1078, 0x2ba4, 0x781b, + 0x0069, 0x0078, 0x1bd3, 0x7000, 0xa086, 0x0001, 0x00c0, 0x27c0, + 0x1078, 0x28fa, 0x1078, 0x2eca, 0x7034, 0x600a, 0x0078, 0x27c5, + 0x7000, 0xa086, 0x0003, 0x0040, 0x27ba, 0x7003, 0x0002, 0x7a80, + 0xa294, 0x0f00, 0x789b, 0x0018, 0x7ca8, 0xa484, 0x0007, 0xa215, + 0x2069, 0x3f80, 0x2d04, 0x2d08, 0x7156, 0x2068, 0xa005, 0x0040, + 0x27e0, 0x6810, 0xa206, 0x0040, 0x27f9, 0x6800, 0x0078, 0x27d3, + 0x7003, 0x0005, 0x2001, 0x3f90, 0x2068, 0x703e, 0x7032, 0x157e, + 0x20a9, 0x002f, 0x2003, 0x0000, 0x8000, 0x0070, 0x27f1, 0x0078, + 0x27ea, 0x157f, 0x6a12, 0x68b3, 0x0700, 0x681f, 0x0800, 0x6823, + 0x0003, 0x6eb0, 0x7e5a, 0x681c, 0xa084, 0x0c00, 0x0040, 0x285a, + 0x1078, 0x2b9c, 0x0078, 0x285a, 0x7000, 0xa086, 0x0001, 0x00c0, + 0x2811, 0x1078, 0x28fa, 0x1078, 0x2eca, 0x7034, 0x600a, 0x0078, + 0x2816, 0x7000, 0xa086, 0x0003, 0x0040, 0x280b, 0x7003, 0x0002, + 0x7a80, 0xa294, 0x0f00, 0x789b, 0x0018, 0x7ca8, 0xa484, 0x0007, + 0xa215, 0x79a8, 0x79a8, 0xa18c, 0x00ff, 0xa1e8, 0x3e80, 0x2d04, + 0x2d08, 0x7156, 0x2068, 0xa005, 0x0040, 0x2835, 0x6810, 0xa206, + 0x0040, 0x284e, 0x6800, 0x0078, 0x2828, 0x7003, 0x0005, 0x2001, + 0x3f90, 0x2068, 0x703e, 0x7032, 0x157e, 0x20a9, 0x002f, 0x2003, + 0x0000, 0x8000, 0x0070, 0x2846, 0x0078, 0x283f, 0x157f, 0x6a12, + 0x68b3, 0x0700, 0x681f, 0x0800, 0x6823, 0x0003, 0x6eb0, 0x7e5a, + 0x681c, 0xa084, 0x0c00, 0x0040, 0x285a, 0x1078, 0x2b98, 0x7e58, + 0x0078, 0x285a, 0x027e, 0x8207, 0xa084, 0x000f, 0x8003, 0x8003, + 0x8003, 0xa080, 0x3600, 0x2060, 0x704a, 0x6000, 0x704e, 0x6004, + 0x7052, 0xa684, 0x0060, 0x0040, 0x2891, 0x6b94, 0x6c90, 0x69a8, + 0x68ac, 0xa105, 0x00c0, 0x287f, 0x7bd2, 0x7bda, 0x7cd6, 0x7cde, + 0xa6b4, 0xb7ff, 0x7e5a, 0x1078, 0x30f3, 0x0078, 0x2891, 0x68ac, + 0xa31a, 0x2100, 0xa423, 0x2400, 0xa305, 0x0040, 0x2891, 0x7bd2, + 0x7bda, 0x7cd6, 0x7cde, 0x68ac, 0xa6b4, 0xbfff, 0x7e5a, 0x1078, + 0x3120, 0x077f, 0x1078, 0x2cac, 0x2009, 0x006a, 0xa684, 0x0008, + 0x0040, 0x289c, 0x2009, 0x0069, 0xa6b5, 0x2000, 0x7e5a, 0x791a, + 0x2d00, 0x703e, 0x8207, 0xa084, 0x000f, 0x8003, 0x8003, 0x8003, + 0xa080, 0x3600, 0x2048, 0x0078, 0x1bd3, 0x6020, 0xa005, 0x0040, + 0x28b9, 0x8001, 0x6022, 0x6008, 0xa085, 0x0008, 0x600a, 0x7010, + 0x6026, 0x007c, 0xa006, 0x1078, 0x2eca, 0x6813, 0x0000, 0x6817, + 0x0001, 0x681f, 0x0040, 0x681b, 0x0100, 0x7000, 0xa084, 0x0007, + 0x0079, 0x28ca, 0x28d2, 0x28d4, 0x28d4, 0x28e0, 0x28dc, 0x28d2, + 0x28d2, 0x28d2, 0x1078, 0x1b81, 0x1078, 0x28eb, 0x1078, 0x28e4, + 0x1078, 0x17e7, 0x0078, 0x1bdb, 0x70a3, 0x0000, 0x0078, 0x1bdb, + 0x6817, 0x0000, 0x0078, 0x2513, 0x6800, 0xa005, 0x00c0, 0x28e9, + 0x6002, 0x6006, 0x007c, 0x6010, 0xa005, 0x0040, 0x28f4, 0x8001, + 0x00d0, 0x28f4, 0x1078, 0x1b81, 0x6012, 0x6008, 0xa084, 0xffef, + 0x600a, 0x007c, 0x6018, 0xa005, 0x0040, 0x2900, 0x8001, 0x601a, + 0x007c, 0x1078, 0x2d82, 0x6817, 0x0018, 0x0078, 0x2931, 0x1078, + 0x2d82, 0x6817, 0x0019, 0x0078, 0x2931, 0x1078, 0x2d82, 0x6817, + 0x001a, 0x0078, 0x2931, 0x77b4, 0x1078, 0x2cac, 0x71b8, 0xa18c, + 0x00ff, 0xa1e8, 0x3e80, 0x2d04, 0x2d08, 0x2068, 0xa005, 0x00c0, + 0x2923, 0x0078, 0x1bdb, 0x6810, 0x72b4, 0xa206, 0x0040, 0x292b, + 0x6800, 0x0078, 0x291c, 0x6800, 0x200a, 0x6817, 0x0005, 0x70bf, + 0x0000, 0x1078, 0x28eb, 0x681c, 0xa084, 0x0001, 0x00c0, 0x293a, + 0x1078, 0x28e4, 0x1078, 0x28fa, 0x681b, 0x0000, 0x681f, 0x0020, + 0x1078, 0x17e7, 0x0078, 0x1bdb, 0xa282, 0x0003, 0x00c0, 0x2b75, + 0x7da8, 0xa5ac, 0x00ff, 0x7ea8, 0xa6b4, 0x00ff, 0x691c, 0xa18d, + 0x0080, 0x691e, 0xa184, 0x0100, 0x0040, 0x29a4, 0xa18c, 0xfeff, + 0x691e, 0xa6b4, 0x00ff, 0x0040, 0x298e, 0xa682, 0x000f, 0x0048, + 0x2965, 0x0040, 0x2965, 0x2031, 0x000f, 0x852b, 0x852b, 0x1078, + 0x2c2f, 0x0040, 0x296f, 0x1078, 0x2a3e, 0x0078, 0x2997, 0x1078, + 0x2bea, 0x0c7e, 0x2960, 0x6004, 0xa084, 0xfff5, 0x6006, 0x1078, + 0x2a62, 0x0c7f, 0x691c, 0xa18d, 0x0100, 0x691e, 0x7e58, 0xa6b5, + 0x0004, 0x7e5a, 0xa684, 0x0400, 0x00c0, 0x298a, 0x781b, 0x0055, + 0x0078, 0x1bd3, 0x781b, 0x0069, 0x0078, 0x1bd3, 0x0c7e, 0x2960, + 0x6004, 0xa084, 0xfff5, 0x6006, 0x1078, 0x2a62, 0x0c7f, 0x7e58, + 0xa684, 0x0400, 0x00c0, 0x29a0, 0x781b, 0x0058, 0x0078, 0x1bd3, + 0x781b, 0x006a, 0x0078, 0x1bd3, 0x0c7e, 0x7048, 0x2060, 0x6100, + 0xa18c, 0x1000, 0x0040, 0x29e4, 0x6208, 0x8217, 0xa294, 0x00ff, + 0xa282, 0x000f, 0x0048, 0x29b8, 0x0040, 0x29b8, 0x2011, 0x000f, + 0x2600, 0xa202, 0x00c8, 0x29bd, 0x2230, 0x6208, 0xa294, 0x00ff, + 0x7018, 0xa086, 0x0028, 0x00c0, 0x29cd, 0xa282, 0x0019, 0x00c8, + 0x29d3, 0x2011, 0x0019, 0x0078, 0x29d3, 0xa282, 0x000c, 0x00c8, + 0x29d3, 0x2011, 0x000c, 0x2200, 0xa502, 0x00c8, 0x29d8, 0x2228, + 0x1078, 0x2bee, 0x852b, 0x852b, 0x1078, 0x2c2f, 0x0040, 0x29e4, + 0x1078, 0x2a3e, 0x0078, 0x29e8, 0x1078, 0x2bea, 0x1078, 0x2a62, + 0x7858, 0xa085, 0x0004, 0x785a, 0x0c7f, 0x781b, 0x0069, 0x0078, + 0x1bd3, 0x0c7e, 0x2960, 0x6000, 0xa084, 0x1000, 0x00c0, 0x2a0c, + 0x6010, 0xa084, 0x000f, 0x00c0, 0x2a06, 0xa18c, 0x0002, 0x00c0, + 0x2a06, 0xa18c, 0xfff5, 0x6106, 0x0c7f, 0x007c, 0x2011, 0x0032, + 0x2019, 0x0000, 0x0078, 0x2a2e, 0x6208, 0xa294, 0x00ff, 0x7018, + 0xa086, 0x0028, 0x00c0, 0x2a1c, 0xa282, 0x0019, 0x00c8, 0x2a22, + 0x2011, 0x0019, 0x0078, 0x2a22, 0xa282, 0x000c, 0x00c8, 0x2a22, + 0x2011, 0x000c, 0x6308, 0x831f, 0xa39c, 0x00ff, 0xa382, 0x000f, + 0x0048, 0x2a2e, 0x0040, 0x2a2e, 0x2019, 0x000f, 0x78ab, 0x0001, + 0x78ab, 0x0003, 0x78ab, 0x0001, 0x7aaa, 0x7baa, 0xa8c0, 0x0005, + 0x681c, 0xa085, 0x0100, 0x681e, 0x0c7f, 0x007c, 0x0c7e, 0x7148, + 0x2160, 0x2008, 0xa084, 0xfff0, 0xa635, 0x7e86, 0x6018, 0x789a, + 0x7eae, 0x6612, 0x78a4, 0xa084, 0xfff8, 0xa18c, 0x0007, 0xa105, + 0x78a6, 0x6016, 0x788a, 0xa6b4, 0x000f, 0x8637, 0x8204, 0x8004, + 0xa084, 0x00ff, 0xa605, 0x600e, 0x6004, 0xa084, 0xfff5, 0x6006, + 0x0c7f, 0x007c, 0x0c7e, 0x7048, 0x2060, 0x6018, 0x789a, 0x78a4, + 0xa084, 0xfff0, 0x78a6, 0x6012, 0x7884, 0xa084, 0xfff0, 0x7886, + 0x0c7f, 0x007c, 0xa282, 0x0002, 0x00c0, 0x2b75, 0x7aa8, 0x691c, + 0xa18d, 0x0080, 0x691e, 0xa184, 0x0200, 0x0040, 0x2ab7, 0xa18c, + 0xfdff, 0x691e, 0xa294, 0x00ff, 0xa282, 0x0002, 0x00c8, 0x2b75, + 0x1078, 0x2afe, 0x1078, 0x2a62, 0xa980, 0x0001, 0x200c, 0x1078, + 0x2ca8, 0x1078, 0x29f1, 0x88ff, 0x0040, 0x2aaa, 0x789b, 0x0060, + 0x2800, 0x78aa, 0x7e58, 0xa6b5, 0x0004, 0x7e5a, 0xa684, 0x0400, + 0x00c0, 0x2aa6, 0x781b, 0x0055, 0x0078, 0x1bd3, 0x781b, 0x0069, + 0x0078, 0x1bd3, 0x7e58, 0xa684, 0x0400, 0x00c0, 0x2ab3, 0x781b, + 0x0058, 0x0078, 0x1bd3, 0x781b, 0x006a, 0x0078, 0x1bd3, 0xa282, + 0x0002, 0x00c8, 0x2abf, 0xa284, 0x0001, 0x0040, 0x2ac9, 0x7148, + 0xa188, 0x0000, 0x210c, 0xa18c, 0x2000, 0x00c0, 0x2ac9, 0x2011, + 0x0000, 0x1078, 0x2bdc, 0x1078, 0x2afe, 0x1078, 0x2a62, 0x7858, + 0xa085, 0x0004, 0x785a, 0x781b, 0x0069, 0x0078, 0x1bd3, 0x0c7e, + 0x027e, 0x2960, 0x6000, 0x2011, 0x0001, 0xa084, 0x2000, 0x00c0, + 0x2aee, 0x6014, 0xa084, 0x0040, 0x00c0, 0x2aec, 0xa18c, 0xffef, + 0x6106, 0xa006, 0x0078, 0x2afb, 0x2011, 0x0000, 0x78ab, 0x0001, + 0x78ab, 0x0002, 0x78ab, 0x0003, 0x7aaa, 0xa8c0, 0x0004, 0x681c, + 0xa085, 0x0200, 0x681e, 0x027f, 0x0c7f, 0x007c, 0x0c7e, 0x7048, + 0x2060, 0x82ff, 0x0040, 0x2b06, 0x2011, 0x0040, 0x6018, 0xa080, + 0x0002, 0x789a, 0x78a4, 0xa084, 0xffbf, 0xa205, 0x78a6, 0x6016, + 0x788a, 0x6004, 0xa084, 0xffef, 0x6006, 0x0c7f, 0x007c, 0x007e, + 0x7000, 0xa086, 0x0003, 0x0040, 0x2b20, 0x007f, 0x0078, 0x2b23, + 0x007f, 0x0078, 0x2b71, 0xa684, 0x0020, 0x0040, 0x2b71, 0x7888, + 0xa084, 0x0040, 0x0040, 0x2b71, 0x78a8, 0x8001, 0x0040, 0x2b30, + 0x7bb8, 0xa384, 0x003f, 0x831b, 0x00c8, 0x2b37, 0x8000, 0xa005, + 0x0040, 0x2b58, 0x831b, 0x00c8, 0x2b40, 0x8001, 0x0040, 0x2b6d, + 0xa006, 0x1078, 0x3197, 0x78b4, 0x1078, 0x31fc, 0x0078, 0x2b71, + 0xa684, 0x4000, 0x0040, 0x2b58, 0x78b8, 0x801b, 0x00c8, 0x2b51, + 0x8000, 0xa084, 0x003f, 0x00c0, 0x2b6d, 0xa6b4, 0xbfff, 0x7e5a, + 0x79d8, 0x7adc, 0x2001, 0x0001, 0xa108, 0x00c8, 0x2b61, 0xa291, + 0x0000, 0x79d2, 0x79da, 0x7ad6, 0x7ade, 0x1078, 0x3197, 0x781b, + 0x0067, 0x1078, 0x3061, 0x0078, 0x1bd3, 0x781b, 0x0067, 0x0078, + 0x1bd3, 0x781b, 0x006a, 0x0078, 0x1bd3, 0x1078, 0x2ba8, 0x781b, + 0x0069, 0x0078, 0x1bd3, 0x1078, 0x2b94, 0x781b, 0x0069, 0x0078, + 0x1bd3, 0x6823, 0x0002, 0x1078, 0x2b9c, 0x691c, 0xa18d, 0x0020, + 0x691e, 0x6814, 0xa084, 0x8000, 0x0040, 0x2b90, 0x6817, 0x0005, + 0x781b, 0x0069, 0x0078, 0x1bd3, 0x2001, 0x0005, 0x0078, 0x2baa, + 0x2001, 0x000c, 0x0078, 0x2baa, 0x2001, 0x0006, 0x0078, 0x2baa, + 0x2001, 0x000d, 0x0078, 0x2baa, 0x2001, 0x0009, 0x0078, 0x2baa, + 0x2001, 0x0007, 0x789b, 0x007f, 0x78aa, 0xa6b5, 0x0008, 0x7e5a, + 0x007c, 0x077e, 0x873f, 0xa7bc, 0x000f, 0x873b, 0x873b, 0x8703, + 0xa0e0, 0x3600, 0xa7b8, 0x0020, 0x7f9a, 0x79a4, 0xa184, 0x000f, + 0x0040, 0x2bca, 0xa184, 0xfff0, 0x78a6, 0x6012, 0x6004, 0xa085, + 0x0008, 0x6006, 0x8738, 0x8738, 0x7f9a, 0x79a4, 0xa184, 0x0040, + 0x0040, 0x2bda, 0xa184, 0xffbf, 0x78a6, 0x6016, 0x6004, 0xa085, + 0x0010, 0x6006, 0x077f, 0x007c, 0x789b, 0x0010, 0x78ab, 0x0001, + 0x78ab, 0x0002, 0x78ab, 0x0003, 0x7aaa, 0x789b, 0x0060, 0x78ab, + 0x0004, 0x007c, 0x2031, 0x0000, 0x2029, 0x0032, 0x789b, 0x0010, + 0x78ab, 0x0001, 0x78ab, 0x0003, 0x78ab, 0x0001, 0x7daa, 0x7eaa, + 0x789b, 0x0060, 0x78ab, 0x0005, 0x007c, 0x157e, 0x8007, 0xa084, + 0x00ff, 0x8003, 0x8003, 0xa080, 0x0020, 0x789a, 0x79a4, 0xa18c, + 0xfff0, 0x2001, 0x3546, 0x2004, 0xa082, 0x0028, 0x0040, 0x2c18, + 0x2021, 0x2c8f, 0x2019, 0x0014, 0x20a9, 0x000c, 0x0078, 0x2c1e, + 0x2021, 0x2c9b, 0x2019, 0x0019, 0x20a9, 0x000d, 0x2011, 0x0064, + 0x2404, 0xa084, 0xfff0, 0xa106, 0x0040, 0x2c2d, 0x8420, 0x2300, + 0xa210, 0x0070, 0x2c2d, 0x0078, 0x2c20, 0x157f, 0x007c, 0x157e, + 0x2011, 0x3546, 0x2214, 0xa282, 0x0032, 0x0048, 0x2c43, 0x0040, + 0x2c47, 0x2021, 0x2c81, 0x2019, 0x0011, 0x20a9, 0x000e, 0x2011, + 0x0032, 0x0078, 0x2c57, 0xa282, 0x0028, 0x0040, 0x2c4f, 0x2021, + 0x2c8f, 0x2019, 0x0014, 0x20a9, 0x000c, 0x0078, 0x2c55, 0x2021, + 0x2c9b, 0x2019, 0x0019, 0x20a9, 0x000d, 0x2011, 0x0064, 0x2200, + 0xa502, 0x0040, 0x2c67, 0x0048, 0x2c67, 0x8420, 0x2300, 0xa210, + 0x0070, 0x2c64, 0x0078, 0x2c57, 0x157f, 0xa006, 0x007c, 0x157f, + 0xa582, 0x0064, 0x00c8, 0x2c70, 0x7808, 0xa085, 0x0070, 0x780a, + 0x78ec, 0xa084, 0x0300, 0x0040, 0x2c7e, 0x2404, 0xa09e, 0x1201, + 0x00c0, 0x2c7e, 0x2001, 0x2101, 0x0078, 0x2c7f, 0x2404, 0xa005, + 0x007c, 0x1201, 0x3002, 0x3202, 0x4203, 0x4403, 0x5404, 0x5604, + 0x6605, 0x6805, 0x7806, 0x7a06, 0x0a07, 0x0c07, 0x0e07, 0x3202, + 0x4202, 0x5202, 0x6202, 0x7202, 0x6605, 0x7605, 0x7805, 0x7a05, + 0x7c05, 0x7e05, 0x7f05, 0x2202, 0x3202, 0x4202, 0x5202, 0x5404, + 0x6404, 0x7404, 0x7604, 0x7804, 0x7a04, 0x7c04, 0x7e04, 0x7f04, + 0x789b, 0x0010, 0xa046, 0x007c, 0xa784, 0x0f00, 0x800c, 0xa784, + 0x0007, 0x8003, 0x8003, 0x8003, 0x8003, 0xa105, 0xa0e0, 0x3680, + 0x007c, 0x79d8, 0x7adc, 0x78d0, 0x801b, 0x00c8, 0x2cc0, 0x8000, + 0xa084, 0x003f, 0xa108, 0xa291, 0x0000, 0x007c, 0x0f7e, 0x2079, + 0x0100, 0x2009, 0x3540, 0x2091, 0x8000, 0x2104, 0x0079, 0x2cd0, + 0x2d02, 0x2cda, 0x2cda, 0x2cda, 0x2cda, 0x2cda, 0x2cd8, 0x2cd8, + 0x1078, 0x1b81, 0x784b, 0x0004, 0x7848, 0xa084, 0x0004, 0x00c0, + 0x2cdc, 0x784b, 0x0008, 0x7848, 0xa084, 0x0008, 0x00c0, 0x2ce3, + 0x68b0, 0xa085, 0x4000, 0x68b2, 0x7858, 0xa085, 0x4000, 0x785a, + 0x7830, 0xa084, 0x0080, 0x00c0, 0x2d02, 0x0018, 0x2d02, 0x6818, + 0xa084, 0x0020, 0x00c0, 0x2d00, 0x781b, 0x00dd, 0x0078, 0x2d02, + 0x781b, 0x00e4, 0x2091, 0x8001, 0x0f7f, 0x007c, 0x0c7e, 0x6810, + 0x8007, 0xa084, 0x000f, 0x8003, 0x8003, 0x8003, 0xa0e0, 0x3600, + 0x6004, 0xa084, 0x000a, 0x00c0, 0x2d39, 0x6108, 0xa194, 0xff00, + 0x0040, 0x2d39, 0xa18c, 0x00ff, 0x2001, 0x0019, 0xa106, 0x0040, + 0x2d28, 0x2001, 0x0032, 0xa106, 0x0040, 0x2d2c, 0x0078, 0x2d30, + 0x2009, 0x0020, 0x0078, 0x2d32, 0x2009, 0x003f, 0x0078, 0x2d32, + 0x2011, 0x0000, 0x2100, 0xa205, 0x600a, 0x6004, 0xa085, 0x0002, + 0x6006, 0x0c7f, 0x007c, 0x781b, 0x006a, 0x0078, 0x1bd3, 0x781b, + 0x0069, 0x0078, 0x1bd3, 0x781b, 0x0058, 0x0078, 0x1bd3, 0x781b, + 0x0055, 0x0078, 0x1bd3, 0x781b, 0x00dd, 0x0078, 0x1bd3, 0x781b, + 0x00dc, 0x0078, 0x1bd3, 0x781b, 0x00e4, 0x0078, 0x1bd3, 0x781b, + 0x00e3, 0x0078, 0x1bd3, 0x781b, 0x009e, 0x0078, 0x1bd3, 0x781b, + 0x009d, 0x0078, 0x1bd3, 0x70a3, 0x0001, 0x781b, 0x0046, 0x0078, + 0x1bd3, 0x007e, 0x7830, 0xa084, 0x00c0, 0x00c0, 0x2d80, 0x7808, + 0xa084, 0xfffd, 0x780a, 0x0005, 0x0005, 0x0005, 0x0005, 0x78ec, + 0xa084, 0x0021, 0x0040, 0x2d80, 0x7808, 0xa085, 0x0002, 0x780a, + 0x007f, 0x007c, 0x7808, 0xa085, 0x0002, 0x780a, 0x007c, 0x7830, + 0xa084, 0x0040, 0x00c0, 0x2d87, 0x0098, 0x2d90, 0x78ac, 0x007c, + 0x7808, 0xa084, 0xfffd, 0x780a, 0x0005, 0x0005, 0x0005, 0x0005, + 0x78ec, 0xa084, 0x0021, 0x0040, 0x2d9f, 0x0098, 0x2d9d, 0x78ac, + 0x007e, 0x7808, 0xa085, 0x0002, 0x780a, 0x007f, 0x007c, 0xa784, + 0x0070, 0x0040, 0x2dab, 0x6817, 0x0003, 0x7858, 0xa084, 0x3f00, + 0x681a, 0x682f, 0x0000, 0x682b, 0x0000, 0x784b, 0x0008, 0x78e4, + 0xa005, 0x00d0, 0x2018, 0xa084, 0x0020, 0x0040, 0x2018, 0x78ec, + 0xa084, 0x0003, 0x0040, 0x2018, 0x0018, 0x2018, 0x0078, 0x2b7b, + 0x0c7e, 0x6810, 0x8007, 0xa084, 0x000f, 0x8003, 0x8003, 0x8003, + 0xa080, 0x3600, 0x2060, 0x2048, 0x704a, 0x6000, 0x704e, 0x6004, + 0x7052, 0x0c7f, 0x007c, 0x0020, 0x0020, 0x0000, 0x0020, 0x0000, + 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, + 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, + 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, + 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0062, 0x0009, 0x0014, + 0x0014, 0x9847, 0x0014, 0x0014, 0x98f5, 0x98e7, 0x0014, 0x0014, + 0x0080, 0x00bf, 0x0100, 0x0402, 0x2008, 0xf880, 0xa20a, 0x0014, + 0x300b, 0xa20c, 0x0014, 0xa200, 0x8838, 0x817e, 0x842a, 0x84a0, + 0x3806, 0x8839, 0x28c2, 0x9cc3, 0xa805, 0x0864, 0xa83b, 0x3008, + 0x28c1, 0x9cc3, 0xa201, 0x300c, 0x2847, 0x8161, 0x846a, 0x8000, + 0x84a4, 0x1856, 0x883a, 0xa808, 0x28e2, 0x9ca0, 0xa8f3, 0x0864, + 0xa829, 0x300c, 0xa801, 0x3008, 0x28e1, 0x9ca0, 0x280d, 0xa204, + 0x64c0, 0x67a0, 0x6fc0, 0x1814, 0x883b, 0x7023, 0x8576, 0x8677, + 0xa80f, 0x786e, 0x883e, 0xa80c, 0x282b, 0xa205, 0x64a0, 0x67a0, + 0x6fc0, 0x1814, 0x883b, 0x7023, 0x8576, 0x8677, 0xa801, 0x883e, + 0x2069, 0x28c1, 0x9cc3, 0x2044, 0x2103, 0x20a2, 0x2081, 0xa8dc, + 0xa207, 0x0014, 0xa203, 0x8000, 0x84a8, 0x85a4, 0x1872, 0x849a, + 0x883c, 0x1fe2, 0xf601, 0xa208, 0x856e, 0x866f, 0x0704, 0x3008, + 0x9ca0, 0x0014, 0xa202, 0x8000, 0x85a4, 0x3009, 0x84a8, 0x19e2, + 0xf848, 0x8174, 0x86eb, 0x85eb, 0x872e, 0x87a9, 0x883f, 0x08e6, + 0xa8f1, 0xf861, 0xa8e8, 0xf801, 0x0014, 0xf881, 0x0016, 0x85b2, + 0x80f0, 0x9532, 0xfaa2, 0x1de2, 0x0014, 0x8532, 0xf221, 0x0014, + 0x1de2, 0x84a8, 0xd6e0, 0x1fe6, 0x0014, 0xa206, 0x6865, 0x817f, + 0x842a, 0x1dc1, 0x8823, 0x0016, 0x6042, 0x8008, 0xa8fa, 0x8000, + 0x84a4, 0x8160, 0x842a, 0xf021, 0x3008, 0x84a8, 0x1dc6, 0x20d7, + 0x8822, 0x0016, 0x8000, 0x2848, 0x1011, 0xa8fc, 0x3008, 0x8000, + 0xa000, 0x2802, 0x1011, 0xa8fd, 0xa887, 0x3008, 0x283d, 0x1011, + 0xa8fd, 0xa209, 0x0017, 0x300c, 0x8000, 0x85a4, 0x1de2, 0xdac1, + 0x0014, 0x26e0, 0x873a, 0xfaa2, 0x19f2, 0x1fe2, 0x0014, 0xa20b, + 0x0014, 0xa20d, 0x817e, 0x842a, 0x84a0, 0x3806, 0x0210, 0x9ccd, + 0x0704, 0x0000, 0x127e, 0x2091, 0x2200, 0x2049, 0x2eca, 0x7000, + 0x7204, 0xa205, 0x720c, 0xa215, 0x7008, 0xa084, 0xfffd, 0xa205, + 0x0040, 0x2edc, 0x0078, 0x2ee1, 0x7003, 0x0000, 0x127f, 0x2000, + 0x007c, 0x7000, 0xa084, 0x0001, 0x00c0, 0x2f0f, 0x7108, 0x8104, + 0x00c8, 0x2eee, 0x1078, 0x2fab, 0x0078, 0x2ee6, 0x700c, 0xa08c, + 0x007f, 0x0040, 0x2f0f, 0x7004, 0x8004, 0x00c8, 0x2f06, 0x7014, + 0xa005, 0x00c0, 0x2f02, 0x7010, 0xa005, 0x0040, 0x2f06, 0xa102, + 0x00c8, 0x2ee6, 0x7007, 0x0010, 0x0078, 0x2f0f, 0x8aff, 0x0040, + 0x2f0f, 0x1078, 0x316e, 0x00c0, 0x2f09, 0x0040, 0x2ee6, 0x1078, + 0x2f59, 0x7003, 0x0000, 0x127f, 0x2000, 0x007c, 0x6424, 0x84ff, + 0x0040, 0x2f33, 0x2c70, 0x2039, 0x2f38, 0x2704, 0xae68, 0x680c, + 0xa630, 0x6808, 0xa529, 0x8421, 0x0040, 0x2f33, 0x8738, 0x2704, + 0xa005, 0x00c0, 0x2f1e, 0x7098, 0xa075, 0x0040, 0x2f33, 0x2039, + 0x2f35, 0x0078, 0x2f1d, 0x007c, 0x0000, 0x0004, 0x0008, 0x000c, + 0x0010, 0x0014, 0x0018, 0x001c, 0x0000, 0x127e, 0x2091, 0x2200, + 0x2079, 0x3500, 0x2071, 0x0010, 0x7007, 0x000a, 0x7007, 0x0002, + 0x7003, 0x0000, 0x2071, 0x0020, 0x7007, 0x000a, 0x7007, 0x0002, + 0x7003, 0x0000, 0x2049, 0x0000, 0x78b3, 0x0000, 0x127f, 0x2000, + 0x007c, 0x2049, 0x2f59, 0x7004, 0x8004, 0x00c8, 0x2f85, 0x7007, + 0x0012, 0x7108, 0x7008, 0xa106, 0x00c0, 0x2f61, 0xa184, 0x0030, + 0x0040, 0x2f6e, 0xa086, 0x0030, 0x00c0, 0x2f61, 0x7000, 0xa084, + 0x0001, 0x00c0, 0x2f85, 0x7008, 0xa084, 0x000c, 0x00c0, 0x2f83, + 0x710c, 0xa184, 0x0300, 0x00c0, 0x2f83, 0xa184, 0x007f, 0x00c0, + 0x2f59, 0x0078, 0x2f85, 0x6817, 0x0003, 0x7007, 0x0012, 0x7007, + 0x0008, 0x7004, 0xa084, 0x0008, 0x00c0, 0x2f89, 0x7007, 0x0012, + 0x7108, 0x8104, 0x0048, 0x2f8e, 0x78b3, 0x0000, 0x7003, 0x0000, + 0x2049, 0x0000, 0x007c, 0x107e, 0x007e, 0x127e, 0x157e, 0x2091, + 0x2200, 0x7108, 0x1078, 0x2fab, 0x157f, 0x127f, 0x2091, 0x8001, + 0x007f, 0x107f, 0x007c, 0x7204, 0x2118, 0x7108, 0x700c, 0xa084, + 0x0300, 0x00c0, 0x2fed, 0xa184, 0x000c, 0x00c0, 0x2fed, 0x8213, + 0x8213, 0x8213, 0x8213, 0xa284, 0x0100, 0xa10d, 0x810b, 0x810b, + 0x810f, 0xa184, 0x0007, 0x0079, 0x2fc5, 0x2fcf, 0x2fdf, 0x2fed, + 0x2fdf, 0x3001, 0x3001, 0x2fed, 0x2fff, 0x1078, 0x1b81, 0x7007, + 0x0002, 0x8aff, 0x00c0, 0x2fd8, 0x2049, 0x0000, 0x0078, 0x2fdc, + 0x1078, 0x316e, 0x00c0, 0x2fd8, 0x78b3, 0x0000, 0x007c, 0x7007, + 0x0002, 0x8aff, 0x00c0, 0x2fe6, 0x0078, 0x2fea, 0x1078, 0x316e, + 0x00c0, 0x2fe6, 0x78b3, 0x0000, 0x007c, 0x7007, 0x0002, 0x1078, + 0x2f59, 0x1078, 0x2cc6, 0x6814, 0xa084, 0x8000, 0x0040, 0x2ffa, + 0x6817, 0x0002, 0x007c, 0x1078, 0x1b81, 0x1078, 0x1b81, 0x1078, + 0x3053, 0x7210, 0x7114, 0x700c, 0xa09c, 0x007f, 0x2800, 0xa300, + 0xa211, 0xa189, 0x0000, 0x78b0, 0xa005, 0x0040, 0x3013, 0x78b3, + 0x0000, 0x0078, 0x3036, 0x1078, 0x3053, 0x2704, 0x2c58, 0xac60, + 0x630c, 0x2200, 0xa322, 0x6308, 0x2100, 0xa31b, 0x2400, 0xa305, + 0x0040, 0x302c, 0x00c8, 0x302c, 0x8412, 0x8210, 0x830a, 0xa189, + 0x0000, 0x2b60, 0x0078, 0x3013, 0x2b60, 0x8a07, 0xa7ba, 0x2f35, + 0xa73d, 0x2c00, 0x6882, 0x6f86, 0x6c8e, 0x6b8a, 0x7007, 0x0012, + 0x1078, 0x2f59, 0x007c, 0x8738, 0x2704, 0xa005, 0x00c0, 0x3047, + 0x6098, 0xa005, 0x0040, 0x3050, 0x2060, 0x2039, 0x2f35, 0x8a51, + 0x0040, 0x304f, 0x7008, 0xa084, 0x00c0, 0xa086, 0x00c0, 0x007c, + 0x2051, 0x0000, 0x007c, 0x8a50, 0x8739, 0x2704, 0xa004, 0x00c0, + 0x3060, 0x2039, 0x2f3b, 0x6000, 0xa064, 0x00c0, 0x3060, 0x2d60, + 0x007c, 0x127e, 0x0d7e, 0x2091, 0x2200, 0x0d7f, 0x6880, 0x2060, + 0x6884, 0x6b88, 0x6c8c, 0x8057, 0xaad4, 0x00ff, 0xa084, 0x00ff, + 0xa0b8, 0x2f35, 0x7e08, 0xa6b5, 0x000c, 0x6818, 0xa084, 0x0040, + 0x0040, 0x307c, 0xa6b5, 0x0001, 0x0f7e, 0x2079, 0x0100, 0x7858, + 0x0f7f, 0xa084, 0x0040, 0x0040, 0x308b, 0xa684, 0x0001, 0x00c0, + 0x308b, 0xa6b5, 0x0001, 0x7007, 0x0004, 0x7004, 0xa084, 0x0004, + 0x00c0, 0x308d, 0x7000, 0xa005, 0x0040, 0x3098, 0x1078, 0x1b81, + 0x2400, 0xa305, 0x00c0, 0x309e, 0x0078, 0x30db, 0x2c58, 0x2704, + 0xac60, 0x6004, 0xa400, 0x007e, 0x701a, 0x6000, 0xa301, 0x701e, + 0x2009, 0x04fd, 0x2104, 0xa086, 0x04fd, 0x007f, 0x00c0, 0x30cb, + 0xa084, 0x0001, 0x0040, 0x30cb, 0xa684, 0x0001, 0x00c0, 0x30cb, + 0x7013, 0x0001, 0x7017, 0x0000, 0x7602, 0x7007, 0x0001, 0x78b3, + 0x0001, 0xa4a0, 0x0001, 0xa399, 0x0000, 0x6004, 0xa400, 0x701a, + 0x6000, 0xa301, 0x701e, 0x620c, 0x2400, 0xa202, 0x7012, 0x6208, + 0x2300, 0xa203, 0x7016, 0x7602, 0x7007, 0x0001, 0x2b60, 0x1078, + 0x303b, 0x0078, 0x30dd, 0x1078, 0x316e, 0x00c0, 0x30db, 0x127f, + 0x2000, 0x007c, 0x127e, 0x0d7e, 0x2091, 0x2200, 0x0d7f, 0x7007, + 0x0004, 0x7004, 0xa084, 0x0004, 0x00c0, 0x30e9, 0x7003, 0x0008, + 0x127f, 0x2000, 0x007c, 0x127e, 0x0d7e, 0x2091, 0x2200, 0x0d7f, + 0x2049, 0x30f3, 0x7007, 0x0004, 0x7004, 0xa084, 0x0004, 0x00c0, + 0x30fc, 0x7000, 0xa005, 0x0040, 0x3107, 0x1078, 0x1b81, 0x7e08, + 0xa6b5, 0x000c, 0x6818, 0xa084, 0x0040, 0x0040, 0x3111, 0xa6b5, + 0x0001, 0x6824, 0xa005, 0x0040, 0x311d, 0x2050, 0x2039, 0x2f38, + 0x2d60, 0x1078, 0x316e, 0x00c0, 0x3119, 0x127f, 0x2000, 0x007c, + 0x127e, 0x007e, 0x017e, 0x0d7e, 0x2091, 0x2200, 0x0d7f, 0x037f, + 0x047f, 0x7e08, 0xa6b5, 0x000c, 0x6818, 0xa084, 0x0040, 0x0040, + 0x3133, 0xa6b5, 0x0001, 0x2049, 0x3120, 0x6824, 0xa055, 0x0040, + 0x316b, 0x2d70, 0x2e60, 0x2039, 0x2f38, 0x2704, 0xae68, 0x680c, + 0xa422, 0x6808, 0xa31b, 0x0048, 0x3158, 0x8a51, 0x00c0, 0x314a, + 0x1078, 0x1b81, 0x8738, 0x2704, 0xa005, 0x00c0, 0x313e, 0x7098, + 0xa075, 0x2060, 0x0040, 0x316b, 0x2039, 0x2f35, 0x0078, 0x313d, + 0x8422, 0x8420, 0x831a, 0xa399, 0x0000, 0x690c, 0x2400, 0xa122, + 0x6908, 0x2300, 0xa11b, 0x00c8, 0x3167, 0x1078, 0x1b81, 0x2071, + 0x0020, 0x0078, 0x308b, 0x127f, 0x2000, 0x007c, 0x7008, 0xa084, + 0x00c0, 0xa086, 0x00c0, 0x0040, 0x3196, 0x2704, 0xac08, 0x2104, + 0x701e, 0x8108, 0x2104, 0x701a, 0x8108, 0x2104, 0x7016, 0x8108, + 0x2104, 0x7012, 0x0f7e, 0x2079, 0x0100, 0x7858, 0x0f7f, 0xa084, + 0x0040, 0x0040, 0x3191, 0xa684, 0x0001, 0x00c0, 0x3191, 0xa6b5, + 0x0001, 0x7602, 0x7007, 0x0001, 0x1078, 0x303b, 0x007c, 0x127e, + 0x007e, 0x0d7e, 0x2091, 0x2200, 0x2049, 0x3197, 0x0d7f, 0x087f, + 0x7108, 0xa184, 0x00c0, 0x00c0, 0x31ad, 0x6824, 0xa005, 0x0040, + 0x31bd, 0x0078, 0x2ee1, 0x0078, 0x31bd, 0x7108, 0x8104, 0x00c8, + 0x31b5, 0x1078, 0x2fab, 0x0078, 0x31a0, 0x7007, 0x0010, 0x7108, + 0x8104, 0x00c8, 0x31b7, 0x1078, 0x2fab, 0x7008, 0xa086, 0x0002, + 0x00c0, 0x31a0, 0x7000, 0xa005, 0x00c0, 0x31a0, 0x7003, 0x0000, + 0x2049, 0x0000, 0x127f, 0x2000, 0x007c, 0x127e, 0x147e, 0x137e, + 0x157e, 0x0d7e, 0x2091, 0x2200, 0x0d7f, 0x2049, 0x31cd, 0xad80, + 0x0010, 0x20a0, 0x2099, 0x0031, 0x700c, 0xa084, 0x007f, 0x6826, + 0x7007, 0x0008, 0x7007, 0x0002, 0x7003, 0x0001, 0x0040, 0x31eb, + 0x8000, 0x80ac, 0x53a5, 0x7007, 0x0004, 0x7004, 0xa084, 0x0004, + 0x00c0, 0x31ed, 0x2049, 0x0000, 0x7003, 0x0000, 0x157f, 0x137f, + 0x147f, 0x127f, 0x2000, 0x007c, 0x127e, 0x007e, 0x0d7e, 0x2091, + 0x2200, 0x0d7f, 0x2049, 0x31fc, 0x6880, 0x2060, 0x6884, 0x6b88, + 0x6c8c, 0x8057, 0xaad4, 0x00ff, 0xa084, 0x00ff, 0xa0b8, 0x2f35, + 0x7e08, 0xa6b5, 0x0004, 0x7007, 0x0004, 0x7004, 0xa084, 0x0004, + 0x00c0, 0x3215, 0x2c58, 0x2704, 0xac60, 0x6004, 0xa400, 0x701a, + 0x6000, 0xa301, 0x701e, 0x7013, 0x0001, 0x7017, 0x0000, 0x7602, + 0x7007, 0x0001, 0x007f, 0x8007, 0x2009, 0x0031, 0x200a, 0x00a0, + 0x322f, 0x7108, 0x7007, 0x0002, 0x810c, 0x00c8, 0x322f, 0x810c, + 0x0048, 0x323c, 0x0078, 0x2fed, 0xa4a0, 0x0001, 0xa399, 0x0000, + 0x6b8a, 0x6c8e, 0x7007, 0x0004, 0x2049, 0x0000, 0x7003, 0x0000, + 0x127f, 0x2000, 0x007c, 0x20a9, 0x0010, 0xa006, 0x8004, 0x8086, + 0x818e, 0x00c8, 0x3254, 0xa200, 0x00f0, 0x324f, 0x8086, 0x818e, + 0x007c, 0x157e, 0x20a9, 0x0010, 0xa005, 0x0040, 0x327a, 0xa11a, + 0x00c8, 0x327a, 0x8213, 0x818d, 0x0048, 0x326d, 0xa11a, 0x00c8, + 0x326e, 0x00f0, 0x3262, 0x0078, 0x3272, 0xa11a, 0x2308, 0x8210, + 0x00f0, 0x3262, 0x007e, 0x3200, 0xa084, 0xf7ff, 0x2080, 0x007f, + 0x157f, 0x007c, 0x007e, 0x3200, 0xa085, 0x0800, 0x0078, 0x3276, + 0x00e0, 0x32c2, 0x2091, 0x6000, 0x7820, 0x8001, 0x7822, 0x00c0, + 0x32bc, 0x7824, 0x7822, 0x2091, 0x8000, 0x2069, 0x3540, 0x6800, + 0xa084, 0x0007, 0x0040, 0x32a4, 0xa086, 0x0002, 0x0040, 0x32a4, + 0x6830, 0xa00d, 0x0040, 0x32a4, 0x2104, 0xa005, 0x0040, 0x32a4, + 0x8001, 0x200a, 0x0040, 0x3372, 0x2061, 0x3680, 0x20a9, 0x0080, + 0x6034, 0xa005, 0x0040, 0x32b6, 0x8001, 0x6036, 0x00c0, 0x32b6, + 0x6010, 0xa005, 0x0040, 0x32b6, 0x1078, 0x1a23, 0xace0, 0x0010, + 0x0070, 0x32bc, 0x0078, 0x32a8, 0x1078, 0x32d7, 0x1078, 0x32c5, + 0x1078, 0x32fc, 0x2091, 0x8001, 0x007c, 0x783c, 0x8001, 0x783e, + 0x00c0, 0x32d6, 0x7840, 0x783e, 0x7848, 0xa005, 0x0040, 0x32d6, + 0x8001, 0x784a, 0x00c0, 0x32d6, 0x1078, 0x1a23, 0x007c, 0x7834, + 0x8001, 0x7836, 0x00c0, 0x32fb, 0x7838, 0x7836, 0x2091, 0x8000, + 0x7844, 0xa005, 0x00c0, 0x32e6, 0x2001, 0x0101, 0x8001, 0x7846, + 0xa080, 0x3e80, 0x2040, 0x2004, 0xa065, 0x0040, 0x32fb, 0x6020, + 0xa005, 0x0040, 0x32f7, 0x8001, 0x6022, 0x0040, 0x332b, 0x6000, + 0x2c40, 0x0078, 0x32ec, 0x007c, 0x7828, 0x8001, 0x782a, 0x00c0, + 0x332a, 0x782c, 0x782a, 0x7830, 0xa005, 0x00c0, 0x3309, 0x2001, + 0x0080, 0x8001, 0x7832, 0x8003, 0x8003, 0x8003, 0x8003, 0xa090, + 0x3680, 0xa298, 0x0002, 0x2304, 0xa084, 0x0008, 0x0040, 0x332a, + 0xa290, 0x0009, 0x2204, 0xa005, 0x0040, 0x3322, 0x8001, 0x2012, + 0x00c0, 0x332a, 0x2304, 0xa084, 0xfff7, 0xa085, 0x0080, 0x201a, + 0x1078, 0x1a23, 0x007c, 0x2069, 0x3540, 0x6800, 0xa005, 0x0040, + 0x3335, 0x683c, 0xac06, 0x0040, 0x3372, 0x6017, 0x0006, 0x60b0, + 0xa084, 0x3f00, 0x601a, 0x601c, 0xa084, 0x00ff, 0xa085, 0x0060, + 0x601e, 0x6000, 0x2042, 0x6710, 0x6fb6, 0x1078, 0x169c, 0x6818, + 0xa005, 0x0040, 0x334d, 0x8001, 0x681a, 0x6808, 0xa084, 0xffef, + 0x680a, 0x6810, 0x8001, 0x00d0, 0x3357, 0x1078, 0x1b81, 0x6812, + 0x602f, 0x0000, 0x602b, 0x0000, 0x2c68, 0x1078, 0x17e7, 0x2069, + 0x3540, 0x2001, 0x0006, 0x68a2, 0x7944, 0xa184, 0x0100, 0x00c0, + 0x336d, 0x69ba, 0x2001, 0x0004, 0x68a2, 0x1078, 0x1a1e, 0x2091, + 0x8001, 0x007c, 0x2009, 0x354f, 0x2164, 0x2069, 0x0100, 0x6017, + 0x0006, 0x6858, 0xa084, 0x3f00, 0x601a, 0x601c, 0xa084, 0x00ff, + 0xa085, 0x0048, 0x601e, 0x602f, 0x0000, 0x602b, 0x0000, 0x6830, + 0xa084, 0x0040, 0x0040, 0x33ac, 0x684b, 0x0004, 0x20a9, 0x0014, + 0x6848, 0xa084, 0x0004, 0x0040, 0x3399, 0x0070, 0x3399, 0x0078, + 0x3390, 0x684b, 0x0009, 0x20a9, 0x0014, 0x6848, 0xa084, 0x0001, + 0x0040, 0x33a6, 0x0070, 0x33a6, 0x0078, 0x339d, 0x20a9, 0x00fa, + 0x0070, 0x33ac, 0x0078, 0x33a8, 0x6808, 0xa084, 0xfffd, 0x680a, + 0x681b, 0x0046, 0x2009, 0x3568, 0x200b, 0x0007, 0x784c, 0x784a, + 0x2091, 0x8001, 0x007c, 0x2079, 0x3500, 0x1078, 0x3404, 0x1078, + 0x33cc, 0x1078, 0x33e1, 0x1078, 0x33f6, 0x7833, 0x0000, 0x7847, + 0x0000, 0x784b, 0x0000, 0x007c, 0x2019, 0x000a, 0x2011, 0x3546, + 0x2204, 0xa086, 0x0032, 0x0040, 0x33de, 0x2019, 0x000c, 0x2204, + 0xa086, 0x003c, 0x0040, 0x33de, 0x2019, 0x0008, 0x7b2a, 0x7b2e, + 0x007c, 0x2019, 0x0030, 0x2011, 0x3546, 0x2204, 0xa086, 0x0032, + 0x0040, 0x33f3, 0x2019, 0x0039, 0x2204, 0xa086, 0x003c, 0x0040, + 0x33f3, 0x2019, 0x0027, 0x7b36, 0x7b3a, 0x007c, 0x2019, 0x000d, + 0x2011, 0x3546, 0x2204, 0xa086, 0x003c, 0x0040, 0x3401, 0x2019, + 0x000a, 0x7b3e, 0x7b42, 0x007c, 0x2019, 0x2faf, 0x2011, 0x3546, + 0x2204, 0xa086, 0x0032, 0x0040, 0x3416, 0x2019, 0x3971, 0x2204, + 0xa086, 0x003c, 0x0040, 0x3416, 0x2019, 0x2626, 0x7b22, 0x7b26, + 0x007c, 0xda3e +}; + +unsigned short sbus_risc_code_length01 = 0x241a; diff -u --recursive --new-file v2.2.3/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c --- v2.2.3/linux/drivers/scsi/scsi.c Tue Jan 19 11:32:51 1999 +++ linux/drivers/scsi/scsi.c Thu Mar 18 15:59:16 1999 @@ -162,7 +162,11 @@ "Scanner ", "Optical Device ", "Medium Changer ", - "Communications " + "Communications ", + "Unknown ", + "Unknown ", + "Unknown ", + "Enclosure ", }; /* @@ -241,6 +245,7 @@ {"SONY","CD-ROM CDU-541","4.3d", BLIST_NOLUN}, {"SONY","CD-ROM CDU-55S","1.0i", BLIST_NOLUN}, {"SONY","CD-ROM CDU-561","1.7x", BLIST_NOLUN}, +{"SONY","CD-ROM CDU-8012","*", BLIST_NOLUN}, {"TANDBERG","TDC 3600","U07", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */ {"TEAC","CD-R55S","1.0H", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */ {"TEAC","CD-ROM","1.06", BLIST_NOLUN}, /* causes failed REQUEST SENSE on lun 1 @@ -276,12 +281,11 @@ {"EMULEX","MD21/S2 ESDI","*", BLIST_SINGLELUN}, {"CANON","IPUBJD","*", BLIST_SPARSELUN}, {"nCipher","Fastness Crypto","*", BLIST_FORCELUN}, +{"NEC","PD-1 ODX654P","*", BLIST_FORCELUN | BLIST_SINGLELUN}, {"MATSHITA","PD","*", BLIST_FORCELUN | BLIST_SINGLELUN}, {"YAMAHA","CDR100","1.00", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */ {"YAMAHA","CDR102","1.00", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */ {"iomega","jaz 1GB","J.86", BLIST_NOTQ | BLIST_NOLUN}, -{"IBM","DPES-","*", BLIST_NOTQ | BLIST_NOLUN}, -{"WDIGTL","WDE","*", BLIST_NOTQ | BLIST_NOLUN}, /* * Must be at end of list... */ @@ -759,6 +763,7 @@ case TYPE_PROCESSOR: case TYPE_SCANNER: case TYPE_MEDIUM_CHANGER: + case TYPE_ENCLOSURE: SDpnt->writeable = 1; break; case TYPE_WORM: @@ -2471,7 +2476,8 @@ } else if (SDpnt->type == TYPE_SCANNER || SDpnt->type == TYPE_PROCESSOR || - SDpnt->type == TYPE_MEDIUM_CHANGER) { + SDpnt->type == TYPE_MEDIUM_CHANGER || + SDpnt->type == TYPE_ENCLOSURE) { new_dma_sectors += (4096 >> 9) * SDpnt->queue_depth; } else { @@ -3293,7 +3299,7 @@ /* One bit per sector to indicate free/busy */ size = (dma_sectors / SECTORS_PER_PAGE)*sizeof(FreeSectorBitmap); - dma_malloc_freelist = (unsigned char *) scsi_init_malloc(size, GFP_ATOMIC); + dma_malloc_freelist = (FreeSectorBitmap *) scsi_init_malloc(size, GFP_ATOMIC); memset(dma_malloc_freelist, 0, size); /* One pointer per page for the page list */ diff -u --recursive --new-file v2.2.3/linux/drivers/scsi/scsi.h linux/drivers/scsi/scsi.h --- v2.2.3/linux/drivers/scsi/scsi.h Wed Jan 13 15:00:42 1999 +++ linux/drivers/scsi/scsi.h Sun Mar 21 14:19:03 1999 @@ -39,7 +39,7 @@ # define FALSE 0 #endif -#define MAX_SCSI_DEVICE_CODE 10 +#define MAX_SCSI_DEVICE_CODE 14 extern const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE]; #ifdef DEBUG diff -u --recursive --new-file v2.2.3/linux/drivers/sound/awacs_defs.h linux/drivers/sound/awacs_defs.h --- v2.2.3/linux/drivers/sound/awacs_defs.h Thu Aug 6 14:06:33 1998 +++ linux/drivers/sound/awacs_defs.h Wed Mar 10 21:48:46 1999 @@ -157,4 +157,68 @@ #define RATE_LOW 1 /* HIGH = 48kHz, etc; LOW = 44.1kHz, etc. */ + +/* Burgundy values */ + +#define MASK_ADDR_BURGUNDY_INPSEL21 (0x11 << 12) +#define MASK_ADDR_BURGUNDY_INPSEL3 (0x12 << 12) + +#define MASK_ADDR_BURGUNDY_GAINCH1 (0x13 << 12) +#define MASK_ADDR_BURGUNDY_GAINCH2 (0x14 << 12) +#define MASK_ADDR_BURGUNDY_GAINCH3 (0x15 << 12) +#define MASK_ADDR_BURGUNDY_GAINCH4 (0x16 << 12) + +#define MASK_ADDR_BURGUNDY_VOLCH1 (0x20 << 12) +#define MASK_ADDR_BURGUNDY_VOLCH2 (0x21 << 12) +#define MASK_ADDR_BURGUNDY_VOLCH3 (0x22 << 12) +#define MASK_ADDR_BURGUNDY_VOLCH4 (0x23 << 12) + +#define MASK_ADDR_BURGUNDY_OUTPUTSELECTS (0x2B << 12) +#define MASK_ADDR_BURGUNDY_OUTPUTENABLES (0x2F << 12) + +#define MASK_ADDR_BURGUNDY_MASTER_VOLUME (0x30 << 12) + +#define MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES (0x60 << 12) + +#define MASK_ADDR_BURGUNDY_ATTENSPEAKER (0x62 << 12) +#define MASK_ADDR_BURGUNDY_ATTENLINEOUT (0x63 << 12) +#define MASK_ADDR_BURGUNDY_ATTENHP (0x64 << 12) + +#define MASK_ADDR_BURGUNDY_VOLCD (MASK_ADDR_BURGUNDY_VOLCH1) +#define MASK_ADDR_BURGUNDY_VOLLINE (MASK_ADDR_BURGUNDY_VOLCH2) +#define MASK_ADDR_BURGUNDY_VOLMIC (MASK_ADDR_BURGUNDY_VOLCH3) +#define MASK_ADDR_BURGUNDY_VOLMODEM (MASK_ADDR_BURGUNDY_VOLCH4) + +#define MASK_ADDR_BURGUNDY_GAINCD (MASK_ADDR_BURGUNDY_GAINCH1) +#define MASK_ADDR_BURGUNDY_GAINLINE (MASK_ADDR_BURGUNDY_GAINCH2) +#define MASK_ADDR_BURGUNDY_GAINMIC (MASK_ADDR_BURGUNDY_GAINCH3) +#define MASK_ADDR_BURGUNDY_GAINMODEM (MASK_ADDR_BURGUNDY_VOLCH4) + + +/* These are all default values for the burgundy */ +#define DEF_BURGUNDY_INPSEL21 (0xAA) +#define DEF_BURGUNDY_INPSEL3 (0x0A) + +#define DEF_BURGUNDY_GAINCD (0x33) +#define DEF_BURGUNDY_GAINLINE (0x44) +#define DEF_BURGUNDY_GAINMIC (0x44) +#define DEF_BURGUNDY_GAINMODEM (0x06) + +/* Remember: lowest volume here is 0x9b */ +#define DEF_BURGUNDY_VOLCD (0xCCCCCCCC) +#define DEF_BURGUNDY_VOLLINE (0x00000000) +#define DEF_BURGUNDY_VOLMIC (0x00000000) +#define DEF_BURGUNDY_VOLMODEM (0xCCCCCCCC) + +#define DEF_BURGUNDY_OUTPUTSELECTS (0x010f010f) +#define DEF_BURGUNDY_OUTPUTENABLES (0x0A) + +#define DEF_BURGUNDY_MASTER_VOLUME (0xFFFFFFFF) + +#define DEF_BURGUNDY_MORE_OUTPUTENABLES (0x7E) + +#define DEF_BURGUNDY_ATTENSPEAKER (0x44) +#define DEF_BURGUNDY_ATTENLINEOUT (0xCC) +#define DEF_BURGUNDY_ATTENHP (0xCC) + #endif /* _AWACS_DEFS_H_ */ diff -u --recursive --new-file v2.2.3/linux/drivers/sound/dmasound.c linux/drivers/sound/dmasound.c --- v2.2.3/linux/drivers/sound/dmasound.c Thu Jan 7 15:11:37 1999 +++ linux/drivers/sound/dmasound.c Wed Mar 10 21:48:46 1999 @@ -173,9 +173,11 @@ static volatile struct dbdma_regs *awacs_txdma, *awacs_rxdma; static int awacs_rate_index; static int awacs_subframe; -static int awacs_revision; static int awacs_spkr_vol; +static int awacs_revision; +#define AWACS_BURGUNDY 100 /* fake revision # for burgundy */ + /* * Space for the DBDMA command blocks. */ @@ -184,6 +186,7 @@ /* * Cached values of AWACS registers (we can't read them). + * Except on the burgundy. XXX */ int awacs_reg[5]; @@ -240,6 +243,14 @@ static volatile struct dbdma_cmd *beep_dbdma_cmd; static void (*orig_mksound)(unsigned int, unsigned int); +/* Burgundy functions */ +static void awacs_burgundy_wcw(unsigned addr,unsigned newval); +static unsigned awacs_burgundy_rcw(unsigned addr); +static void awacs_burgundy_write_volume(unsigned address, int volume); +static int awacs_burgundy_read_volume(unsigned address); +static void awacs_burgundy_write_mvolume(unsigned address, int volume); +static int awacs_burgundy_read_mvolume(unsigned address); + #ifdef CONFIG_PMAC_PBOOK /* * Stuff for restoring after a sleep. @@ -3007,6 +3018,9 @@ if (beep_buf) kfree(beep_buf); kd_mksound = orig_mksound; +#ifdef CONFIG_PMAC_PBOOK + notifier_chain_unregister(&sleep_notifier_list, &awacs_sleep_notifier); +#endif } #endif /* MODULE */ @@ -3040,8 +3054,9 @@ * If we have a sample rate which is within catchRadius percent * of the requested value, we don't have to expand the samples. * Otherwise choose the next higher rate. + * N.B.: burgundy awacs (iMac and later) only works at 44100 Hz. */ - i = 8; + i = (awacs_revision >= AWACS_BURGUNDY)? 1: 8; do { tolerance = catchRadius * awacs_freqs[--i] / 100; } while (sound.soft.speed > awacs_freqs[i] + tolerance && i > 0); @@ -3053,7 +3068,9 @@ awacs_rate_index = i; PMacSilence(); - out_le32(&awacs->control, MASK_IEPC | MASK_IEE | (i << 8) | 0x11); + /* XXX disable error interrupt on burgundy for now */ + out_le32(&awacs->control, MASK_IEPC | (i << 8) | 0x11 + | (awacs_revision < AWACS_BURGUNDY? MASK_IEE: 0)); awacs_reg[1] = (awacs_reg[1] & ~MASK_SAMPLERATE) | (i << 3); awacs_write(awacs_reg[1] | MASK_ADDR1); out_le32(&awacs->byteswap, sound.hard.format != AFMT_S16_BE); @@ -3213,8 +3230,9 @@ /* do something when headphone is plugged/unplugged? */ } if (ctrl & MASK_CNTLERR) { - printk(KERN_ERR "AWACS: error, status = %x\n", - in_le32(&awacs->codec_stat)); + int err = (in_le32(&awacs->codec_stat) & MASK_ERRCODE) >> 16; + if (err != 0 && awacs_revision < AWACS_BURGUNDY) + printk(KERN_ERR "AWACS: error %x\n", err); } /* Writing 1s to the CNTLERR and PORTCHG bits clears them... */ out_le32(&awacs->control, ctrl); @@ -3223,6 +3241,8 @@ static void awacs_write(int val) { + if (awacs_revision >= AWACS_BURGUNDY) + return; while (in_le32(&awacs->codec_ctrl) & MASK_NEWECMD) ; /* XXX should have timeout */ out_le32(&awacs->codec_ctrl, val | (awacs_subframe << 22)); @@ -3247,7 +3267,8 @@ static void awacs_mksound(unsigned int hz, unsigned int ticks) { unsigned long flags; - int srate = awacs_freqs[BEEP_SPEED]; + int beep_speed = (awacs_revision < AWACS_BURGUNDY)? BEEP_SPEED: 0; + int srate = awacs_freqs[beep_speed]; int period, ncycles, nsamples; int i, j, f; short *p; @@ -3308,7 +3329,7 @@ out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16); out_le32(&awacs->control, (in_le32(&awacs->control) & ~0x1f00) - | (BEEP_SPEED << 8)); + | (beep_speed << 8)); out_le32(&awacs->byteswap, 0); out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd)); out_le32(&awacs_txdma->control, RUN | (RUN << 16)); @@ -3330,8 +3351,9 @@ PMacSilence(); break; case PBOOK_WAKE: - out_le32(&awacs->control, MASK_IEPC | MASK_IEE | - (awacs_rate_index << 8) | 0x11); + out_le32(&awacs->control, MASK_IEPC + | (awacs_rate_index << 8) | 0x11 + | (awacs_revision < AWACS_BURGUNDY? MASK_IEE: 0)); awacs_write(awacs_reg[0] | MASK_ADDR0); awacs_write(awacs_reg[1] | MASK_ADDR1); awacs_write(awacs_reg[2] | MASK_ADDR2); @@ -3342,6 +3364,220 @@ } #endif /* CONFIG_PMAC_PBOOK */ + +/* All the burgundy functions: */ + +/* Waits for busy flag to clear */ +inline static void +awacs_burgundy_busy_wait(void) +{ + while (in_le32(&awacs->codec_ctrl) & MASK_NEWECMD) + ; +} + +inline static void +awacs_burgundy_extend_wait(void) +{ + while (!(in_le32(&awacs->codec_stat) & MASK_EXTEND)) + ; + while (in_le32(&awacs->codec_stat) & MASK_EXTEND) + ; +} + +static void +awacs_burgundy_wcw(unsigned addr, unsigned val) +{ + out_le32(&awacs->codec_ctrl, addr + 0x200c00 + (val & 0xff)); + awacs_burgundy_busy_wait(); + out_le32(&awacs->codec_ctrl, addr + 0x200d00 +((val>>8) & 0xff)); + awacs_burgundy_busy_wait(); + out_le32(&awacs->codec_ctrl, addr + 0x200e00 +((val>>16) & 0xff)); + awacs_burgundy_busy_wait(); + out_le32(&awacs->codec_ctrl, addr + 0x200f00 +((val>>24) & 0xff)); + awacs_burgundy_busy_wait(); +} + +static unsigned +awacs_burgundy_rcw(unsigned addr) +{ + unsigned val = 0; + int flags; + + /* should have timeouts here */ + save_flags(flags); cli(); + + out_le32(&awacs->codec_ctrl, addr + 0x100000); + awacs_burgundy_busy_wait(); + awacs_burgundy_extend_wait(); + val += (in_le32(&awacs->codec_stat) >> 4) & 0xff; + + out_le32(&awacs->codec_ctrl, addr + 0x100100); + awacs_burgundy_busy_wait(); + awacs_burgundy_extend_wait(); + val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<8; + + out_le32(&awacs->codec_ctrl, addr + 0x100200); + awacs_burgundy_busy_wait(); + awacs_burgundy_extend_wait(); + val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<16; + + out_le32(&awacs->codec_ctrl, addr + 0x100300); + awacs_burgundy_busy_wait(); + awacs_burgundy_extend_wait(); + val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<24; + + restore_flags(flags); + + return val; +} + + +static void +awacs_burgundy_wcb(unsigned addr, unsigned val) +{ + out_le32(&awacs->codec_ctrl, addr + 0x300000 + (val & 0xff)); + awacs_burgundy_busy_wait(); +} + +static unsigned +awacs_burgundy_rcb(unsigned addr) +{ + unsigned val = 0; + int flags; + + /* should have timeouts here */ + save_flags(flags); cli(); + + out_le32(&awacs->codec_ctrl, addr + 0x100000); + awacs_burgundy_busy_wait(); + awacs_burgundy_extend_wait(); + val += (in_le32(&awacs->codec_stat) >> 4) & 0xff; + + restore_flags(flags); + + return val; +} + +static int +awacs_burgundy_check(void) +{ + /* Checks to see the chip is alive and kicking */ + int error = in_le32(&awacs->codec_ctrl) & MASK_ERRCODE; + + return error == 0xf0000; +} + +static int +awacs_burgundy_init(void) +{ + if (awacs_burgundy_check()) { + printk(KERN_WARNING "AWACS: disabled by MacOS :-(\n"); + return 1; + } + + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_OUTPUTENABLES, + DEF_BURGUNDY_OUTPUTENABLES); + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + DEF_BURGUNDY_MORE_OUTPUTENABLES); + awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_OUTPUTSELECTS, + DEF_BURGUNDY_OUTPUTSELECTS); + + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_INPSEL21, + DEF_BURGUNDY_INPSEL21); + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_INPSEL3, + DEF_BURGUNDY_INPSEL3); + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINCD, + DEF_BURGUNDY_GAINCD); + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINLINE, + DEF_BURGUNDY_GAINLINE); + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINMIC, + DEF_BURGUNDY_GAINMIC); + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINMODEM, + DEF_BURGUNDY_GAINMODEM); + + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER, + DEF_BURGUNDY_ATTENSPEAKER); + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENLINEOUT, + DEF_BURGUNDY_ATTENLINEOUT); + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENHP, + DEF_BURGUNDY_ATTENHP); + + awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_MASTER_VOLUME, + DEF_BURGUNDY_MASTER_VOLUME); + awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLCD, + DEF_BURGUNDY_VOLCD); + awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLLINE, + DEF_BURGUNDY_VOLLINE); + awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLMIC, + DEF_BURGUNDY_VOLMIC); + return 0; +} + +static void +awacs_burgundy_write_volume(unsigned address, int volume) +{ + int hardvolume,lvolume,rvolume; + + lvolume = (volume & 0xff) ? (volume & 0xff) + 155 : 0; + rvolume = ((volume >>8)&0xff) ? ((volume >> 8)&0xff ) + 155 : 0; + + hardvolume = lvolume + (rvolume << 16); + + awacs_burgundy_wcw(address, hardvolume); +} + +static int +awacs_burgundy_read_volume(unsigned address) +{ + int softvolume,wvolume; + + wvolume = awacs_burgundy_rcw(address); + + softvolume = (wvolume & 0xff) - 155; + softvolume += (((wvolume >> 16) & 0xff) - 155)<<8; + + return softvolume > 0 ? softvolume : 0; +} + + + + +static int +awacs_burgundy_read_mvolume(unsigned address) +{ + int lvolume,rvolume,wvolume; + + wvolume = awacs_burgundy_rcw(address); + + wvolume &= 0xffff; + + rvolume = (wvolume & 0xff) - 155; + lvolume = ((wvolume & 0xff00)>>8) - 155; + + return lvolume + (rvolume << 8); +} + + +static void +awacs_burgundy_write_mvolume(unsigned address, int volume) +{ + int lvolume,rvolume,hardvolume; + + lvolume = (volume &0xff) ? (volume & 0xff) + 155 :0; + rvolume = ((volume >>8) & 0xff) ? (volume >> 8) + 155 :0; + + hardvolume = lvolume + (rvolume << 8); + hardvolume += (hardvolume << 16); + + awacs_burgundy_wcw(address, hardvolume); +} + +/* End burgundy functions */ + + + + + /* Turn on sound output, needed on G3 desktop powermacs */ static void awacs_enable_amp(int spkr_vol) @@ -3715,117 +3951,244 @@ #ifdef CONFIG_PPC case DMASND_AWACS: - switch (cmd) { - case SOUND_MIXER_READ_DEVMASK: - data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER - | SOUND_MASK_LINE | SOUND_MASK_MIC - | SOUND_MASK_CD | SOUND_MASK_RECLEV - | SOUND_MASK_ALTPCM; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_RECMASK: - data = SOUND_MASK_LINE | SOUND_MASK_MIC - | SOUND_MASK_CD; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_RECSRC: - data = 0; - if (awacs_reg[0] & MASK_MUX_AUDIN) - data |= SOUND_MASK_LINE; - if (awacs_reg[0] & MASK_MUX_MIC) - data |= SOUND_MASK_MIC; - if (awacs_reg[0] & MASK_MUX_CD) - data |= SOUND_MASK_CD; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_RECSRC: - IOCTL_IN(arg, data); - data &= (SOUND_MASK_LINE - | SOUND_MASK_MIC | SOUND_MASK_CD); - awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC - | MASK_MUX_AUDIN); - if (data & SOUND_MASK_LINE) - awacs_reg[0] |= MASK_MUX_AUDIN; - if (data & SOUND_MASK_MIC) - awacs_reg[0] |= MASK_MUX_MIC; - if (data & SOUND_MASK_CD) - awacs_reg[0] |= MASK_MUX_CD; - awacs_write(awacs_reg[0] | MASK_ADDR0); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_STEREODEVS: - data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER - | SOUND_MASK_RECLEV; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_CAPS: - return IOCTL_OUT(arg, 0); - case SOUND_MIXER_READ_VOLUME: - data = (awacs_reg[1] & MASK_AMUTE)? 0: - awacs_get_volume(awacs_reg[2], 6); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_VOLUME: - IOCTL_IN(arg, data); - return IOCTL_OUT(arg, sound_set_volume(data)); - case SOUND_MIXER_READ_SPEAKER: - if (awacs_revision >= 3 && adb_hardware == ADB_VIACUDA) - data = awacs_spkr_vol; - else - data = (awacs_reg[1] & MASK_CMUTE)? 0: - awacs_get_volume(awacs_reg[4], 6); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_SPEAKER: - IOCTL_IN(arg, data); - if (awacs_revision >= 3 && adb_hardware == ADB_VIACUDA) - awacs_enable_amp(data); - else - data = awacs_volume_setter(data, 4, MASK_CMUTE, 6); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_ALTPCM: /* really bell volume */ - IOCTL_IN(arg, data); - beep_volume = data & 0xff; - /* fall through */ - case SOUND_MIXER_READ_ALTPCM: - return IOCTL_OUT(arg, beep_volume); - case SOUND_MIXER_WRITE_LINE: - IOCTL_IN(arg, data); - awacs_reg[0] &= ~MASK_MUX_AUDIN; - if ((data & 0xff) >= 50) - awacs_reg[0] |= MASK_MUX_AUDIN; - awacs_write(MASK_ADDR0 | awacs_reg[0]); - /* fall through */ - case SOUND_MIXER_READ_LINE: - data = (awacs_reg[0] & MASK_MUX_AUDIN)? 100: 0; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_MIC: - IOCTL_IN(arg, data); - data &= 0xff; - awacs_reg[0] &= ~(MASK_MUX_MIC | MASK_GAINLINE); - if (data >= 25) { - awacs_reg[0] |= MASK_MUX_MIC; - if (data >= 75) - awacs_reg[0] |= MASK_GAINLINE; + if (awacs_revision= 50) + awacs_reg[0] |= MASK_MUX_AUDIN; + awacs_write(MASK_ADDR0 | awacs_reg[0]); + /* fall through */ + case SOUND_MIXER_READ_LINE: + data = (awacs_reg[0] & MASK_MUX_AUDIN)? 100: 0; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_MIC: + IOCTL_IN(arg, data); + data &= 0xff; + awacs_reg[0] &= ~(MASK_MUX_MIC | MASK_GAINLINE); + if (data >= 25) { + awacs_reg[0] |= MASK_MUX_MIC; + if (data >= 75) + awacs_reg[0] |= MASK_GAINLINE; + } + awacs_write(MASK_ADDR0 | awacs_reg[0]); + /* fall through */ + case SOUND_MIXER_READ_MIC: + data = (awacs_reg[0] & MASK_MUX_MIC)? + (awacs_reg[0] & MASK_GAINLINE? 100: 50): 0; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_CD: + IOCTL_IN(arg, data); + awacs_reg[0] &= ~MASK_MUX_CD; + if ((data & 0xff) >= 50) + awacs_reg[0] |= MASK_MUX_CD; + awacs_write(MASK_ADDR0 | awacs_reg[0]); + /* fall through */ + case SOUND_MIXER_READ_CD: + data = (awacs_reg[0] & MASK_MUX_CD)? 100: 0; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_RECLEV: + IOCTL_IN(arg, data); + data = awacs_volume_setter(data, 0, 0, 4); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECLEV: + data = awacs_get_volume(awacs_reg[0], 4); + return IOCTL_OUT(arg, data); } - awacs_write(MASK_ADDR0 | awacs_reg[0]); - /* fall through */ - case SOUND_MIXER_READ_MIC: - data = (awacs_reg[0] & MASK_MUX_MIC)? - (awacs_reg[0] & MASK_GAINLINE? 100: 50): 0; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_CD: - IOCTL_IN(arg, data); - awacs_reg[0] &= ~MASK_MUX_CD; - if ((data & 0xff) >= 50) - awacs_reg[0] |= MASK_MUX_CD; - awacs_write(MASK_ADDR0 | awacs_reg[0]); - /* fall through */ - case SOUND_MIXER_READ_CD: - data = (awacs_reg[0] & MASK_MUX_CD)? 100: 0; - return IOCTL_OUT(arg, data); - case SOUND_MIXER_WRITE_RECLEV: - IOCTL_IN(arg, data); - data = awacs_volume_setter(data, 0, 0, 4); - return IOCTL_OUT(arg, data); - case SOUND_MIXER_READ_RECLEV: - data = awacs_get_volume(awacs_reg[0], 4); - return IOCTL_OUT(arg, data); + break; + } else { + /* We are, we are, we are... Burgundy or better */ + switch(cmd) { + case SOUND_MIXER_READ_DEVMASK: + data = SOUND_MASK_VOLUME | SOUND_MASK_CD | + SOUND_MASK_LINE | SOUND_MASK_MIC | + SOUND_MASK_SPEAKER | SOUND_MASK_ALTPCM; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECMASK: + data = SOUND_MASK_LINE | SOUND_MASK_MIC + | SOUND_MASK_CD; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECSRC: + data = 0; + if (awacs_reg[0] & MASK_MUX_AUDIN) + data |= SOUND_MASK_LINE; + if (awacs_reg[0] & MASK_MUX_MIC) + data |= SOUND_MASK_MIC; + if (awacs_reg[0] & MASK_MUX_CD) + data |= SOUND_MASK_CD; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_RECSRC: + IOCTL_IN(arg, data); + data &= (SOUND_MASK_LINE + | SOUND_MASK_MIC | SOUND_MASK_CD); + awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC + | MASK_MUX_AUDIN); + if (data & SOUND_MASK_LINE) + awacs_reg[0] |= MASK_MUX_AUDIN; + if (data & SOUND_MASK_MIC) + awacs_reg[0] |= MASK_MUX_MIC; + if (data & SOUND_MASK_CD) + awacs_reg[0] |= MASK_MUX_CD; + awacs_write(awacs_reg[0] | MASK_ADDR0); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_STEREODEVS: + data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER + | SOUND_MASK_RECLEV | SOUND_MASK_CD + | SOUND_MASK_LINE; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_CAPS: + return IOCTL_OUT(arg, 0); + case SOUND_MIXER_WRITE_VOLUME: + IOCTL_IN(arg, data); + awacs_burgundy_write_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME, data); + /* Fall through */ + case SOUND_MIXER_READ_VOLUME: + return IOCTL_OUT(arg, awacs_burgundy_read_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME)); + case SOUND_MIXER_WRITE_SPEAKER: + IOCTL_IN(arg, data); + + if (!(data & 0xff)) { + /* Mute the left speaker */ + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) & ~0x2); + } else { + /* Unmute the left speaker */ + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) | 0x2); + } + if (!(data & 0xff00)) { + /* Mute the right speaker */ + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) & ~0x4); + } else { + /* Unmute the right speaker */ + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) | 0x4); + } + + data = (((data&0xff)*16)/100 > 0xf ? 0xf : + (((data&0xff)*16)/100)) + + ((((data>>8)*16)/100 > 0xf ? 0xf : + ((((data>>8)*16)/100)))<<4); + + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER, ~data); + /* Fall through */ + case SOUND_MIXER_READ_SPEAKER: + data = awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER); + data = (((data & 0xf)*100)/16) + ((((data>>4)*100)/16)<<8); + return IOCTL_OUT(arg, ~data); + case SOUND_MIXER_WRITE_ALTPCM: /* really bell volume */ + IOCTL_IN(arg, data); + beep_volume = data & 0xff; + /* fall through */ + case SOUND_MIXER_READ_ALTPCM: + return IOCTL_OUT(arg, beep_volume); + case SOUND_MIXER_WRITE_LINE: + IOCTL_IN(arg, data); + awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLLINE, data); + + /* fall through */ + case SOUND_MIXER_READ_LINE: + data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLLINE); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_MIC: + IOCTL_IN(arg, data); + /* Mic is mono device */ + data = (data << 8) + (data << 24); + awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLMIC, data); + /* fall through */ + case SOUND_MIXER_READ_MIC: + data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLMIC); + data <<= 24; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_CD: + IOCTL_IN(arg, data); + awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLCD, data); + /* fall through */ + case SOUND_MIXER_READ_CD: + data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLCD); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_RECLEV: + IOCTL_IN(arg, data); + data = awacs_volume_setter(data, 0, 0, 4); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECLEV: + data = awacs_get_volume(awacs_reg[0], 4); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_OUTMASK: + break; + case SOUND_MIXER_OUTSRC: + break; + } + break; } - break; #endif } @@ -3946,9 +4309,7 @@ sq.front = sq.count = 0; sq.rear = -1; - sq.write_queue = sq.open_queue = sq.sync_queue = 0; sq.syncing = 0; - sq.playing = 0; #ifdef CONFIG_ATARI @@ -4129,21 +4490,21 @@ static int sq_release(struct inode *inode, struct file *file) { int rc = 0; - if (sq.busy) { + + if (sq.busy) rc = sq_fsync(file, file->f_dentry); - sq.busy = 0; - WAKE_UP(sq.open_queue); - /* Wake up a process waiting for the queue being released. - * Note: There may be several processes waiting for a call - * to open() returning. */ - } sound.soft = sound.dsp; sound.hard = sound.dsp; sound_silence(); - if (rc == 0) { - sq_release_buffers(); - MOD_DEC_USE_COUNT; - } + sq_release_buffers(); + MOD_DEC_USE_COUNT; + + sq.busy = 0; + WAKE_UP(sq.open_queue); + /* Wake up a process waiting for the queue being released. + * Note: There may be several processes waiting for a call + * to open() returning. */ + return rc; } @@ -4262,6 +4623,9 @@ if (sq_unit < 0) return; + sq.write_queue = sq.open_queue = sq.sync_queue = 0; + sq.busy = 0; + /* whatever you like as startup mode for /dev/dsp, * (/dev/audio hasn't got a startup mode). note that * once changed a new open() will *not* restore these! @@ -4332,7 +4696,7 @@ #endif /* CONFIG_AMIGA */ #ifdef CONFIG_PPC case DMASND_AWACS: - mach = "PowerMac "; + sprintf(mach, "PowerMac (AWACS rev %d) ", awacs_revision); break; #endif /* CONFIG_PPC */ } @@ -4510,6 +4874,7 @@ #ifdef CONFIG_PPC awacs_subframe = 0; + awacs_revision = 0; np = find_devices("awacs"); if (np == 0) { /* @@ -4524,6 +4889,8 @@ sfprop = (int *) get_property(sound, "sub-frame", 0); if (sfprop != 0 && *sfprop >= 0 && *sfprop < 16) awacs_subframe = *sfprop; + if (device_is_compatible(sound, "burgundy")) + awacs_revision = AWACS_BURGUNDY; } } if (np != NULL && np->n_addrs >= 3 && np->n_intrs >= 3) { @@ -4542,7 +4909,7 @@ awacs_tx_cmd_space = kmalloc((numBufs + 4) * sizeof(struct dbdma_cmd), GFP_KERNEL); if (awacs_tx_cmd_space == NULL) { - printk("DMA sound driver: Not enough buffer memory, driver disabled!\n"); + printk(KERN_ERR "DMA sound driver: Not enough buffer memory, driver disabled!\n"); return; } awacs_tx_cmds = (volatile struct dbdma_cmd *) @@ -4560,17 +4927,22 @@ awacs_write(awacs_reg[4] + MASK_ADDR4); /* Initialize recent versions of the awacs */ - awacs_revision = (in_le32(&awacs->codec_stat) >> 12) & 0xf; - if (awacs_revision >= 3) { - awacs_write(0x6000); - awacs_enable_amp(100 * 0x101); + if (awacs_revision == 0) { + awacs_revision = + (in_le32(&awacs->codec_stat) >> 12) & 0xf; + if (awacs_revision == 3) { + awacs_write(0x6000); + awacs_enable_amp(100 * 0x101); + } } + if (awacs_revision >= AWACS_BURGUNDY) + awacs_burgundy_init(); /* Initialize beep stuff */ beep_dbdma_cmd = awacs_tx_cmds + (numBufs + 1); orig_mksound = kd_mksound; kd_mksound = awacs_mksound; - beep_buf = (short *) kmalloc(BEEP_BUFLEN * 2, GFP_KERNEL); + beep_buf = (short *) kmalloc(BEEP_BUFLEN * 4, GFP_KERNEL); if (beep_buf == NULL) printk(KERN_WARNING "dmasound: no memory for " "beep buffer\n"); @@ -4596,15 +4968,15 @@ mixer_init(); if (!sound.mach.irqinit()) { - printk("DMA sound driver: Interrupt initialization failed\n"); + printk(KERN_ERR "DMA sound driver: Interrupt initialization failed\n"); return; } #ifdef MODULE irq_installed = 1; #endif - printk("DMA sound driver installed, using %d buffers of %dk.\n", numBufs, - bufSize); + printk(KERN_INFO "DMA sound driver installed, using %d buffers of %dk.\n", + numBufs, bufSize); return; } diff -u --recursive --new-file v2.2.3/linux/drivers/sound/es1370.c linux/drivers/sound/es1370.c --- v2.2.3/linux/drivers/sound/es1370.c Wed Mar 10 15:29:47 1999 +++ linux/drivers/sound/es1370.c Sun Mar 21 07:11:37 1999 @@ -127,6 +127,8 @@ /* --------------------------------------------------------------------- */ #undef OSS_DOCUMENTED_MIXER_SEMANTICS +#define DBG(x) {} +/*#define DBG(x) {x}*/ /* --------------------------------------------------------------------- */ @@ -1019,7 +1021,7 @@ tmo = (count * HZ) / dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL]; tmo >>= sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT]; if (!schedule_timeout(tmo ? : 1) && tmo) - printk(KERN_DEBUG "es1370: dma timed out??\n"); + DBG(printk(KERN_DEBUG "es1370: dma timed out??\n");) } remove_wait_queue(&s->dma_dac1.wait, &wait); current->state = TASK_RUNNING; @@ -1054,7 +1056,7 @@ tmo = (count * HZ) / DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV); tmo >>= sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; if (!schedule_timeout(tmo ? : 1) && tmo) - printk(KERN_DEBUG "es1370: dma timed out??\n"); + DBG(printk(KERN_DEBUG "es1370: dma timed out??\n");) } remove_wait_queue(&s->dma_dac2.wait, &wait); current->state = TASK_RUNNING; @@ -2189,7 +2191,7 @@ } tmo = (count * HZ) / 3100; if (!schedule_timeout(tmo ? : 1) && tmo) - printk(KERN_DEBUG "es1370: midi timed out??\n"); + DBG(printk(KERN_DEBUG "es1370: midi timed out??\n");) } remove_wait_queue(&s->midi.owait, &wait); current->state = TASK_RUNNING; diff -u --recursive --new-file v2.2.3/linux/drivers/sound/gus_card.c linux/drivers/sound/gus_card.c --- v2.2.3/linux/drivers/sound/gus_card.c Fri Jan 8 22:36:10 1999 +++ linux/drivers/sound/gus_card.c Wed Mar 10 17:03:52 1999 @@ -44,8 +44,6 @@ void attach_gus_card(struct address_info *hw_config) { - if(request_irq(hw_config->irq, gusintr, 0, "Gravis Ultrasound", hw_config)<0) - printk(KERN_ERR "gus_card.c: Unable to allocate IRQ %d\n", hw_config->irq); gus_wave_init(hw_config); @@ -60,6 +58,9 @@ #ifdef CONFIG_MIDI gus_midi_init(hw_config); #endif + if(request_irq(hw_config->irq, gusintr, 0, "Gravis Ultrasound", hw_config)<0) + printk(KERN_ERR "gus_card.c: Unable to allocate IRQ %d\n", hw_config->irq); + } int probe_gus(struct address_info *hw_config) diff -u --recursive --new-file v2.2.3/linux/drivers/sound/midi_synth.c linux/drivers/sound/midi_synth.c --- v2.2.3/linux/drivers/sound/midi_synth.c Tue Feb 23 15:21:34 1999 +++ linux/drivers/sound/midi_synth.c Sun Mar 21 07:11:37 1999 @@ -84,7 +84,7 @@ case 0xE0: STORE(SEQ_BENDER(synthno, msg[0] & 0x0f, - (msg[1] % 0x7f) | ((msg[2] & 0x7f) << 7))); + (msg[1] & 0x7f) | ((msg[2] & 0x7f) << 7))); break; default: diff -u --recursive --new-file v2.2.3/linux/drivers/video/Config.in linux/drivers/video/Config.in --- v2.2.3/linux/drivers/video/Config.in Wed Mar 10 15:29:47 1999 +++ linux/drivers/video/Config.in Wed Mar 17 09:17:22 1999 @@ -73,6 +73,9 @@ bool 'VESA VGA graphics console' CONFIG_FB_VESA define_bool CONFIG_VIDEO_SELECT y fi + if [ "$CONFIG_VISWS" = "y" ]; then + tristate 'SGI Visual Workstation framebuffer support' CONFIG_FB_SGIVW + fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then if [ "$CONFIG_PCI" != "n" ]; then tristate 'Matrox acceleration' CONFIG_FB_MATROX @@ -174,7 +177,8 @@ "$CONFIG_FB_VIRGE" = "y" -o "$CONFIG_FB_CYBER" = "y" -o \ "$CONFIG_FB_VALKYRIE" = "y" -o "$CONFIG_FB_PLATINUM" = "y" -o \ "$CONFIG_FB_IGA" = "y" -o "$CONFIG_FB_MATROX" = "y" -o \ - "$CONFIG_FB_CT65550" = "y" -o "$CONFIG_FB_PM2" = "y" ]; then + "$CONFIG_FB_CT65550" = "y" -o "$CONFIG_FB_PM2" = "y" -o \ + "$CONFIG_FB_SGIVW" = "y" ]; then define_bool CONFIG_FBCON_CFB8 y else if [ "$CONFIG_FB_ACORN" = "m" -o "$CONFIG_FB_ATARI" = "m" -o \ @@ -187,7 +191,8 @@ "$CONFIG_FB_VIRGE" = "m" -o "$CONFIG_FB_CYBER" = "m" -o \ "$CONFIG_FB_VALKYRIE" = "m" -o "$CONFIG_FB_PLATINUM" = "m" -o \ "$CONFIG_FB_IGA" = "m" -o "$CONFIG_FB_MATROX" = "m" -o \ - "$CONFIG_FB_CT65550" = "m" -o "$CONFIG_FB_PM2" = "m" ]; then + "$CONFIG_FB_CT65550" = "m" -o "$CONFIG_FB_PM2" = "m" -o \ + "$CONFIG_FB_SGIVW" = "m" ]; then define_bool CONFIG_FBCON_CFB8 m fi fi @@ -199,7 +204,7 @@ "$CONFIG_FB_VIRGE" = "y" -o "$CONFIG_FB_CYBER" = "y" -o \ "$CONFIG_FB_VALKYRIE" = "y" -o "$CONFIG_FB_PLATINUM" = "y" -o \ "$CONFIG_FB_CT65550" = "y" -o "$CONFIG_FB_MATROX" = "y" -o \ - "$CONFIG_FB_PM2" = "y" ]; then + "$CONFIG_FB_PM2" = "y" -o "$CONFIG_FB_SGIVW" = "y" ]; then define_bool CONFIG_FBCON_CFB16 y else if [ "$CONFIG_FB_ATARI" = "m" -o "$CONFIG_FB_ATY" = "m" -o \ @@ -210,7 +215,7 @@ "$CONFIG_FB_VIRGE" = "m" -o "$CONFIG_FB_CYBER" = "m" -o \ "$CONFIG_FB_VALKYRIE" = "m" -o "$CONFIG_FB_PLATINUM" = "m" -o \ "$CONFIG_FB_CT65550" = "m" -o "$CONFIG_FB_MATROX" = "m" -o \ - "$CONFIG_FB_PM2" = "m" ]; then + "$CONFIG_FB_PM2" = "m" -o "$CONFIG_FB_SGIVW" = "m" ]; then define_bool CONFIG_FBCON_CFB16 m fi fi @@ -230,14 +235,15 @@ "$CONFIG_FB_CONTROL" = "y" -o "$CONFIG_FB_CLGEN" = "y" -o \ "$CONFIG_FB_TGA" = "y" -o "$CONFIG_FB_PLATINUM" = "y" -o \ "$CONFIG_FB_MATROX" = "y" -o "$CONFIG_FB_PM2" = "y" -o \ - "$CONFIG_FB_FM2" = "y" ]; then + "$CONFIG_FB_FM2" = "y" -o "$CONFIG_FB_SGIVW" = "y" ]; then define_bool CONFIG_FBCON_CFB32 y else if [ "$CONFIG_FB_ATARI" = "m" -o "$CONFIG_FB_ATY" = "m" -o \ "$CONFIG_FB_VESA" = "m" -o "$CONFIG_FB_VIRTUAL" = "m" -o \ "$CONFIG_FB_CONTROL" = "m" -o "$CONFIG_FB_CLGEN" = "m" -o \ "$CONFIG_FB_TGA" = "m" -o "$CONFIG_FB_PLATINUM" = "m" -o \ - "$CONFIG_FB_MATROX" = "m" -o "$CONFIG_FB_PM2" = "m" ]; then + "$CONFIG_FB_MATROX" = "m" -o "$CONFIG_FB_PM2" = "m" -o \ + "$CONFIG_FB_SGIVW" = "m" ]; then define_bool CONFIG_FBCON_CFB32 m fi fi diff -u --recursive --new-file v2.2.3/linux/drivers/video/Makefile linux/drivers/video/Makefile --- v2.2.3/linux/drivers/video/Makefile Wed Mar 10 15:29:48 1999 +++ linux/drivers/video/Makefile Wed Mar 17 09:17:22 1999 @@ -132,6 +132,14 @@ endif endif +ifeq ($(CONFIG_FB_SGIVW),y) +L_OBJS += sgivwfb.o +else + ifeq ($(CONFIG_FB_SGIVW),m) + M_OBJS += sgivwfb.o + endif +endif + ifeq ($(CONFIG_FB_MAC),y) L_OBJS += macfb.o endif diff -u --recursive --new-file v2.2.3/linux/drivers/video/atyfb.c linux/drivers/video/atyfb.c --- v2.2.3/linux/drivers/video/atyfb.c Wed Mar 10 15:29:48 1999 +++ linux/drivers/video/atyfb.c Mon Mar 15 16:11:31 1999 @@ -1,4 +1,4 @@ -/* $Id: atyfb.c,v 1.102 1999/01/21 22:44:42 geert Exp $ +/* $Id: atyfb.c,v 1.103 1999/03/09 14:01:44 davem Exp $ * linux/drivers/video/atyfb.c -- Frame buffer device for ATI Mach64 * * Copyright (C) 1997-1998 Geert Uytterhoeven diff -u --recursive --new-file v2.2.3/linux/drivers/video/cgsixfb.c linux/drivers/video/cgsixfb.c --- v2.2.3/linux/drivers/video/cgsixfb.c Wed Mar 10 15:29:48 1999 +++ linux/drivers/video/cgsixfb.c Mon Mar 15 16:11:31 1999 @@ -1,4 +1,4 @@ -/* $Id: cgsixfb.c,v 1.13 1999/01/28 08:19:18 ecd Exp $ +/* $Id: cgsixfb.c,v 1.16 1999/03/09 14:01:49 davem Exp $ * cgsixfb.c: CGsix (GX,GXplus) frame buffer driver * * Copyright (C) 1996,1998 Jakub Jelinek (jj@ultra.linux.cz) diff -u --recursive --new-file v2.2.3/linux/drivers/video/creatorfb.c linux/drivers/video/creatorfb.c --- v2.2.3/linux/drivers/video/creatorfb.c Wed Mar 10 15:29:48 1999 +++ linux/drivers/video/creatorfb.c Mon Mar 15 16:11:31 1999 @@ -1,4 +1,4 @@ -/* $Id: creatorfb.c,v 1.19 1999/02/22 16:20:25 jj Exp $ +/* $Id: creatorfb.c,v 1.26 1999/03/11 00:29:54 davem Exp $ * creatorfb.c: Creator/Creator3D frame buffer driver * * Copyright (C) 1997,1998,1999 Jakub Jelinek (jj@ultra.linux.cz) @@ -132,18 +132,10 @@ struct ffb_fbc { /* Next vertex registers */ - u32 xxx1[3]; - volatile u32 alpha; - volatile u32 red; - volatile u32 green; - volatile u32 blue; - volatile u32 depth; - volatile u32 y; - volatile u32 x; - u32 xxx2[2]; - volatile u32 ryf; - volatile u32 rxf; - u32 xxx3[2]; + u32 xxx1[3]; volatile u32 alpha; volatile u32 red; + volatile u32 green; volatile u32 blue; volatile u32 depth; volatile + u32 y; volatile u32 x; u32 xxx2[2]; volatile u32 ryf; volatile u32 + rxf; u32 xxx3[2]; volatile u32 dmyf; volatile u32 dmxf; @@ -283,8 +275,7 @@ if (cache - n < 0) { fbc = fb->s.ffb.fbc; - do { - cache = (fbc->ucsr & FFB_UCSR_FIFO_MASK) - 4; + do { cache = (fbc->ucsr & FFB_UCSR_FIFO_MASK) - 8; } while (cache - n < 0); } fb->s.ffb.fifo_cache = cache - n; diff -u --recursive --new-file v2.2.3/linux/drivers/video/fbmem.c linux/drivers/video/fbmem.c --- v2.2.3/linux/drivers/video/fbmem.c Wed Mar 10 15:29:48 1999 +++ linux/drivers/video/fbmem.c Wed Mar 17 09:17:22 1999 @@ -91,12 +91,17 @@ extern void fm2fb_init(void); extern void fm2fb_setup(char *options, int *ints); extern void q40fb_init(void); +extern void sgivwfb_init(void); +extern void sgivwfb_setup(char* options, int *ints); static struct { const char *name; void (*init)(void); void (*setup)(char *options, int *ints); } fb_drivers[] __initdata = { +#ifdef CONFIG_FB_SGIVW + { "sgivw", sgivwfb_init, sgivwfb_setup }, +#endif #ifdef CONFIG_FB_RETINAZ3 { "retz3", retz3fb_init, retz3fb_setup }, #endif diff -u --recursive --new-file v2.2.3/linux/drivers/video/leofb.c linux/drivers/video/leofb.c --- v2.2.3/linux/drivers/video/leofb.c Wed Mar 10 15:29:48 1999 +++ linux/drivers/video/leofb.c Mon Mar 15 16:11:31 1999 @@ -1,4 +1,4 @@ -/* $Id: leofb.c,v 1.4 1998/09/04 15:43:45 jj Exp $ +/* $Id: leofb.c,v 1.5 1999/03/09 14:02:29 davem Exp $ * leofb.c: Leo (ZX) 24/8bit frame buffer driver * * Copyright (C) 1996,1997,1998 Jakub Jelinek (jj@ultra.linux.cz) diff -u --recursive --new-file v2.2.3/linux/drivers/video/offb.c linux/drivers/video/offb.c --- v2.2.3/linux/drivers/video/offb.c Wed Jan 20 23:14:06 1999 +++ linux/drivers/video/offb.c Fri Mar 19 10:51:42 1999 @@ -476,6 +476,9 @@ printk("no framebuffer address found for %s\n", dp->full_name); return; } + /* needed for the gxt on the f50 -- Cort */ + if ( dp->addrs[i].address < isa_mem_base ) + (u_long)dp->addrs[i].address += isa_mem_base; address = (u_long)dp->addrs[i].address; /* kludge for valkyrie */ diff -u --recursive --new-file v2.2.3/linux/drivers/video/pm2fb.c linux/drivers/video/pm2fb.c --- v2.2.3/linux/drivers/video/pm2fb.c Wed Mar 10 15:29:48 1999 +++ linux/drivers/video/pm2fb.c Mon Mar 15 16:11:31 1999 @@ -62,7 +62,7 @@ * The _DEFINITIVE_ memory mapping/unmapping functions. * This is due to the fact that they're changing soooo often... */ -#define MMAP(a,b) ioremap((u32 )(a), b) +#define MMAP(a,b) ioremap((unsigned long )(a), b) #define UNMAP(a,b) iounmap(a) /* diff -u --recursive --new-file v2.2.3/linux/drivers/video/promcon.c linux/drivers/video/promcon.c --- v2.2.3/linux/drivers/video/promcon.c Wed Mar 10 15:29:48 1999 +++ linux/drivers/video/promcon.c Mon Mar 15 16:11:31 1999 @@ -1,4 +1,4 @@ -/* $Id: promcon.c,v 1.13 1999/01/19 09:56:46 jj Exp $ +/* $Id: promcon.c,v 1.14 1999/03/09 14:02:42 davem Exp $ * Console driver utilizing PROM sun terminal emulation * * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) diff -u --recursive --new-file v2.2.3/linux/drivers/video/sbusfb.c linux/drivers/video/sbusfb.c --- v2.2.3/linux/drivers/video/sbusfb.c Wed Mar 10 15:29:48 1999 +++ linux/drivers/video/sbusfb.c Mon Mar 15 16:11:31 1999 @@ -229,7 +229,12 @@ for (i = 0; fb->mmap_map[i].size; i++) if (fb->mmap_map[i].voff == vma->vm_offset+page) { map_size = sbusfb_mmapsize(fb,fb->mmap_map[i].size); - map_offset = (fb->physbase + fb->mmap_map[i].poff) & PAGE_MASK; +#ifdef __sparc_v9__ +#define POFF_MASK (PAGE_MASK|0x1UL) +#else +#define POFF_MASK (PAGE_MASK) +#endif + map_offset = (fb->physbase + fb->mmap_map[i].poff) & POFF_MASK; break; } if (!map_size){ diff -u --recursive --new-file v2.2.3/linux/drivers/video/sgivwfb.c linux/drivers/video/sgivwfb.c --- v2.2.3/linux/drivers/video/sgivwfb.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/video/sgivwfb.c Wed Mar 17 09:17:22 1999 @@ -0,0 +1,1036 @@ +/* + * linux/drivers/video/sgivwfb.c -- SGI DBE frame buffer device + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Jeffrey Newquist, newquist@engr.sgi.som + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include