N: John A. Martin
E: jam@acm.org
-W: http://linux.wauug.org/~jam/
+W: http://www.tux.org/~jam/
P: 1024/04456D53 9D A3 6C 6B 88 80 8A 61 D7 06 22 4F 95 40 CE D2
P: 1024/3B986635 5A61 7EE6 9E20 51FB 59FB 2DA5 3E18 DD55 3B98 6635
D: FSSTND contributor
S: Germany
N: David C. Niemi
-E: niemi@erols.com
+E: niemi@tux.org
+W: http://www.tux.org/~niemi/
D: Assistant maintainer of Mtools, fdutils, and floppy driver
-D: Administrator of WAUUG Linux Server, http://linux.wauug.org
+D: Administrator of Tux.Org Linux Server, http://www.tux.org
S: 2364 Old Trail Drive
S: Reston, Virginia 20191
S: USA
your EPP chipset is from the SMC series, you are likely to have to
set this value greater than 0.
-EPP Timing
-CONFIG_SCSI_PPA_EPP_TIME
- This is the "reset time period", a delay time. The lower the value,
- the faster the access to the ZIP drive; too low a value may
- cause all sorts of mid-level SCSI problems however. If unsure, go
- with the default.
-
Network device support?
CONFIG_NETDEVICES
You can say N here in case you don't intend to connect to any other
CONFIG_MISC_RADIO
If you have a radio card (you will probably know if you do!), then
you will want to say "y" here and make a character device file
- (usually /dev/radio) with major number 10 and minor 129 using mknod
+ (usually /dev/radio) with major number 10 and minor 152 using mknod
("man mknod"). And then, don't forget to pick up some useful tools
to use said device (you _might_ find something at ftp.lmh.ox.ac.uk:
/users/weejock/linux/radio/, but I haven't written anything too
VERSION = 2
PATCHLEVEL = 1
-SUBLEVEL = 63
+SUBLEVEL = 64
ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/)
movw %dx,%ax /* selector = 0x0010 = cs */
movw $0x8E00,%dx /* interrupt gate - dpl=0, present */
- lea SYMBOL_NAME(idt),%edi
+ lea SYMBOL_NAME(idt_table),%edi
mov $256,%ecx
rp_sidt:
movl %eax,(%edi)
iret
/*
- * The interrupt descriptor table has room for 256 idt's
+ * The interrupt descriptor table has room for 256 idt's,
+ * the global descriptor table is dependent on the number
+ * of tasks we can have..
*/
+#define IDT_ENTRIES 256
+#ifdef CONFIG_APM
+#define GDT_ENTRIES (11+2*NR_TASKS)
+#else
+#define GDT_ENTRIES (8+2*NR_TASKS)
+#endif
+
+
+.globl SYMBOL_NAME(idt)
+.globl SYMBOL_NAME(gdt)
+
ALIGN
-.word 0
+ .word 0
idt_descr:
- .word 256*8-1 # idt contains 256 entries
- .long SYMBOL_NAME(idt)
+ .word IDT_ENTRIES*8-1 # idt contains 256 entries
+SYMBOL_NAME(idt):
+ .long SYMBOL_NAME(idt_table)
- ALIGN
-.word 0
+ .word 0
gdt_descr:
-#ifdef CONFIG_APM
- .word (11+2*NR_TASKS)*8-1
-#else
- .word (8+2*NR_TASKS)*8-1
-#endif
- .long SYMBOL_NAME(gdt)
+ .word GDT_ENTRIES*8-1
+SYMBOL_NAME(gdt):
+ .long SYMBOL_NAME(gdt_table)
/*
* This is initialized to create a identity-mapping at 0-4M (for bootup
ALIGN
/* 256 quadwords - 2048 bytes of idt */
-ENTRY(idt)
+ENTRY(idt_table)
.fill 256,8,0 # idt is uninitialized
/*
* NOTE! Make sure the gdt descriptor in head.S matches this if you
* change anything.
*/
-ENTRY(gdt)
+ENTRY(gdt_table)
.quad 0x0000000000000000 /* NULL descriptor */
.quad 0x0000000000000000 /* not used */
.quad 0x00cf9a000000ffff /* 0x10 kernel 4GB code at 0x00000000 */
"fdiv_bug\t: %s\n"
"hlt_bug\t\t: %s\n"
"sep_bug\t\t: %s\n"
- "pentium_f00f_bug\t\t: %s\n"
+ "f00f_bug\t: %s\n"
"fpu\t\t: %s\n"
"fpu_exception\t: %s\n"
"cpuid\t\t: %s\n"
gdt_48:
.word 0x0800 # gdt limit = 2048, 256 GDT entries
- .long gdt-0xc0000000 # gdt base = gdt (first SMP CPU)
+ .long gdt_table-0xc0000000 # gdt base = gdt (first SMP CPU)
.globl SYMBOL_NAME(trampoline_end)
SYMBOL_NAME_LABEL(trampoline_end)
#endif /* CONFIG_MATH_EMULATION */
-static struct
-{
- short limit __attribute__((packed));
- void * addr __attribute__((packed));
- short __pad __attribute__((packed));
-} idt_d;
-
-void * idt2;
-
__initfunc(void trap_init_f00f_bug(void))
{
- pgd_t * pgd;
- pmd_t * pmd;
- pte_t * pte;
- unsigned long twopage;
-
- printk("moving IDT ... ");
-
- twopage = (unsigned long) vmalloc (2*PAGE_SIZE);
-
- idt2 = (void *)(twopage + 4096-7*8);
-
- memcpy(idt2,&idt,sizeof(idt));
-
- idt_d.limit = 256*8-1;
- idt_d.addr = idt2;
- idt_d.__pad = 0;
-
- __asm__ __volatile__("\tlidt %0": "=m" (idt_d));
+ unsigned long page;
/*
- * Unmap lower page:
+ * Allocate a new page in virtual address space,
+ * and move the IDT to have entry #7 starting at
+ * the beginning of the page. We'll force a page
+ * fault for IDT entries #0-#6..
*/
- pgd = pgd_offset(current->mm, twopage);
- pmd = pmd_offset(pgd, twopage);
- pte = pte_offset(pmd, twopage);
+ page = (unsigned long) vmalloc(PAGE_SIZE);
+ memcpy((void *) page, idt_table + 7, (256-7)*8);
- pte_clear(pte);
- flush_tlb_all();
-
- printk(" ... done\n");
+ /*
+ * "idt" is magic - it overlaps the idt_descr
+ * variable so that updating idt will automatically
+ * update the idt descriptor..
+ */
+ idt = (struct desc_struct *)(page - 7*8);
+ __asm__ __volatile__("lidt %0": "=m" (idt_descr));
}
return 0;
}
-asmlinkage void divide_error(void);
-asmlinkage void debug(void);
-asmlinkage void nmi(void);
-asmlinkage void int3(void);
-asmlinkage void overflow(void);
-asmlinkage void bounds(void);
-asmlinkage void invalid_op(void);
-
asmlinkage void do_divide_error (struct pt_regs *, unsigned long);
asmlinkage void do_debug (struct pt_regs *, unsigned long);
asmlinkage void do_nmi (struct pt_regs *, unsigned long);
asmlinkage void do_bounds (struct pt_regs *, unsigned long);
asmlinkage void do_invalid_op (struct pt_regs *, unsigned long);
-extern int * idt2;
extern int pentium_f00f_bug;
/*
*/
asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code)
{
- struct task_struct *tsk = current;
- struct mm_struct *mm = tsk->mm;
+ struct task_struct *tsk;
+ struct mm_struct *mm;
struct vm_area_struct * vma;
unsigned long address;
unsigned long page;
unsigned long fixup;
int write;
- lock_kernel();
-
/* get the address */
__asm__("movl %%cr2,%0":"=r" (address));
+
+ /*
+ * Pentium F0 0F C7 C8 bug workaround. Do this first,
+ * to make sure we don't have locking problems with
+ * asynchronous traps (ie NMI).
+ */
+ if ( !(error_code & 7) && pentium_f00f_bug ) {
+ unsigned long nr;
+
+ nr = (address - (unsigned long) idt) >> 3;
+
+ if (nr < 7) {
+ static void (*handler[])(struct pt_regs *, unsigned long) = {
+ do_divide_error, /* 0 - divide overflow */
+ do_debug, /* 1 - debug trap */
+ do_nmi, /* 2 - NMI */
+ do_int3, /* 3 - int 3 */
+ do_overflow, /* 4 - overflow */
+ do_bounds, /* 5 - bound range */
+ do_invalid_op }; /* 6 - invalid opcode */
+ handler[nr](regs, 0);
+ return;
+ }
+ }
+
+ lock_kernel();
+
+ tsk = current;
+ mm = tsk->mm;
+
down(&mm->mmap_sem);
vma = find_vma(mm, address);
if (!vma)
goto out;
}
- printk("<%p/%p>\n", idt2, (void *)address);
- /*
- * Pentium F0 0F C7 C8 bug workaround:
- */
- if ( pentium_f00f_bug && (address >= (unsigned long)idt2) &&
- (address < (unsigned long)idt2+256*8) ) {
-
- void (*handler) (void);
- int nr = (address-(unsigned long)idt2)/8;
- unsigned long low, high;
-
- low = idt[nr].a;
- high = idt[nr].b;
-
- handler = (void (*) (void)) ((low&0x0000ffff) | (high&0xffff0000));
- printk("<handler %p... ", handler);
- unlock_kernel();
-
- if (handler==divide_error)
- do_divide_error(regs,error_code);
- else if (handler==debug)
- do_debug(regs,error_code);
- else if (handler==nmi)
- do_nmi(regs,error_code);
- else if (handler==int3)
- do_int3(regs,error_code);
- else if (handler==overflow)
- do_overflow(regs,error_code);
- else if (handler==bounds)
- do_bounds(regs,error_code);
- else if (handler==invalid_op)
- do_invalid_op(regs,error_code);
- else {
- printk("INVALID HANDLER!\n");
- for (;;) __cli();
- }
- printk("... done>\n");
- goto out;
- }
-
/* Are we prepared to handle this kernel fault? */
if ((fixup = search_exception_table(regs->eip)) != 0) {
printk(KERN_DEBUG "%s: Exception at [<%lx>] cr2=%lx (fixup: %lx)\n",
}
-static long write_mouse(struct inode * inode, struct file * file,
- const char * buffer, unsigned long count)
+static ssize_t write_mouse(struct file * file, const char * buffer,
+ size_t count, loff_t *ppos)
{
return -EINVAL;
}
-static long read_mouse(struct inode * inode, struct file * file,
- char * buffer, unsigned long count)
+static ssize_t read_mouse(struct file * file, char * buffer,
+ size_t count, loff_t *ppos)
{
- int i;
+ ssize_t i;
if (count < 3)
return -EINVAL;
return 0;
}
-
-static long write_mouse(struct inode * inode, struct file * file,
- const char * buffer, unsigned long count)
+static ssize_t write_mouse(struct file * file,
+ const char * buffer, size_t count, loff_t *ppos)
{
return -EINVAL;
}
-static long read_mouse(struct inode * inode, struct file * file,
- char * buffer, unsigned long count)
+static ssize_t read_mouse(struct file * file,
+ char * buffer, size_t count, loff_t *ppos)
{
int i, dx, dy;
}
#ifdef MODULE
-static int io[PC_MAX_PORTS+1] = { 0, };
-static int dma[PC_MAX_PORTS] = { PARPORT_DMA_AUTO, };
-static int irq[PC_MAX_PORTS] = { PARPORT_IRQ_AUTO, };
+static int io[PC_MAX_PORTS+1] = { [0 ... PC_MAX_PORTS] = 0 };
+static int dma[PC_MAX_PORTS] = { [0 ... PC_MAX_PORTS-1] = PARPORT_DMA_AUTO };
+static int irq[PC_MAX_PORTS] = { [0 ... PC_MAX_PORTS-1] = PARPORT_IRQ_AUTO };
MODULE_PARM(io, "1-" __MODULE_STRING(PC_MAX_PORTS) "i");
MODULE_PARM(irq, "1-" __MODULE_STRING(PC_MAX_PORTS) "i");
MODULE_PARM(dma, "1-" __MODULE_STRING(PC_MAX_PORTS) "i");
-This driver has been upgraded to include generic DECchip support through the
-use of the on-board SROM that is found on all DECchip cards except for the
-DC21040. The driver will work with the following set of cards and probably
-more:
+ Originally, this driver was written for the Digital Equipment
+ Corporation series of EtherWORKS ethernet cards:
+
+ DE425 TP/COAX EISA
+ DE434 TP PCI
+ DE435 TP/COAX/AUI PCI
+ DE450 TP/COAX/AUI PCI
+ DE500 10/100 PCI Fasternet
+
+ but it will now attempt to support all cards which conform to the
+ Digital Semiconductor SROM Specification. The driver currently
+ recognises the following chips:
+
+ DC21040 (no SROM)
+ DC21041[A]
+ DC21140[A]
+ DC21142
+ DC21143
+
+ So far the driver is known to work with the following cards:
KINGSTON
Linksys
SMC8432
SMC9332 (w/new SROM)
ZNYX31[45]
- DIGITAL EtherWORKS PCI/EISA (DE425, DE434, DE435, DE450, DE500)
-
-Auto media detection is provided so that the media choice isn't compiled in
-and is flexible enough to be able to reconfigure on-the-fly.
-
-The ability to load this driver as a loadable module has been included and
-will now load (and unload) as many DECchip cards as it can find and
-configure with just one invocation of 'insmod'.
+ ZNYX346 10/100 4 port (can act as a 10/100 bridge!)
-The performance we've achieved so far has been measured through the 'ttcp'
-tool at 1.06MB/s for TCP and 1.17MB/s for UDP. This measures the total
-stack performance which includes the card, so don't expect to get much
-nearer the 1.25MB/s theoretical ethernet rate.
+ The driver has been tested on a relatively busy network using the DE425,
+ DE434, DE435 and DE500 cards and benchmarked with 'ttcp': it transferred
+ 16M of data to a DECstation 5000/200 as follows:
TCP UDP
TX RX TX RX
- DE425 1030k 997k 1170k 1128k (EISA on a Dell 433DE)
- DE434 1063k 995k 1170k 1125k (PCI: DECpc XL 466d2)
- DE435 1063k 995k 1170k 1125k (PCI: DECpc XL 466d2)
- DE500 1063k 998k 1170k 1125k in 10Mb/s mode (PCI: DECpc XL 466d2)
+ DE425 1030k 997k 1170k 1128k
+ DE434 1063k 995k 1170k 1125k
+ DE435 1063k 995k 1170k 1125k
+ DE500 1063k 998k 1170k 1125k in 10Mb/s mode
+
+ All values are typical (in kBytes/sec) from a sample of 4 for each
+ measurement. Their error is +/-20k on a quiet (private) network and also
+ depend on what load the CPU has.
+
+ =========================================================================
+
+ The ability to load this driver as a loadable module has been included
+ and used extensively during the driver development (to save those long
+ reboot sequences). Loadable module support under PCI and EISA has been
+ achieved by letting the driver autoprobe as if it were compiled into the
+ kernel. Do make sure you're not sharing interrupts with anything that
+ cannot accommodate interrupt sharing!
+
+ To utilise this ability, you have to do 8 things:
+
+ 0) have a copy of the loadable modules code installed on your system.
+ 1) copy de4x5.c from the /linux/drivers/net directory to your favourite
+ temporary directory.
+ 2) for fixed autoprobes (not recommended), edit the source code near
+ line 5537 to reflect the I/O address you're using, or assign these when
+ loading by:
+
+ insmod de4x5 io=0xghh where g = bus number
+ hh = device number
+
+ NB: autoprobing for modules is now supported by default. You may just
+ use:
+
+ insmod de4x5
+
+ to load all available boards. For a specific board, still use
+ the 'io=?' above.
+ 3) compile de4x5.c, but include -DMODULE in the command line to ensure
+ that the correct bits are compiled (see end of source code).
+ 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a
+ kernel with the de4x5 configuration turned off and reboot.
+ 5) insmod de4x5 [io=0xghh]
+ 6) run the net startup bits for your new eth?? interface(s) manually
+ (usually /etc/rc.inet[12] at boot time).
+ 7) enjoy!
+
+ To unload a module, turn off the associated interface(s)
+ 'ifconfig eth?? down' then 'rmmod de4x5'.
+
+ Automedia detection is included so that in principal you can disconnect
+ from, e.g. TP, reconnect to BNC and things will still work (after a
+ pause whilst the driver figures out where its media went). My tests
+ using ping showed that it appears to work....
+
+ By default, the driver will now autodetect any DECchip based card.
+ Should you have a need to restrict the driver to DIGITAL only cards, you
+ can compile with a DEC_ONLY define, or if loading as a module, use the
+ 'dec_only=1' parameter.
+
+ The SMC9332 card has a non-compliant SROM which needs fixing - I have
+ patched this driver to detect it because the SROM format used complies
+ to a previous DEC-STD format.
+
+ I have removed the buffer copies needed for receive on Intels. I cannot
+ remove them for Alphas since the Tulip hardware only does longword
+ aligned DMA transfers and the Alphas get alignment traps with non
+ longword aligned data copies (which makes them really slow). No comment.
+
+ I have added SROM decoding routines to make this driver work with any
+ card that supports the Digital Semiconductor SROM spec. This will help
+ all cards running the dc2114x series chips in particular. Cards using
+ the dc2104x chips should run correctly with the basic driver. I'm in
+ debt to <mjacob@feral.com> for the testing and feedback that helped get
+ this feature working. So far we have tested KINGSTON, SMC8432, SMC9332
+ (with the latest SROM complying with the SROM spec V3: their first was
+ broken), ZNYX342 and LinkSys. ZYNX314 (dual 21041 MAC) and ZNYX 315
+ (quad 21041 MAC) cards also appear to work despite their incorrectly
+ wired IRQs.
-All values are typical (in kBytes/sec) from a sample of 4 for each
-measurement. Their error is approx +/-20k on a quiet (private) network and
-also depend on what load the CPU has, CPU speed etc.
+ I have added a temporary fix for interrupt problems when some SCSI cards
+ share the same interrupt as the DECchip based cards. The problem occurs
+ because the SCSI card wants to grab the interrupt as a fast interrupt
+ (runs the service routine with interrupts turned off) vs. this card
+ which really needs to run the service routine with interrupts turned on.
+ This driver will now add the interrupt service routine as a fast
+ interrupt if it is bounced from the slow interrupt. THIS IS NOT A
+ RECOMMENDED WAY TO RUN THE DRIVER and has been done for a limited time
+ until people sort out their compatibility issues and the kernel
+ interrupt service code is fixed. YOU SHOULD SEPARATE OUT THE FAST
+ INTERRUPT CARDS FROM THE SLOW INTERRUPT CARDS to ensure that they do not
+ run on the same interrupt. PCMCIA/CardBus is another can of worms...
-I've had reports that Alphas can get 80+Mb/s when using 100BASE-TX and
-similar figures for 133MHz Pentium Pros.
+ Finally, I think I have really fixed the module loading problem with
+ more than one DECchip based card. As a side effect, I don't mess with
+ the device structure any more which means that if more than 1 card in
+ 2.0.x is installed (4 in 2.1.x), the user will have to edit
+ linux/drivers/net/Space.c to make room for them. Hence, module loading
+ is the preferred way to use this driver, since it doesn't have this
+ limitation.
-Enjoy!
+ Where SROM media detection is used and full duplex is specified in the
+ SROM, the feature is ignored unless de4x5_full_duplex is set at compile
+ time OR during a module load (insmod de4x5 de4x5_full_duplex=1). This
+ is because there is no way to automatically detect full duplex links
+ except through autonegotiation. When I include the autonegotiation
+ feature in the SROM autoconf code, this detection will occur
+ automatically.
-Dave
at1700 PROMISC PROMISC YES Software
atp PROMISC PROMISC YES Software
cs89x0 YES YES YES Software
-de4x5 YES NO YES Hardware
+de4x5 YES YES YES Hardware
de600 NO NO NO N/A
de620 PROMISC PROMISC YES Software
depca YES PROMISC YES Hardware
Digital Semiconductor SROM Specification. The driver currently
recognises the following chips:
- DC21040 (no SROM)
- DC21041[A]
- DC21140[A]
+ DC21040 (no SROM)
+ DC21041[A]
+ DC21140[A]
+ DC21142
+ DC21143
- I plan to add DC2114[23] support ASAP, time permitting. So far the
- driver is known to work with the following cards:
+ So far the driver is known to work with the following cards:
KINGSTON
Linksys
1) copy de4x5.c from the /linux/drivers/net directory to your favourite
temporary directory.
2) for fixed autoprobes (not recommended), edit the source code near
- line 5005 to reflect the I/O address you're using, or assign these when
+ line 5539 to reflect the I/O address you're using, or assign these when
loading by:
insmod de4x5 io=0xghh where g = bus number
INTERRUPT CARDS FROM THE SLOW INTERRUPT CARDS to ensure that they do not
run on the same interrupt. PCMCIA/CardBus is another can of worms...
+ Finally, I think I have really fixed the module loading problem with
+ more than one DECchip based card. As a side effect, I don't mess with
+ the device structure any more which means that if more than 1 card in
+ 2.0.x is installed (4 in 2.1.x), the user will have to edit
+ linux/drivers/net/Space.c to make room for them. Hence, module loading
+ is the preferred way to use this driver, since it doesn't have this
+ limitation.
+
+ Where SROM media detection is used and full duplex is specified in the
+ SROM, the feature is ignored unless de4x5_full_duplex is set at compile
+ time OR during a module load (insmod de4x5 de4x5_full_duplex=1). This
+ is because there is no way to automatically detect full duplex links
+ except through autonegotiation. When I include the autonegotiation
+ feature in the SROM autoconf code, this detection will occur
+ automatically.
+
TO DO:
------
+ o check what revision numbers the 21142 and 21143 have
+ o
Revision History
----------------
Added byte counters from <phil@tazenda.demon.co.uk>
Added SA_INTERRUPT temporary fix from
<mjacob@feral.com>.
+ 0.53 12-Nov-97 Fix the *_probe() to include 'eth??' name during
+ module load: bug reported by
+ <Piete.Brooks@cl.cam.ac.uk>
+ Fix multi-MAC, one SROM, to work with 2114x chips:
+ bug reported by <cmetz@inner.net>.
+ Make above search independent of BIOS device scan
+ direction.
+ Completed DC2114[23] autosense functions.
=========================================================================
*/
-static const char *version = "de4x5.c:V0.52 1997/4/26 davies@maniac.ultranet.com\n";
+static const char *version = "de4x5.c:V0.53 1997/11/12 davies@maniac.ultranet.com\n";
#include <linux/module.h>
#include <linux/bios32.h>
#include <linux/pci.h>
#include <linux/delay.h>
-#include <linux/init.h>
#include <asm/bitops.h>
#include <asm/io.h>
#include <asm/dma.h>
#define c_char const char
#include <linux/version.h>
-#if LINUX_VERSION_CODE < ((2 << 16) | (1 << 8))
-#define net_device_stats enet_statistics
-#define copy_to_user(a,b,c) memcpy_tofs(a,b,c)
-#define copy_from_user(a,b,c) memcpy_fromfs(a,b,c)
-#define le16_to_cpu(a) cpu_to_le16(a)
-#define le32_to_cpu(a) cpu_to_le32(a)
-#ifdef __powerpc__
-#define cpu_to_le16(a) ((((a) & 0x00ffU) << 8) | (((a) & 0xff00U) >> 8))
-#define cpu_to_le32(a) ((((a) & 0x000000ffU) << 24) |\
- (((a) & 0x0000ff00U) << 8) |\
- (((a) & 0x00ff0000U) >> 8) |\
- (((a) & 0xff000000U) >> 24))
-#else
-#define cpu_to_le16(a) (a)
-#define cpu_to_le32(a) (a)
-#endif /* __powerpc__ */
-#include <asm/segment.h>
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,0)
+# define __initfunc(__arginit) __arginit
+# define test_and_set_bit set_bit
+# define net_device_stats enet_statistics
+# define copy_to_user(a,b,c) memcpy_tofs(a,b,c)
+# define copy_from_user(a,b,c) memcpy_fromfs(a,b,c)
+# define le16_to_cpu(a) cpu_to_le16(a)
+# define le32_to_cpu(a) cpu_to_le32(a)
+# ifdef __powerpc__
+# define cpu_to_le16(a) ((((a) & 0x00ffU) << 8) | (((a) & 0xff00U) >> 8))
+# define cpu_to_le32(a) ((((a) & 0x000000ffU) << 24) |\
+ (((a) & 0x0000ff00U) << 8) |\
+ (((a) & 0x00ff0000U) >> 8) |\
+ (((a) & 0xff000000U) >> 24))
+# else
+# define cpu_to_le16(a) (a)
+# define cpu_to_le32(a) (a)
+# endif /* __powerpc__ */
+# include <asm/segment.h>
#else
-#include <asm/uaccess.h>
+# include <asm/uaccess.h>
+# include <linux/init.h>
#endif /* LINUX_VERSION_CODE */
#define TWIDDLE(a) (u_short)le16_to_cpu(get_unaligned((u_short *)(a)))
u_int ana; /* NWay Advertisement */
u_int fdx; /* Full DupleX capabilites for each media */
u_int ttm; /* Transmit Threshold Mode for each media */
+ u_int mci; /* 21142 MII Connector Interrupt info */
};
#define DE4X5_MAX_PHY 8 /* Allow upto 8 attached PHY devices per board */
#ifdef DE4X5_DEBUG
static int de4x5_debug = DE4X5_DEBUG;
#else
-static int de4x5_debug = (0);
+/*static int de4x5_debug = (DEBUG_MII | DEBUG_SROM | DEBUG_PCICFG | DEBUG_MEDIA | DEBUG_VERSION);*/
+static int de4x5_debug = (DEBUG_MEDIA | DEBUG_VERSION);
#endif
#ifdef DE4X5_AUTOSENSE /* Should be done on a per adapter basis */
#define PCI_MAX_BUS_NUM 8
#define DE4X5_PCI_TOTAL_SIZE 0x80 /* I/O address extent */
#define DE4X5_CLASS_CODE 0x00020000 /* Network controller, Ethernet */
+#define NO_MORE_PCI -2 /* PCI bus search all done */
/*
** Memory Alignment. Each descriptor is 4 longwords long. To force a
static int dc21040_autoconf(struct device *dev);
static int dc21041_autoconf(struct device *dev);
static int dc21140m_autoconf(struct device *dev);
+static int dc2114x_autoconf(struct device *dev);
static int srom_autoconf(struct device *dev);
static int de4x5_suspect_state(struct device *dev, int timeout, int prev_state, int (*fn)(struct device *, int), int (*asfn)(struct device *));
static int dc21040_state(struct device *dev, int csr13, int csr14, int csr15, int timeout, int next_state, int suspect_state, int (*fn)(struct device *, int));
static int test_media(struct device *dev, s32 irqs, s32 irq_mask, s32 csr13, s32 csr14, s32 csr15, s32 msec);
-static int test_sym_link(struct device *dev, int msec);
+static int test_for_100Mb(struct device *dev, int msec);
+static int wait_for_link(struct device *dev);
static int test_mii_reg(struct device *dev, int reg, int mask, int pol, long msec);
static int is_spd_100(struct device *dev);
static int is_100_up(struct device *dev);
/*static void srom_busy(u_int command, u_long address);*/
static void sendto_srom(u_int command, u_long addr);
static int getfrom_srom(u_long addr);
-static void srom_map_media(struct device *dev);
+static int srom_map_media(struct device *dev);
static int srom_infoleaf_info(struct device *dev);
static void srom_init(struct device *dev);
static void srom_exec(struct device *dev, u_char *p);
static void eisa_probe(struct device *dev, u_long iobase);
static void pci_probe(struct device *dev, u_long iobase);
-static struct device *alloc_device(struct device *dev, u_long iobase);
-static struct device *insert_device(struct device *dev, u_long iobase,
- int (*init)(struct device *));
+static void srom_search(int index);
static char *build_setup_frame(struct device *dev, int mode);
static void disable_ast(struct device *dev);
static void enable_ast(struct device *dev, u32 time_out);
static long de4x5_switch_mac_port(struct device *dev);
+static int gep_rd(struct device *dev);
+static void gep_wr(s32 data, struct device *dev);
static void timeout(struct device *dev, void (*fn)(u_long data), u_long data, u_long msec);
static void yawn(struct device *dev, int state);
-static int de4x5_dev_index(char *s);
static void link_modules(struct device *dev, struct device *tmp);
static void de4x5_dbg_open(struct device *dev);
static void de4x5_dbg_mii(struct device *dev, int k);
int init_module(void);
void cleanup_module(void);
static struct device *unlink_modules(struct device *p);
-static int autoprobed = 0, loading_module = 1;
+static struct device *insert_device(struct device *dev, u_long iobase,
+ int (*init)(struct device *));
+static int count_adapters(void);
+static int loading_module = 1;
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0)
+MODULE_PARM(de4x5_debug, "i");
+MODULE_PARM(de4x5_full_duplex, "i");
+MODULE_PARM(dec_only, "i");
+#endif /* LINUX_VERSION_CODE */
# else
-static int autoprobed = 0, loading_module = 0;
+static int loading_module = 0;
#endif /* MODULE */
static char name[DE4X5_NAME_LENGTH + 1];
static u_char de4x5_irq[] = EISA_ALLOWED_IRQ_LIST;
-static int num_de4x5s = 0, num_eth = 0;
+static int num_de4x5s = 0;
static int cfrv = 0, useSROM = 0;
+static int lastEISA = 0, lastPCI = -1;
+static struct device *lastModule = NULL;
/*
** List the SROM infoleaf functions and chipsets
\f
/*
** Autoprobing in modules is allowed here. See the top of the file for
-** more info. Until I fix (un)register_netdevice() we won't be able to use it
-** though.
+** more info.
*/
__initfunc(int
de4x5_probe(struct device *dev))
{
- int status = -ENODEV;
u_long iobase = dev->base_addr;
eisa_probe(dev, iobase);
pci_probe(dev, iobase);
- /*
- ** Walk the device list to check that at least one device
- ** initialised OK
- */
- for (; (dev->priv == NULL) && (dev->next != NULL); dev = dev->next);
-
- if (dev->priv) status = 0;
- if (iobase == 0) autoprobed = 1;
-
- return status;
+ return (dev->priv ? 0 : -ENODEV);
}
__initfunc(static int
/* Initialise the SROM pointers if possible */
if (lp->useSROM) {
lp->state = INITIALISED;
- de4x5_dbg_srom((struct de4x5_srom *)&lp->srom);
if (srom_infoleaf_info(dev)) {
return -ENXIO;
}
u_long iobase = dev->base_addr;
int i, status = 0;
s32 omr;
-
+
/* Allocate the RX buffers */
for (i=0; i<lp->rxRingSize; i++) {
if (de4x5_alloc_rx_buff(dev, i, 0) == NULL) {
printk("WARNING: there may be IRQ related problems in heavily loaded systems.\n");
}
}
+
dev->tbusy = 0;
dev->start = 1;
dev->interrupt = UNMASK_INTERRUPTS;
de4x5_init(struct device *dev)
{
/* Lock out other processes whilst setting up the hardware */
- set_bit(0, (void *)&dev->tbusy);
+ test_and_set_bit(0, (void *)&dev->tbusy);
de4x5_sw_reset(dev);
** without these values. Cache align 16 long.
*/
bmr = (lp->chipset==DC21140 ? PBL_8 : PBL_4) | DESC_SKIP_LEN | CACHE_ALIGN;
+ bmr |= ((lp->chipset & ~0x00ff)==DC2114x ? BMR_RML : 0);
outl(bmr, DE4X5_BMR);
-
+
omr = inl(DE4X5_OMR) & ~OMR_PR; /* Turn off promiscuous mode */
if (lp->chipset == DC21140) {
omr |= (OMR_SDP | OMR_SB);
}
barrier();
-
+
/* Build the setup frame depending on filtering mode */
SetMulticastFilter(dev);
load_packet(dev, lp->setup_frame, PERFECT_F|TD_SET|SETUP_FRAME_LEN, NULL);
outl(omr|OMR_ST, DE4X5_OMR);
-
+
/* Poll for setup frame completion (adapter interrupts are disabled now) */
sti(); /* Ensure timer interrupts */
for (j=0, i=0;(i<500) && (j==0);i++) { /* Upto 500ms delay */
if ((s32)le32_to_cpu(lp->tx_ring[lp->tx_new].status) >= 0) j=1;
}
outl(omr, DE4X5_OMR); /* Stop everything! */
-
+
if (j == 0) {
printk("%s: Setup frame timed out, status %08x\n", dev->name,
inl(DE4X5_STS));
lp->tx_new = (++lp->tx_new) % lp->txRingSize;
lp->tx_old = lp->tx_new;
-
+
return status;
}
return 0;
}
- set_bit(0, (void*)&dev->tbusy); /* Stop send re-tries */
+ test_and_set_bit(0, (void*)&dev->tbusy); /* Stop send re-tries */
if (lp->tx_enable == NO) { /* Cannot send for now */
return -1;
}
sti();
/* Test if cache is already locked - requeue skb if so */
- if (test_and_set_bit(0, (void *)&lp->cache.lock) && !dev->interrupt) return -1;
+ if (test_and_set_bit(0, (void *)&lp->cache.lock) && !dev->interrupt)
+ return -1;
/* Transmit descriptor ring full or stale skb */
if (dev->tbusy || lp->tx_skb[lp->tx_new]) {
while (skb && !dev->tbusy && !lp->tx_skb[lp->tx_new]) {
cli();
- set_bit(0, (void*)&dev->tbusy);
+ test_and_set_bit(0, (void*)&dev->tbusy);
load_packet(dev, skb->data, TD_IC | TD_LS | TD_FS | skb->len, skb);
#if LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8))
lp->stats.tx_bytes += skb->len;
eisa_probe(struct device *dev, u_long ioaddr))
{
int i, maxSlots, status, device;
+ u_char irq;
u_short vendor;
u32 cfid;
u_long iobase;
struct bus_type *lp = &bus;
char name[DE4X5_STRLEN];
- struct device *tmp;
-
- if (autoprobed) return; /* Been here before ! */
+ if (lastEISA == MAX_EISA_SLOTS) return; /* No more EISA devices to search */
+
lp->bus = EISA;
if (ioaddr == 0) { /* Autoprobing */
maxSlots = i + 1;
}
- for (status= -ENODEV;(i<maxSlots)&&(dev!=NULL);i++,iobase+=EISA_SLOT_INC) {
+ for (status = -ENODEV; (i<maxSlots) && (dev!=NULL); i++, iobase+=EISA_SLOT_INC) {
if (EISA_signature(name, EISA_ID)) {
cfid = (u32) inl(PCI_CFID);
cfrv = (u_short) inl(PCI_CFRV);
vendor = (u_short) cfid;
/* Read the EISA Configuration Registers */
- dev->irq = inb(EISA_REG0);
- dev->irq = de4x5_irq[(dev->irq >> 1) & 0x03];
+ irq = inb(EISA_REG0);
+ irq = de4x5_irq[(irq >> 1) & 0x03];
- if (is_DC2114x) device |= (cfrv & 0x00f0);
+ if (is_DC2114x) device |= (cfrv & CFRV_RN);
lp->chipset = device;
- DevicePresent(DE4X5_APROM);
+
/* Write the PCI Configuration Registers */
outl(PCI_COMMAND_IO | PCI_COMMAND_MASTER, PCI_CFCS);
outl(0x00006000, PCI_CFLT);
outl(iobase, PCI_CBIO);
+ DevicePresent(DE4X5_APROM);
if (check_region(iobase, DE4X5_EISA_TOTAL_SIZE) == 0) {
- if ((tmp = alloc_device(dev, iobase)) != NULL) {
- if ((status = de4x5_hw_init(tmp, iobase)) == 0) {
- num_de4x5s++;
- if (loading_module) link_modules(dev, tmp);
- } else if (loading_module && (tmp != dev)) {
- kfree(tmp);
- }
+ dev->irq = irq;
+ if ((status = de4x5_hw_init(dev, iobase)) == 0) {
+ num_de4x5s++;
+ if (loading_module) link_modules(lastModule, dev);
+ lastEISA = i;
+ return;
}
- } else if (autoprobed) {
+ } else if (ioaddr != 0) {
printk("%s: region already allocated at 0x%04lx.\n", dev->name,iobase);
}
}
}
-
+
+ if (ioaddr == 0) lastEISA = i;
+
return;
}
__initfunc(static void
pci_probe(struct device *dev, u_long ioaddr))
{
- u_char irq;
+ u_char irq, timer;
u_char pb, pbus, dev_num, dnum, dev_fn;
u_short dev_id, vendor, index, status;
u_int class = DE4X5_CLASS_CODE;
u_int device, iobase;
struct bus_type *lp = &bus;
- struct device *tmp;
- if (autoprobed) return;
-
- if (!pcibios_present()) return; /* No PCI bus in this machine! */
+ if (lastPCI == NO_MORE_PCI) return;
+
+ if (!pcibios_present()) {
+ lastPCI = NO_MORE_PCI;
+ return; /* No PCI bus in this machine! */
+ }
lp->bus = PCI;
-
+ lp->bus_num = 0;
+
if ((ioaddr < 0x1000) && loading_module) {
pbus = (u_short)(ioaddr >> 8);
dnum = (u_short)(ioaddr & 0xff);
dnum = 0;
}
- for (index=0;
+ for (index=lastPCI+1;
(pcibios_find_class(class, index, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND);
index++) {
dev_num = PCI_SLOT(dev_fn);
continue;
}
+ /* Search for an SROM on this bus */
+ if (lp->bus_num != pb) {
+ lp->bus_num = pb;
+ srom_search(index);
+ }
+
/* Get the chip configuration revision register */
pcibios_read_config_dword(pb, PCI_DEVICE, PCI_REVISION_ID, &cfrv);
lp->bus_num = pb;
/* Set the chipset information */
- if (is_DC2114x) device |= (cfrv & 0x00f0);
+ if (is_DC2114x) device |= (cfrv & CFRV_RN);
lp->chipset = device;
- if (is_DC21142 || is_DC21143) {
- printk("de4x5: Detected a %s chip. Currently this is unsupported in this driver.\nPlease email the author to request its inclusion!\n", (is_DC21142?"DC21142":"DC21143"));
- continue;
- }
-
/* Get the board I/O address */
pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &iobase);
iobase &= CBIO_MASK;
}
if (!(status & PCI_COMMAND_MASTER)) continue;
+ /* Check the latency timer for values > 0x60 */
+ pcibios_read_config_byte(pb, PCI_DEVICE, PCI_LATENCY_TIMER, &timer);
+ if (timer < 0x60) {
+ pcibios_write_config_byte(pb, PCI_DEVICE, PCI_LATENCY_TIMER, 0x60);
+ }
+
DevicePresent(DE4X5_APROM);
if (check_region(iobase, DE4X5_PCI_TOTAL_SIZE) == 0) {
- if ((tmp = alloc_device(dev, iobase)) != NULL) {
- tmp->irq = irq;
- if ((status = de4x5_hw_init(tmp, iobase)) == 0) {
- num_de4x5s++;
- if (loading_module) link_modules(dev, tmp);
- } else if (loading_module && (tmp != dev)) {
- kfree(tmp);
+ dev->irq = irq;
+ if ((status = de4x5_hw_init(dev, iobase)) == 0) {
+ num_de4x5s++;
+ if (loading_module) {
+ link_modules(lastModule, dev);
+ lastPCI = index;
}
+ return;
}
- } else if (autoprobed) {
+ } else if (ioaddr != 0) {
printk("%s: region already allocated at 0x%04x.\n", dev->name,
(u_short)iobase);
}
}
}
-
+
+ if (loading_module) lastPCI = NO_MORE_PCI;
+
return;
}
/*
-** Search the entire 'eth' device list for a fixed probe. If a match isn't
-** found then check for an autoprobe or unused device location. If they
-** are not available then insert a new device structure at the end of
-** the current list.
+** This function searches the current bus (which is >0) for a DECchip with an
+** SROM, so that in multiport cards that have one SROM shared between multiple
+** DECchips, we can find the base SROM irrespective of the BIOS scan direction.
+** For single port cards this is a time waster...
*/
-__initfunc(static struct device *
-alloc_device(struct device *dev, u_long iobase))
+__initfunc(static void
+srom_search(int index))
{
- struct device *adev = NULL;
- int fixed = 0, new_dev = 0;
+ u_char irq, pb, dev_fn;
+ u_short dev_id, dev_num, vendor, status;
+ u_int class = DE4X5_CLASS_CODE;
+ u_int device, iobase;
+ int i, j;
+ struct bus_type *lp = &bus;
- if (!dev) return dev;
- num_eth = de4x5_dev_index(dev->name);
+ for (;
+ (pcibios_find_class(class, index, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND);
+ index++) {
- if (loading_module) {
- if (dev->priv) {
- dev = insert_device(dev, iobase, de4x5_probe);
+ if (lp->bus_num != pb) return;
+ dev_num = PCI_SLOT(dev_fn);
+ device = 0;
+ pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor);
+ pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &dev_id);
+ device = dev_id;
+ device <<= 8;
+ if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) {
+ continue;
}
- num_eth++;
- return dev;
- }
- while (1) {
- if (((dev->base_addr == DE4X5_NDA) || (dev->base_addr==0)) && !adev) {
- adev=dev;
- } else if ((dev->priv == NULL) && (dev->base_addr==iobase)) {
- fixed = 1;
- } else {
- if (dev->next == NULL) {
- new_dev = 1;
- } else if (strncmp(dev->next->name, "eth", 3) != 0) {
- new_dev = 1;
- }
- }
- if ((dev->next == NULL) || new_dev || fixed) break;
- dev = dev->next;
- num_eth++;
- }
- if (adev && !fixed) {
- dev = adev;
- num_eth = de4x5_dev_index(dev->name);
- new_dev = 0;
- }
+ /* Get the chip configuration revision register */
+ pcibios_read_config_dword(pb, PCI_DEVICE, PCI_REVISION_ID, &cfrv);
- if (((dev->next == NULL) &&
- ((dev->base_addr != DE4X5_NDA) && (dev->base_addr != 0)) && !fixed) ||
- new_dev) {
- num_eth++; /* New device */
- dev = insert_device(dev, iobase, de4x5_probe);
- }
-
- return dev;
-}
+ /* Set the device number information */
+ lp->device = dev_num;
+ lp->bus_num = pb;
+
+ /* Set the chipset information */
+ if (is_DC2114x) device |= (cfrv & CFRV_RN);
+ lp->chipset = device;
-/*
-** If at end of eth device list and can't use current entry, malloc
-** one up. If memory could not be allocated, print an error message.
-*/
-__initfunc(static struct device *
-insert_device(struct device *dev, u_long iobase, int (*init)(struct device *)))
-{
- struct device *new;
+ /* Get the board I/O address */
+ pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &iobase);
+ iobase &= CBIO_MASK;
- new = (struct device *)kmalloc(sizeof(struct device)+8, GFP_KERNEL);
- if (new == NULL) {
- printk("eth%d: Device not initialised, insufficient memory\n",num_eth);
- return NULL;
- } else {
- memset((char *)new, 0, sizeof(struct device)+8);
- new->name = (char *)(new + 1);
- new->base_addr = iobase; /* assign the io address */
- new->init = init; /* initialisation routine */
- if (!loading_module) {
- new->next = dev->next;
- dev->next = new;
- if (num_eth > 9999) {
- sprintf(new->name,"eth????");/* New device name */
- } else {
- sprintf(new->name,"eth%d", num_eth);/* New device name */
+ /* Fetch the IRQ to be used */
+ pcibios_read_config_byte(pb, PCI_DEVICE, PCI_INTERRUPT_LINE, &irq);
+ if ((irq == 0) || (irq == (u_char) 0xff)) continue;
+
+ /* Check if I/O accesses are enabled */
+ pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status);
+ if (!(status & PCI_COMMAND_IO)) continue;
+
+ /* Search for a valid SROM attached to this DECchip */
+ DevicePresent(DE4X5_APROM);
+ for (j=0, i=0; i<ETH_ALEN; i++) {
+ j += (u_char) *((u_char *)&lp->srom + SROM_HWADD + i);
+ }
+ if ((j != 0) && (j != 0x5fa)) {
+ last.chipset = device;
+ last.bus = pb;
+ last.irq = irq;
+ for (i=0; i<ETH_ALEN; i++) {
+ last.addr[i] = (u_char)*((u_char *)&lp->srom + SROM_HWADD + i);
}
+ return;
}
}
- return new;
-}
-
-__initfunc(static int
-de4x5_dev_index(char *s))
-{
- int i=0, j=0;
-
- for (;*s; s++) {
- if (isdigit(*s)) {
- j=1;
- i = (i * 10) + (*s - '0');
- } else if (j) break;
- }
-
- return i;
+ return;
}
__initfunc(static void
{
struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
u_long iobase = dev->base_addr;
- int next_tick = DE4X5_AUTOSENSE_MS;;
-
+ int next_tick = DE4X5_AUTOSENSE_MS;
+
lp->linkOK = 0;
lp->c_media = AUTO; /* Bogus last media */
disable_ast(dev);
inl(DE4X5_MFC); /* Zero the lost frames counter */
lp->media = INIT;
+ lp->tcount = 0;
+
if (lp->useSROM) {
next_tick = srom_autoconf(dev);
} else if (lp->chipset == DC21040) {
next_tick &= ~TIMER_CB;
} else {
if (lp->useSROM) {
- srom_map_media(dev);
+ if (srom_map_media(dev) < 0) {
+ lp->tcount++;
+ return next_tick;
+ }
srom_exec(dev, lp->phy[lp->active].gep);
if (lp->infoblock_media == ANS) {
ana = lp->phy[lp->active].ana | MII_ANA_CSMA;
case SPD_DET: /* Choose 10Mb/s or 100Mb/s */
if (lp->timeout < 0) {
- lp->tmp = (lp->phy[lp->active].id ? MII_SR_LKS :
- (~inl(DE4X5_GEP) & GEP_LNP));
+ lp->tmp = (lp->phy[lp->active].id ? MII_SR_LKS :
+ (~gep_rd(dev) & GEP_LNP));
SET_100Mb_PDET;
}
- if ((slnk = test_sym_link(dev, 6200)) < 0) {
+ if ((slnk = test_for_100Mb(dev, 6500)) < 0) {
next_tick = slnk & ~TIMER_CB;
} else {
if (is_spd_100(dev) && is_100_up(dev)) {
de4x5_init_connection(dev);
} else {
if (!lp->linkOK && (lp->autosense == AUTO)) {
- if (!(is_spd_100(dev) && is_100_up(dev))) {
+ if (!is_100_up(dev) || (!lp->useSROM && !is_spd_100(dev))) {
lp->media = INIT;
lp->tcount++;
next_tick = DE4X5_AUTOSENSE_MS;
de4x5_init_connection(dev);
} else {
if (!lp->linkOK && (lp->autosense == AUTO)) {
- if (!(!is_spd_100(dev) && is_10_up(dev))) {
+ if (!is_10_up(dev) || (!lp->useSROM && is_spd_100(dev))) {
lp->media = INIT;
lp->tcount++;
next_tick = DE4X5_AUTOSENSE_MS;
return next_tick;
}
-static int
-srom_autoconf(struct device *dev)
-{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
-
- return lp->infoleaf_fn(dev);
-}
-
/*
-** This mapping keeps the original media codes and FDX flag unchanged.
-** While it isn't strictly necessary, it helps me for the moment...
+** This routine may be merged into dc21140m_autoconf() sometime as I'm
+** changing how I figure out the media - but trying to keep it backwards
+** compatible with the de500-xa and de500-aa.
+** Whether it's BNC, AUI, SYM or MII is sorted out in the infoblock
+** functions and set during de4x5_mac_port() and/or de4x5_reset_phy().
+** This routine just has to figure out whether 10Mb/s or 100Mb/s is
+** active.
+** When autonegotiation is working, the ANS part searches the SROM for
+** the highest common speed (TP) link that both can run and if that can
+** be full duplex. That infoblock is executed and then the link speed set.
+**
+** Only _10Mb and _100Mb are tested here.
*/
-static void
-srom_map_media(struct device *dev)
+static int
+dc2114x_autoconf(struct device *dev)
{
struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ int iobase = dev->base_addr;
+ s32 cr, anlpa, ana, cap, irqs, irq_mask, imr, omr, slnk, sr, sts;
+ int next_tick = DE4X5_AUTOSENSE_MS;
- lp->fdx = 0;
- switch(lp->infoblock_media) {
- case SROM_10BASETF:
- lp->fdx = TRUE;
- case SROM_10BASET:
- if (lp->chipset == DC21140) {
- lp->media = _10Mb;
- } else {
- lp->media = TP;
+ switch (lp->media) {
+ case INIT:
+ if (lp->timeout < 0) {
+ DISABLE_IRQs;
+ lp->tx_enable = FALSE;
+ lp->linkOK = 0;
+ lp->timeout = -1;
+ de4x5_save_skbs(dev); /* Save non transmitted skb's */
}
+ if ((next_tick = de4x5_reset_phy(dev)) < 0) {
+ next_tick &= ~TIMER_CB;
+ } else {
+ lp->media = SPD_DET;
+ if ((lp->infoblock_media == ANS) &&
+ ((sr=is_anc_capable(dev)) & MII_SR_ANC)) {
+ ana = (((sr >> 6) & MII_ANA_TAF) | MII_ANA_CSMA);
+ ana &= (lp->fdx ? ~0 : ~MII_ANA_FDAM);
+ mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII);
+ lp->media = ANS;
+ }
+ lp->local_state = 0;
+ next_tick = dc2114x_autoconf(dev);
+ }
break;
-
- case SROM_10BASE2:
- lp->media = BNC;
- break;
-
- case SROM_10BASE5:
- lp->media = AUI;
- break;
-
- case SROM_100BASETF:
- lp->fdx = TRUE;
- case SROM_100BASET:
- lp->media = _100Mb;
- break;
-
- case SROM_100BASET4:
- lp->media = _100Mb;
- break;
-
- case SROM_100BASEFF:
- lp->fdx = TRUE;
- case SROM_100BASEF:
- lp->media = _100Mb;
- break;
-
+
case ANS:
- lp->media = ANS;
- break;
-
- default:
- printk("%s: Bad media code [%d] detected in SROM!\n", dev->name,
- lp->infoblock_media);
- break;
- }
-
- return;
-}
-
-static void
-de4x5_init_connection(struct device *dev)
-{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
-
- if (lp->media != lp->c_media) {
- de4x5_dbg_media(dev);
- lp->c_media = lp->media; /* Stop scrolling media messages */
- }
-
- cli();
- de4x5_restore_skbs(dev);
- de4x5_setup_intr(dev);
- lp->tx_enable = YES;
- dev->tbusy = 0;
- sti();
- outl(POLL_DEMAND, DE4X5_TPD);
- mark_bh(NET_BH);
-
- return;
-}
-
-/*
-** General PHY reset function. Some MII devices don't reset correctly
-** since their MII address pins can float at voltages that are dependent
-** on the signal pin use. Do a double reset to ensure a reset.
-*/
-static int
-de4x5_reset_phy(struct device *dev)
-{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
- int next_tick = 0;
-
- if ((lp->useSROM) || (lp->phy[lp->active].id)) {
- if (lp->timeout < 0) {
- if (lp->useSROM) {
- if (lp->phy[lp->active].rst) { /* MII device specific reset */
- srom_exec(dev, lp->phy[lp->active].rst);
- srom_exec(dev, lp->phy[lp->active].rst);
- } else if (lp->rst) { /* Type 5 infoblock reset */
- srom_exec(dev, lp->rst);
- srom_exec(dev, lp->rst);
- }
+ switch (lp->local_state) {
+ case 0:
+ if (lp->timeout < 0) {
+ mii_wr(MII_CR_ASSE | MII_CR_RAN, MII_CR, lp->phy[lp->active].addr, DE4X5_MII);
+ }
+ cr = test_mii_reg(dev, MII_CR, MII_CR_RAN, FALSE, 500);
+ if (cr < 0) {
+ next_tick = cr & ~TIMER_CB;
} else {
- PHY_HARD_RESET;
+ if (cr) {
+ lp->local_state = 0;
+ lp->media = SPD_DET;
+ } else {
+ lp->local_state++;
+ }
+ next_tick = dc2114x_autoconf(dev);
}
- if (lp->useMII) {
- mii_wr(MII_CR_RST, MII_CR, lp->phy[lp->active].addr, DE4X5_MII);
- }
- }
- if (lp->useMII) {
- next_tick = test_mii_reg(dev, MII_CR, MII_CR_RST, FALSE, 500);
- }
- } else if (lp->chipset == DC21140) {
- PHY_HARD_RESET;
- }
-
- return next_tick;
-}
+ break;
+
+ case 1:
+ if ((sr=test_mii_reg(dev, MII_SR, MII_SR_ASSC, TRUE, 2000)) < 0) {
+ next_tick = sr & ~TIMER_CB;
+ } else {
+ lp->media = SPD_DET;
+ lp->local_state = 0;
+ if (sr) { /* Success! */
+ lp->tmp = MII_SR_ASSC;
+ anlpa = mii_rd(MII_ANLPA, lp->phy[lp->active].addr, DE4X5_MII);
+ ana = mii_rd(MII_ANA, lp->phy[lp->active].addr, DE4X5_MII);
+ if (!(anlpa & MII_ANLPA_RF) &&
+ (cap = anlpa & MII_ANLPA_TAF & ana)) {
+ if (cap & MII_ANA_100M) {
+ lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_100M) ? TRUE : FALSE);
+ lp->media = _100Mb;
+ } else if (cap & MII_ANA_10M) {
+ lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_10M) ? TRUE : FALSE);
+ lp->media = _10Mb;
+ }
+ }
+ } /* Auto Negotiation failed to finish */
+ next_tick = dc2114x_autoconf(dev);
+ } /* Auto Negotiation failed to start */
+ break;
+ }
+ break;
+
+ case AUI:
+ if (!lp->tx_enable) {
+ if (lp->timeout < 0) {
+ omr = inl(DE4X5_OMR); /* Set up half duplex for AUI */
+ outl(omr & ~OMR_FDX, DE4X5_OMR);
+ }
+ irqs = 0;
+ irq_mask = 0;
+ sts = test_media(dev,irqs, irq_mask, 0, 0, 0, 1000);
+ if (sts < 0) {
+ next_tick = sts & ~TIMER_CB;
+ } else {
+ if (!(inl(DE4X5_SISR) & SISR_SRA) && (lp->autosense == AUTO)) {
+ lp->media = BNC;
+ next_tick = dc2114x_autoconf(dev);
+ } else {
+ lp->local_state = 1;
+ de4x5_init_connection(dev);
+ }
+ }
+ } else if (!lp->linkOK && (lp->autosense == AUTO)) {
+ lp->media = AUI_SUSPECT;
+ next_tick = 3000;
+ }
+ break;
+
+ case AUI_SUSPECT:
+ next_tick = de4x5_suspect_state(dev, 1000, AUI, ping_media, dc2114x_autoconf);
+ break;
+
+ case BNC:
+ switch (lp->local_state) {
+ case 0:
+ if (lp->timeout < 0) {
+ omr = inl(DE4X5_OMR); /* Set up half duplex for BNC */
+ outl(omr & ~OMR_FDX, DE4X5_OMR);
+ }
+ irqs = 0;
+ irq_mask = 0;
+ sts = test_media(dev,irqs, irq_mask, 0, 0, 0, 1000);
+ if (sts < 0) {
+ next_tick = sts & ~TIMER_CB;
+ } else {
+ lp->local_state++; /* Ensure media connected */
+ next_tick = dc2114x_autoconf(dev);
+ }
+ break;
+
+ case 1:
+ if (!lp->tx_enable) {
+ if ((sts = ping_media(dev, 3000)) < 0) {
+ next_tick = sts & ~TIMER_CB;
+ } else {
+ if (sts) {
+ lp->local_state = 0;
+ lp->tcount++;
+ lp->media = INIT;
+ } else {
+ de4x5_init_connection(dev);
+ }
+ }
+ } else if (!lp->linkOK && (lp->autosense == AUTO)) {
+ lp->media = BNC_SUSPECT;
+ next_tick = 3000;
+ }
+ break;
+ }
+ break;
+
+ case BNC_SUSPECT:
+ next_tick = de4x5_suspect_state(dev, 1000, BNC, ping_media, dc2114x_autoconf);
+ break;
+
+ case SPD_DET: /* Choose 10Mb/s or 100Mb/s */
+ if (srom_map_media(dev) < 0) {
+ lp->tcount++;
+ lp->media = INIT;
+ return next_tick;
+ }
+ if (lp->media == _100Mb) {
+ if ((slnk = test_for_100Mb(dev, 6500)) < 0) {
+ lp->media = SPD_DET;
+ return (slnk & ~TIMER_CB);
+ }
+ } else {
+ if (wait_for_link(dev) < 0) {
+ lp->media = SPD_DET;
+ return PDET_LINK_WAIT;
+ }
+ }
+ if (((lp->media == _100Mb) && is_100_up(dev)) ||
+ ((lp->media == _10Mb) && is_10_up(dev)) ||
+ (lp->media == BNC) || (lp->media == AUI)) {
+ next_tick = dc2114x_autoconf(dev);
+ } else {
+ lp->tcount++;
+ lp->media = INIT;
+ }
+ break;
+
+ case _10Mb:
+ next_tick = 3000;
+ if (!lp->tx_enable) {
+ SET_10Mb;
+ de4x5_init_connection(dev);
+ } else {
+ if (!lp->linkOK && (lp->autosense == AUTO)) {
+ if (!is_10_up(dev) || (!lp->useSROM && is_spd_100(dev))) {
+ lp->media = INIT;
+ lp->tcount++;
+ next_tick = DE4X5_AUTOSENSE_MS;
+ }
+ }
+ }
+ break;
+
+ case _100Mb:
+ next_tick = 3000;
+ if (!lp->tx_enable) {
+ SET_100Mb;
+ de4x5_init_connection(dev);
+ } else {
+ if (!lp->linkOK && (lp->autosense == AUTO)) {
+ if (!is_100_up(dev) || (!lp->useSROM && !is_spd_100(dev))) {
+ lp->media = INIT;
+ lp->tcount++;
+ next_tick = DE4X5_AUTOSENSE_MS;
+ }
+ }
+ }
+ break;
+
+ default:
+ lp->tcount++;
+printk("Huh?: media:%02x\n", lp->media);
+ lp->media = INIT;
+ break;
+ }
+
+ return next_tick;
+}
+
+static int
+srom_autoconf(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+
+ return lp->infoleaf_fn(dev);
+}
+
+/*
+** This mapping keeps the original media codes and FDX flag unchanged.
+** While it isn't strictly necessary, it helps me for the moment...
+** The early return avoids a media state / SROM media space clash.
+*/
+static int
+srom_map_media(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+
+ lp->fdx = 0;
+ if (lp->infoblock_media == lp->media)
+ return 0;
+
+ switch(lp->infoblock_media) {
+ case SROM_10BASETF:
+ if (!de4x5_full_duplex) return -1;
+ lp->fdx = TRUE;
+ case SROM_10BASET:
+ if (de4x5_full_duplex && !lp->fdx) return -1;
+ if ((lp->chipset == DC21140) || ((lp->chipset & ~0x00ff) == DC2114x)) {
+ lp->media = _10Mb;
+ } else {
+ lp->media = TP;
+ }
+ break;
+
+ case SROM_10BASE2:
+ lp->media = BNC;
+ break;
+
+ case SROM_10BASE5:
+ lp->media = AUI;
+ break;
+
+ case SROM_100BASETF:
+ if (!de4x5_full_duplex) return -1;
+ lp->fdx = TRUE;
+ case SROM_100BASET:
+ if (de4x5_full_duplex && !lp->fdx) return -1;
+ lp->media = _100Mb;
+ break;
+
+ case SROM_100BASET4:
+ lp->media = _100Mb;
+ break;
+
+ case SROM_100BASEFF:
+ if (!de4x5_full_duplex) return -1;
+ lp->fdx = TRUE;
+ case SROM_100BASEF:
+ if (de4x5_full_duplex && !lp->fdx) return -1;
+ lp->media = _100Mb;
+ break;
+
+ case ANS:
+ lp->media = ANS;
+ break;
+
+ default:
+ printk("%s: Bad media code [%d] detected in SROM!\n", dev->name,
+ lp->infoblock_media);
+ return -1;
+ break;
+ }
+
+ return 0;
+}
+
+static void
+de4x5_init_connection(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+
+ if (lp->media != lp->c_media) {
+ de4x5_dbg_media(dev);
+ lp->c_media = lp->media; /* Stop scrolling media messages */
+ }
+
+ cli();
+ de4x5_restore_skbs(dev);
+ de4x5_setup_intr(dev);
+ lp->tx_enable = YES;
+ dev->tbusy = 0;
+ sti();
+ outl(POLL_DEMAND, DE4X5_TPD);
+ mark_bh(NET_BH);
+
+ return;
+}
+
+/*
+** General PHY reset function. Some MII devices don't reset correctly
+** since their MII address pins can float at voltages that are dependent
+** on the signal pin use. Do a double reset to ensure a reset.
+*/
+static int
+de4x5_reset_phy(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ int next_tick = 0;
+
+ if ((lp->useSROM) || (lp->phy[lp->active].id)) {
+ if (lp->timeout < 0) {
+ if (lp->useSROM) {
+ if (lp->phy[lp->active].rst) {
+ srom_exec(dev, lp->phy[lp->active].rst);
+ srom_exec(dev, lp->phy[lp->active].rst);
+ } else if (lp->rst) { /* Type 5 infoblock reset */
+ srom_exec(dev, lp->rst);
+ srom_exec(dev, lp->rst);
+ }
+ } else {
+ PHY_HARD_RESET;
+ }
+ if (lp->useMII) {
+ mii_wr(MII_CR_RST, MII_CR, lp->phy[lp->active].addr, DE4X5_MII);
+ }
+ }
+ if (lp->useMII) {
+ next_tick = test_mii_reg(dev, MII_CR, MII_CR_RST, FALSE, 500);
+ }
+ } else if (lp->chipset == DC21140) {
+ PHY_HARD_RESET;
+ }
+
+ return next_tick;
+}
static int
test_media(struct device *dev, s32 irqs, s32 irq_mask, s32 csr13, s32 csr14, s32 csr15, s32 msec)
if (lp->timeout < 0) {
lp->timeout = msec/100;
- reset_init_sia(dev, csr13, csr14, csr15);
+ if (!lp->useSROM) { /* Already done if by SROM, else dc2104[01] */
+ reset_init_sia(dev, csr13, csr14, csr15);
+ }
/* set up the interrupt mask */
outl(irq_mask, DE4X5_IMR);
outl(sts, DE4X5_STS);
/* clear csr12 NRA and SRA bits */
- if (lp->chipset == DC21041) {
+ if ((lp->chipset == DC21041) || lp->useSROM) {
csr12 = inl(DE4X5_SISR);
outl(csr12, DE4X5_SISR);
}
return sisr;
}
+/*
+** Samples the 100Mb Link State Signal. The sample interval is important
+** because too fast a rate can give erroneous results and confuse the
+** speed sense algorithm.
+*/
+#define SAMPLE_INTERVAL 500 /* ms */
+#define SAMPLE_DELAY 2000 /* ms */
static int
-test_sym_link(struct device *dev, int msec)
+test_for_100Mb(struct device *dev, int msec)
{
struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- int iobase = dev->base_addr;
- int gep = 0;
-
+ int gep = 0, ret = ((lp->chipset & ~0x00ff)==DC2114x? -1 :GEP_SLNK);
+
if (lp->timeout < 0) {
- lp->timeout = msec/100;
+ if ((msec/SAMPLE_INTERVAL) <= 0) return 0;
+ if (msec > SAMPLE_DELAY) {
+ lp->timeout = (msec - SAMPLE_DELAY)/SAMPLE_INTERVAL;
+ gep = SAMPLE_DELAY | TIMER_CB;
+ return gep;
+ } else {
+ lp->timeout = msec/SAMPLE_INTERVAL;
+ }
}
if (lp->phy[lp->active].id || lp->useSROM) {
- gep = ((is_100_up(dev) && is_spd_100(dev)) ? GEP_SLNK : 0);
+ gep = is_100_up(dev) | is_spd_100(dev);
} else {
- gep = (~inl(DE4X5_GEP) & (GEP_SLNK | GEP_LNP));
+ gep = (~gep_rd(dev) & (GEP_SLNK | GEP_LNP));
}
- if (!(gep & GEP_SLNK) && --lp->timeout) {
- gep = 100 | TIMER_CB;
+ if (!(gep & ret) && --lp->timeout) {
+ gep = SAMPLE_INTERVAL | TIMER_CB;
} else {
lp->timeout = -1;
}
return gep;
}
+static int
+wait_for_link(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+
+ if (lp->timeout < 0) {
+ lp->timeout = 1;
+ }
+
+ if (lp->timeout--) {
+ return TIMER_CB;
+ } else {
+ lp->timeout = -1;
+ }
+
+ return 0;
+}
+
/*
**
**
u_long iobase = dev->base_addr;
int spd;
- if (lp->useSROM && !lp->useMII) {
- spd = (lp->asBitValid &
- (lp->asPolarity ^ (inl(DE4X5_GEP) & lp->asBit))) |
- (lp->linkOK & ~lp->asBitValid);
- } else if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) {
+ if (lp->useMII) {
spd = mii_rd(lp->phy[lp->active].spd.reg, lp->phy[lp->active].addr, DE4X5_MII);
spd = ~(spd ^ lp->phy[lp->active].spd.value);
spd &= lp->phy[lp->active].spd.mask;
+ } else if (!lp->useSROM) { /* de500-xa */
+ spd = ((~gep_rd(dev)) & GEP_SLNK);
} else {
- spd = ((~inl(DE4X5_GEP)) & GEP_SLNK);
+ if ((lp->ibn == 2) || !lp->asBitValid)
+ return ((lp->chipset == DC21143)?(~inl(DE4X5_SISR)&SISR_LS100):0);
+
+ spd = (lp->asBitValid & (lp->asPolarity ^ (gep_rd(dev) & lp->asBit))) |
+ (lp->linkOK & ~lp->asBitValid);
}
return spd;
struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
u_long iobase = dev->base_addr;
- if (lp->useSROM && !lp->useMII) {
- return ((lp->asBitValid &
- (lp->asPolarity ^ (inl(DE4X5_GEP) & lp->asBit))) |
- (lp->linkOK & ~lp->asBitValid));
- } else if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) {
+ if (lp->useMII) {
/* Double read for sticky bits & temporary drops */
mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII);
return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII) & MII_SR_LKS);
+ } else if (!lp->useSROM) { /* de500-xa */
+ return ((~gep_rd(dev)) & GEP_SLNK);
} else {
- return ((~inl(DE4X5_GEP)) & GEP_SLNK);
+ if ((lp->ibn == 2) || !lp->asBitValid)
+ return ((lp->chipset == DC21143)?(~inl(DE4X5_SISR)&SISR_LS100):0);
+
+ return ((lp->asBitValid&(lp->asPolarity^(gep_rd(dev)&lp->asBit))) |
+ (lp->linkOK & ~lp->asBitValid));
}
}
struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
u_long iobase = dev->base_addr;
- if (lp->useSROM && !lp->useMII) {
- return ((lp->asBitValid &
- (lp->asPolarity ^ (inl(DE4X5_GEP) & lp->asBit))) |
- (lp->linkOK & ~lp->asBitValid));
- } else if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) {
+ if (lp->useMII) {
/* Double read for sticky bits & temporary drops */
mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII);
return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII) & MII_SR_LKS);
+ } else if (!lp->useSROM) { /* de500-xa */
+ return ((~gep_rd(dev)) & GEP_LNP);
} else {
- return ((~inl(DE4X5_GEP)) & GEP_LNP);
+ if ((lp->ibn == 2) || !lp->asBitValid)
+ return (((lp->chipset & ~0x00ff) == DC2114x) ?
+ (~inl(DE4X5_SISR)&SISR_LS10):
+ 0);
+
+ return ((lp->asBitValid&(lp->asPolarity^(gep_rd(dev)&lp->asBit))) |
+ (lp->linkOK & ~lp->asBitValid));
}
}
if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) {
return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII));
+ } else if ((lp->chipset & ~0x00ff) == DC2114x) {
+ return (inl(DE4X5_SISR) & SISR_LPN) >> 11;
} else {
return 0;
}
lp->cache.csr0 = inl(DE4X5_BMR);
lp->cache.csr6 = (inl(DE4X5_OMR) & ~(OMR_ST | OMR_SR));
lp->cache.csr7 = inl(DE4X5_IMR);
- if (lp->chipset != DC21140) {
- lp->cache.csr13 = inl(DE4X5_SICR);
- lp->cache.csr14 = inl(DE4X5_STRR);
- lp->cache.csr15 = inl(DE4X5_SIGR);
- }
break;
case DE4X5_RESTORE_STATE:
outl(lp->cache.csr6, DE4X5_OMR);
outl(lp->cache.csr7, DE4X5_IMR);
if (lp->chipset == DC21140) {
- outl(lp->cache.gepc, DE4X5_GEP);
- outl(lp->cache.gep, DE4X5_GEP);
+ gep_wr(lp->cache.gepc, dev);
+ gep_wr(lp->cache.gep, dev);
} else {
reset_init_sia(dev, lp->cache.csr13, lp->cache.csr14,
lp->cache.csr15);
**
*/
static void
-reset_init_sia(struct device *dev, s32 sicr, s32 strr, s32 sigr)
+reset_init_sia(struct device *dev, s32 csr13, s32 csr14, s32 csr15)
{
struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
u_long iobase = dev->base_addr;
-
+
RESET_SIA;
- outl(sigr, DE4X5_SIGR);
- outl(strr, DE4X5_STRR);
- outl(sicr, DE4X5_SICR);
+ if (lp->useSROM) {
+ if (lp->ibn == 3) {
+ srom_exec(dev, lp->phy[lp->active].rst);
+ srom_exec(dev, lp->phy[lp->active].gep);
+ outl(1, DE4X5_SICR);
+ return;
+ } else {
+ csr15 = lp->cache.csr15;
+ csr14 = lp->cache.csr14;
+ csr13 = lp->cache.csr13;
+ outl(csr15 | lp->cache.gepc, DE4X5_SIGR);
+ outl(csr15 | lp->cache.gep, DE4X5_SIGR);
+ }
+ } else {
+ outl(csr15, DE4X5_SIGR);
+ }
+ outl(csr14, DE4X5_STRR);
+ outl(csr13, DE4X5_SICR);
+
+ de4x5_ms_delay(10);
return;
}
if (lp->chipset != DC21041) {
useSROM = TRUE; /* card is not recognisably DEC */
}
+ } else if ((lp->chipset & ~0x00ff) == DC2114x) {
+ useSROM = TRUE;
}
+
return status;
}
/*
** Set up the Ethernet PROM counter to the start of the Ethernet address on
** the DC21040, else read the SROM for the other chips.
+** The SROM may not be present in a multi-MAC card, so first read the
+** MAC address and check for a bad address. If there is a bad one then exit
+** immediately with the prior srom contents intact (the h/w address will
+** be fixed up later).
*/
static void
DevicePresent(u_long aprom_addr)
{
- int i;
+ int i, j=0;
struct bus_type *lp = &bus;
if (lp->chipset == DC21040) {
outl(0, aprom_addr); /* Reset Ethernet Address ROM Pointer */
} else { /* Read new srom */
- u_short tmp, *p = (short *)&lp->srom;
+ u_short tmp, *p = (short *)((char *)&lp->srom + SROM_HWADD);
+ for (i=0; i<(ETH_ALEN>>1); i++) {
+ tmp = srom_rd(aprom_addr, (SROM_HWADD>>1) + i);
+ *p = le16_to_cpu(tmp);
+ j += *p++;
+ }
+ if ((j == 0) || (j == 0x2fffd)) {
+ return;
+ }
+
+ p=(short *)&lp->srom;
for (i=0; i<(sizeof(struct de4x5_srom)>>1); i++) {
tmp = srom_rd(aprom_addr, i);
*p++ = le16_to_cpu(tmp);
struct bus_type *lp = &bus;
broken = de4x5_bad_srom(lp);
+
for (i=0,k=0,j=0;j<3;j++) {
k <<= 1;
if (k > 0xffff) k-=0xffff;
return;
}
+/*
+** Assume that the irq's do not follow the PCI spec - this is seems
+** to be true so far (2 for 2).
+*/
static int
test_bad_enet(struct device *dev, int status)
{
if (dev->dev_addr[i] != 0) break;
}
for (i=0; i<ETH_ALEN; i++) last.addr[i] = dev->dev_addr[i];
- if (((*((int *)dev->dev_addr) & 0x00ffffff) == 0x95c000) &&
- (lp->chipset == DC21040)) {
- dev->irq = last.irq;
- }
+ dev->irq = last.irq;
+
status = 0;
}
} else if (!status) {
de4x5_us_delay(1);
i = (getfrom_srom(addr) >> 3) & 0x01;
- if (i != 0) {
- printk("Bad SROM address phase.....\n");
- }
return;
}
srom_init(struct device *dev)
{
struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset;
u_char count;
p+=2;
if (lp->chipset == DC21140) {
lp->cache.gepc = (*p++ | GEP_CTRL);
- outl(lp->cache.gepc, DE4X5_GEP);
+ gep_wr(lp->cache.gepc, dev);
}
/* Block count */
} else if (*(p+1) == 5) {
type5_infoblock(dev, 1, p);
p += ((*p & BLOCK_LEN) + 1);
+ } else if (*(p+1) == 4) {
+ p += ((*p & BLOCK_LEN) + 1);
} else if (*(p+1) == 3) {
type3_infoblock(dev, 1, p);
p += ((*p & BLOCK_LEN) + 1);
+ } else if (*(p+1) == 2) {
+ p += ((*p & BLOCK_LEN) + 1);
} else if (*(p+1) == 1) {
type1_infoblock(dev, 1, p);
p += ((*p & BLOCK_LEN) + 1);
return;
}
+/*
+** A generic routine that writes GEP control, data and reset information
+** to the GEP register (21140) or csr15 GEP portion (2114[23]).
+*/
static void
srom_exec(struct device *dev, u_char *p)
{
struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
u_long iobase = dev->base_addr;
u_char count = (p ? *p++ : 0);
+ u_short *w = (u_short *)p;
+
+ if (((lp->ibn != 1) && (lp->ibn != 3) && (lp->ibn != 5)) || !count) return;
+ if (lp->chipset != DC21140) RESET_SIA;
+
while (count--) {
- if (lp->chipset == DC21140) {
- outl(*p++, DE4X5_GEP);
- }
+ gep_wr(((lp->chipset==DC21140) && (lp->ibn!=5) ?
+ *p++ : TWIDDLE(w++)), dev);
udelay(2000); /* 2ms per action */
}
+ if (lp->chipset != DC21140) {
+ outl(lp->cache.csr14, DE4X5_STRR);
+ outl(lp->cache.csr13, DE4X5_SICR);
+ }
+
return;
}
static int
dc21142_infoleaf(struct device *dev)
{
-printk("dc21142_infoleaf()\n");
- return DE4X5_AUTOSENSE_MS;
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_char count = 0;
+ u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset;
+ int next_tick = DE4X5_AUTOSENSE_MS;
+
+ /* Read the connection type */
+ p+=2;
+
+ /* Block count */
+ count = *p++;
+
+ /* Recursively figure out the info blocks */
+ if (*p < 128) {
+ next_tick = dc_infoblock[COMPACT](dev, count, p);
+ } else {
+ next_tick = dc_infoblock[*(p+1)](dev, count, p);
+ }
+
+ if (lp->tcount == count) {
+ lp->media = NC;
+ if (lp->media != lp->c_media) {
+ de4x5_dbg_media(dev);
+ lp->c_media = lp->media;
+ }
+ lp->media = INIT;
+ lp->tcount = 0;
+ lp->tx_enable = FALSE;
+ }
+
+ return next_tick & ~TIMER_CB;
}
static int
dc21143_infoleaf(struct device *dev)
{
-printk("dc21143_infoleaf()\n");
- return DE4X5_AUTOSENSE_MS;
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_char count = 0;
+ u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset;
+ int next_tick = DE4X5_AUTOSENSE_MS;
+
+ /* Read the connection type */
+ p+=2;
+
+ /* Block count */
+ count = *p++;
+
+ /* Recursively figure out the info blocks */
+ if (*p < 128) {
+ next_tick = dc_infoblock[COMPACT](dev, count, p);
+ } else {
+ next_tick = dc_infoblock[*(p+1)](dev, count, p);
+ }
+ if (lp->tcount == count) {
+ lp->media = NC;
+ if (lp->media != lp->c_media) {
+ de4x5_dbg_media(dev);
+ lp->c_media = lp->media;
+ }
+ lp->media = INIT;
+ lp->tcount = 0;
+ lp->tx_enable = FALSE;
+ }
+
+ return next_tick & ~TIMER_CB;
}
/*
compact_infoblock(struct device *dev, u_char count, u_char *p)
{
struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
u_char flags, csr6;
/* Recursively figure out the info blocks */
}
if ((lp->media == INIT) && (lp->timeout < 0)) {
- outl(lp->cache.gepc, DE4X5_GEP);
+ lp->ibn = COMPACT;
+ lp->active = 0;
+ gep_wr(lp->cache.gepc, dev);
lp->infoblock_media = (*p++) & COMPACT_MC;
lp->cache.gep = *p++;
csr6 = *p++;
lp->defMedium = (flags & 0x40) ? -1 : 0;
lp->asBit = 1 << ((csr6 >> 1) & 0x07);
lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit;
- lp->infoblock_csr6 = (csr6 & 0x71) << 18;
+ lp->infoblock_csr6 = OMR_DEF | ((csr6 & 0x71) << 18);
lp->useMII = FALSE;
de4x5_switch_mac_port(dev);
/*
** This block describes non MII media for the DC21140[A] only.
- */
+*/
static int
type0_infoblock(struct device *dev, u_char count, u_char *p)
{
struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
u_char flags, csr6, len = (*p & BLOCK_LEN)+1;
/* Recursively figure out the info blocks */
}
if ((lp->media == INIT) && (lp->timeout < 0)) {
- outl(lp->cache.gepc, DE4X5_GEP);
+ lp->ibn = 0;
+ lp->active = 0;
+ gep_wr(lp->cache.gepc, dev);
p+=2;
lp->infoblock_media = (*p++) & BLOCK0_MC;
lp->cache.gep = *p++;
lp->defMedium = (flags & 0x40) ? -1 : 0;
lp->asBit = 1 << ((csr6 >> 1) & 0x07);
lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit;
- lp->infoblock_csr6 = (csr6 & 0x71) << 18;
+ lp->infoblock_csr6 = OMR_DEF | ((csr6 & 0x71) << 18);
lp->useMII = FALSE;
de4x5_switch_mac_port(dev);
p += 2;
if (lp->state == INITIALISED) {
- lp->ibn = 1;
lp->active = *p++;
lp->phy[lp->active].gep = (*p ? p : 0); p += (*p + 1);
lp->phy[lp->active].rst = (*p ? p : 0); p += (*p + 1);
} else if ((lp->media == INIT) && (lp->timeout < 0)) {
lp->ibn = 1;
lp->active = *p;
- lp->infoblock_csr6 = OMR_PS | OMR_HBD;
+ lp->infoblock_csr6 = OMR_MII_100;
lp->useMII = TRUE;
lp->infoblock_media = ANS;
+ lp->media = ANS;
de4x5_switch_mac_port(dev);
}
static int
type2_infoblock(struct device *dev, u_char count, u_char *p)
{
- return DE4X5_AUTOSENSE_MS;
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_char len = (*p & BLOCK_LEN)+1;
+
+ /* Recursively figure out the info blocks */
+ if (--count > lp->tcount) {
+ if (*(p+len) < 128) {
+ return dc_infoblock[COMPACT](dev, count, p+len);
+ } else {
+ return dc_infoblock[*(p+len+1)](dev, count, p+len);
+ }
+ }
+
+ if ((lp->media == INIT) && (lp->timeout < 0)) {
+ lp->ibn = 2;
+ lp->active = 0;
+ p += 2;
+ lp->infoblock_media = (*p) & MEDIA_CODE;
+
+ if ((*p++) & EXT_FIELD) {
+ lp->cache.csr13 = TWIDDLE(p); p += 2;
+ lp->cache.csr14 = TWIDDLE(p); p += 2;
+ lp->cache.csr15 = TWIDDLE(p); p += 2;
+ } else {
+ lp->cache.csr13 = CSR13;
+ lp->cache.csr14 = CSR14;
+ lp->cache.csr15 = CSR15;
+ }
+ lp->cache.gepc = ((s32)(TWIDDLE(p)) << 16); p += 2;
+ lp->cache.gep = ((s32)(TWIDDLE(p)) << 16);
+ lp->infoblock_csr6 = OMR_SIA;
+ lp->useMII = FALSE;
+
+ de4x5_switch_mac_port(dev);
+ }
+
+ return dc2114x_autoconf(dev);
}
static int
type3_infoblock(struct device *dev, u_char count, u_char *p)
{
struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_char flags, csr6, len = (*p & BLOCK_LEN)+1;
+ u_char len = (*p & BLOCK_LEN)+1;
/* Recursively figure out the info blocks */
if (--count > lp->tcount) {
}
}
+ p += 2;
if (lp->state == INITIALISED) {
- lp->ibn = 3; p += 2;
lp->active = *p++;
- lp->phy[lp->active].gep = (*p ? p : 0); p += (*p + 1);
- lp->phy[lp->active].rst = (*p ? p : 0); p += (*p + 1);
+ lp->phy[lp->active].gep = (*p ? p : 0); p += (2 * (*p) + 1);
+ lp->phy[lp->active].rst = (*p ? p : 0); p += (2 * (*p) + 1);
lp->phy[lp->active].mc = TWIDDLE(p); p += 2;
lp->phy[lp->active].ana = TWIDDLE(p); p += 2;
lp->phy[lp->active].fdx = TWIDDLE(p); p += 2;
- lp->phy[lp->active].ttm = TWIDDLE(p);
+ lp->phy[lp->active].ttm = TWIDDLE(p); p += 2;
+ lp->phy[lp->active].mci = *p;
return 0;
- } else if (lp->media == INIT) {
+ } else if ((lp->media == INIT) && (lp->timeout < 0)) {
+ lp->ibn = 3;
+ lp->active = *p;
+ lp->infoblock_csr6 = OMR_MII_100;
+ lp->useMII = TRUE;
+ lp->infoblock_media = ANS;
+
+ de4x5_switch_mac_port(dev);
+ }
+
+ return dc2114x_autoconf(dev);
+}
+
+static int
+type4_infoblock(struct device *dev, u_char count, u_char *p)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_char flags, csr6, len = (*p & BLOCK_LEN)+1;
+
+ /* Recursively figure out the info blocks */
+ if (--count > lp->tcount) {
+ if (*(p+len) < 128) {
+ return dc_infoblock[COMPACT](dev, count, p+len);
+ } else {
+ return dc_infoblock[*(p+len+1)](dev, count, p+len);
+ }
+ }
+
+ if ((lp->media == INIT) && (lp->timeout < 0)) {
+ lp->ibn = 4;
+ lp->active = 0;
p+=2;
- lp->infoblock_media = (*p++) & BLOCK0_MC;
- lp->cache.gep = *p++;
+ lp->infoblock_media = (*p++) & MEDIA_CODE;
+ lp->cache.csr13 = CSR13; /* Hard coded defaults */
+ lp->cache.csr14 = CSR14;
+ lp->cache.csr15 = CSR15;
+ lp->cache.gepc = ((s32)(TWIDDLE(p)) << 16); p += 2;
+ lp->cache.gep = ((s32)(TWIDDLE(p)) << 16); p += 2;
csr6 = *p++;
flags = *p++;
lp->defMedium = (flags & 0x40) ? -1 : 0;
lp->asBit = 1 << ((csr6 >> 1) & 0x07);
lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit;
- lp->infoblock_csr6 = (csr6 & 0x71) << 18;
- lp->useMII = TRUE;
+ lp->infoblock_csr6 = OMR_DEF | ((csr6 & 0x71) << 18);
+ lp->useMII = FALSE;
de4x5_switch_mac_port(dev);
}
- return dc21140m_autoconf(dev);
-}
-
-static int
-type4_infoblock(struct device *dev, u_char count, u_char *p)
-{
- return DE4X5_AUTOSENSE_MS;
+ return dc2114x_autoconf(dev);
}
/*
type5_infoblock(struct device *dev, u_char count, u_char *p)
{
struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
- u_char i, j, len = (*p & BLOCK_LEN)+1;
+ u_char len = (*p & BLOCK_LEN)+1;
/* Recursively figure out the info blocks */
if (--count > lp->tcount) {
if ((lp->state == INITIALISED) || (lp->media == INIT)) {
p+=2;
lp->rst = p;
- i = *p++;
- for (j=0;i;--i) {
- if (lp->chipset == DC21140) {
- if (!j) {
- outl(*p++ | GEP_CTRL, DE4X5_GEP);
- j++;
- }
- outl(*p++, DE4X5_GEP);
- } else if (lp->chipset == DC21142) {
- } else if (lp->chipset == DC21143) {
- }
- }
-
+ srom_exec(dev, lp->rst);
}
return DE4X5_AUTOSENSE_MS;
}
/*
-** Here's 3 ways to calculate the OUI from the ID registers. One's a brain
-** dead approach, 2 aren't (clue: mine isn't!).
+** Here's 3 ways to calculate the OUI from the ID registers.
*/
static int
mii_get_oui(u_char phyaddr, u_long ioaddr)
if (i==0) n++; /* Count cycles */
while (de4x5_reset_phy(dev)<0) udelay(100);/* Wait for reset */
id = mii_get_oui(i, DE4X5_MII);
- if ((id == 0) || (id == -1)) continue; /* Valid ID? */
+ if ((id == 0) || (id == 65535)) continue; /* Valid ID? */
for (j=0; j<limit; j++) { /* Search PHY table */
if (id != phy_info[j].id) continue; /* ID match? */
for (k=0; lp->phy[k].id && (k < DE4X5_MAX_PHY); k++);
lp->mii_cnt++;
lp->active++;
} else {
- i = DE4X5_MAX_MII; /* Stop the search */
- j = limit;
+ goto purgatory; /* Stop the search */
}
+ break;
+ }
+ if ((j == limit) && (i < DE4X5_MAX_MII)) {
+ printk("%s: Found MII device not currently supported. Please mail the following dump to\nthe author:\n", dev->name);
+ de4x5_debug |= DEBUG_MII;
+ de4x5_dbg_mii(dev, i);
+ printk("\n");
}
}
+ purgatory:
lp->active = 0;
if (lp->phy[0].id) { /* Reset the PHY devices */
for (k=0; lp->phy[k].id && (k < DE4X5_MAX_PHY); k++) { /*For each PHY*/
de4x5_dbg_mii(dev, k);
}
}
-
+ if (!lp->mii_cnt) lp->useMII = FALSE;
+
return lp->mii_cnt;
}
int iobase = dev->base_addr;
s32 omr;
+ STOP_DE4X5;
+
/* Assert the OMR_PS bit in CSR6 */
omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR |
OMR_FDX));
/* Soft Reset */
RESET_DE4X5;
- /* Restore the GEP */
+ /* Restore the GEP - especially for COMPACT and Type 0 Infoblocks */
if (lp->chipset == DC21140) {
- outl(lp->cache.gepc, DE4X5_GEP);
- outl(lp->cache.gep, DE4X5_GEP);
+ gep_wr(lp->cache.gepc, dev);
+ gep_wr(lp->cache.gep, dev);
+ } else if ((lp->chipset & ~0x0ff) == DC2114x) {
+ reset_init_sia(dev, lp->cache.csr13, lp->cache.csr14, lp->cache.csr15);
}
/* Restore CSR6 */
return omr;
}
+static void
+gep_wr(s32 data, struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ int iobase = dev->base_addr;
+
+ if (lp->chipset == DC21140) {
+ outl(data, DE4X5_GEP);
+ } else if ((lp->chipset & ~0x00ff) == DC2114x) {
+ outl((data<<16) | lp->cache.csr15, DE4X5_SIGR);
+ }
+
+ return;
+}
+
+static int
+gep_rd(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ int iobase = dev->base_addr;
+
+ if (lp->chipset == DC21140) {
+ return inl(DE4X5_GEP);
+ } else if ((lp->chipset & ~0x00ff) == DC2114x) {
+ return (inl(DE4X5_SIGR) & 0x000fffff);
+ }
+
+ return 0;
+}
+
static void
timeout(struct device *dev, void (*fn)(u_long data), u_long data, u_long msec)
{
if (lp->media != lp->c_media) {
if (de4x5_debug & DEBUG_MEDIA) {
- if (lp->chipset != DC21140) {
- printk("%s: media is %s\n", dev->name,
- (lp->media == NC ? "unconnected!" :
- (lp->media == TP ? "TP." :
- (lp->media == ANS ? "TP/Nway." :
- (lp->media == BNC ? "BNC." :
- (lp->media == AUI ? "AUI." :
- (lp->media == BNC_AUI ? "BNC/AUI." :
- (lp->media == EXT_SIA ? "EXT SIA." :
- "???."
- ))))))));
- } else {
- printk("%s: mode is %s\n", dev->name,
- (lp->media == NC ? "link down or incompatible connection.":
- (lp->media == _100Mb ? "100Mb/s." :
- (lp->media == _10Mb ? "10Mb/s." :
- "\?\?\?"
- ))));
- }
+ printk("%s: media is %s%s\n", dev->name,
+ (lp->media == NC ? "unconnected, link down or incompatible connection" :
+ (lp->media == TP ? "TP" :
+ (lp->media == ANS ? "TP/Nway" :
+ (lp->media == BNC ? "BNC" :
+ (lp->media == AUI ? "AUI" :
+ (lp->media == BNC_AUI ? "BNC/AUI" :
+ (lp->media == EXT_SIA ? "EXT SIA" :
+ (lp->media == _100Mb ? "100Mb/s" :
+ (lp->media == _10Mb ? "10Mb/s" :
+ "???"
+ ))))))))), (lp->fdx?" full duplex.":"."));
}
lp->c_media = lp->media;
}
/*
** Perform IOCTL call functions here. Some are privileged operations and the
-** effective uid is checked in those cases.
+** effective uid is checked in those cases. In the normal course of events
+** this function is only used for my testing.
*/
static int
de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd)
}
build_setup_frame(dev, PHYS_ADDR_ONLY);
/* Set up the descriptor and give ownership to the card */
- while (test_and_set_bit(0, (void *)&dev->tbusy) != 0);/* Wait for lock to free*/
+ while (test_and_set_bit(0, (void *)&dev->tbusy) != 0);
load_packet(dev, lp->setup_frame, TD_IC | PERFECT_F | TD_SET |
SETUP_FRAME_LEN, NULL);
lp->tx_new = (++lp->tx_new) % lp->txRingSize;
omr = inl(DE4X5_OMR);
omr |= OMR_PR;
outl(omr, DE4X5_OMR);
+ dev->flags |= IFF_PROMISC;
} else {
status = -EPERM;
}
omr = inl(DE4X5_OMR);
omr &= ~OMR_PR;
outb(omr, DE4X5_OMR);
+ dev->flags &= ~IFF_PROMISC;
} else {
status = -EPERM;
}
printk("%s: Boo!\n", dev->name);
break;
- case DE4X5_GET_MCA: /* Get the multicast address table */
- break;
- case DE4X5_SET_MCA: /* Set a multicast address */
- break;
- case DE4X5_CLR_MCA: /* Clear all multicast addresses */
- break;
- case DE4X5_MCA_EN: /* Enable pass all multicast addresses*/
+ case DE4X5_MCA_EN: /* Enable pass all multicast addressing */
if (suser()) {
omr = inl(DE4X5_OMR);
omr |= OMR_PM;
}
break;
-/*
-#define DE4X5_DUMP 0x0f / * Dump the DE4X5 Status * /
-
+#define DE4X5_DUMP 0x0f /* Dump the DE4X5 Status */
+/*
case DE4X5_DUMP:
j = 0;
tmp.addr[j++] = dev->irq;
tmp.lval[j>>2] = inl(DE4X5_IMR); j+=4;
tmp.lval[j>>2] = lp->chipset; j+=4;
if (lp->chipset == DC21140) {
- tmp.lval[j>>2] = inl(DE4X5_GEP); j+=4;
+ tmp.lval[j>>2] = gep_rd(dev); j+=4;
} else {
tmp.lval[j>>2] = inl(DE4X5_SISR); j+=4;
tmp.lval[j>>2] = inl(DE4X5_SICR); j+=4;
#define LP(a) ((struct de4x5_private *)(a))
static struct device *mdev = NULL;
static int io=0x0;/* EDIT THIS LINE FOR YOUR CONFIGURATION IF NEEDED */
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0)
+MODULE_PARM(io, "i");
+#endif /* LINUX_VERSION_CODE */
int
init_module(void)
{
+ int i, num, status = -EIO;
struct device *p;
- if ((mdev = insert_device(NULL, io, de4x5_probe)) == NULL)
- return -ENOMEM;
+ num = count_adapters();
+
+ for (i=0; i<num; i++) {
+ if ((p = insert_device(NULL, io, de4x5_probe)) == NULL)
+ return -ENOMEM;
+
+ if (!mdev) mdev = p;
- for (p = mdev; p != NULL; p = LP(p->priv)->next_module) {
- if (register_netdev(p) != 0)
- return -EIO;
+ if (register_netdev(p) != 0) {
+ kfree(p);
+ } else {
+ status = 0; /* At least one adapter will work */
+ lastModule = p;
+ }
}
- return 0;
+ return status;
}
void
return next;
}
+static int
+count_adapters(void)
+{
+ int i, j;
+ char name[DE4X5_STRLEN];
+ u_char pb, dev_fn, dev_num;
+ u_short dev_id, vendor;
+ u_int class = DE4X5_CLASS_CODE;
+ u_int device, iobase = 0x1000;
+
+ for (j=0, i=1; i<MAX_EISA_SLOTS; i++, iobase+=EISA_SLOT_INC) {
+ if (EISA_signature(name, EISA_ID)) j++;
+ }
+ if (!pcibios_present()) return j;
+
+ for (i=0;
+ (pcibios_find_class(class, i, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND);
+ i++) {
+ dev_num = PCI_SLOT(dev_fn);
+ device = 0;
+ pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor);
+ pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &dev_id);
+ device = dev_id;
+ device <<= 8;
+ if (is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x) j++;
+ }
+
+ return j;
+}
+
+/*
+** If at end of eth device list and can't use current entry, malloc
+** one up. If memory could not be allocated, print an error message.
+*/
+__initfunc(static struct device *
+insert_device(struct device *dev, u_long iobase, int (*init)(struct device *)))
+{
+ struct device *new;
+
+ new = (struct device *)kmalloc(sizeof(struct device)+8, GFP_KERNEL);
+ if (new == NULL) {
+ printk("de4x5.c: Device not initialised, insufficient memory\n");
+ return NULL;
+ } else {
+ memset((char *)new, 0, sizeof(struct device)+8);
+ new->name = (char *)(new + 1);
+ new->base_addr = iobase; /* assign the io address */
+ new->init = init; /* initialisation routine */
+ }
+
+ return new;
+}
+
#endif /* MODULE */
\f
* compile-command: "gcc -D__KERNEL__ -DMODULE -I/linux/include -Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strength-reduce -malign-loops=2 -malign-jumps=2 -malign-functions=2 -O2 -m486 -c de4x5.c"
* End:
*/
+
+
+
+
+
+
+
#define DC21140 DC21140_DID
#define DC2114x DC2114x_DID
#define DC21142 (DC2114x_DID | 0x0010)
-#define DC21143 (DC2114x_DID | 0x0020)
+#define DC21143 (DC2114x_DID | 0x0030)
#define is_DC21040 ((vendor == DC21040_VID) && (device == DC21040_DID))
#define is_DC21041 ((vendor == DC21041_VID) && (device == DC21041_DID))
#define TR_128 0x00008000 /* Threshold set to 128 (512) bytes */
#define TR_160 0x0000c000 /* Threshold set to 160 (1024) bytes */
+#define OMR_DEF (OMR_SDP)
+#define OMR_SIA (OMR_SDP | OMR_TTM)
+#define OMR_SYM (OMR_SDP | OMR_SCR | OMR_PCS | OMR_HBD | OMR_PS)
+#define OMR_MII_10 (OMR_SDP | OMR_TTM | OMR_PS)
+#define OMR_MII_100 (OMR_SDP | OMR_SCR | OMR_HBD | OMR_PS)
+
/*
** DC21040 Interrupt Mask Register (DE4X5_IMR)
*/
** DC21140 General Purpose Register (DE4X5_GEP) (hardware dependent bits)
*/
/* Valid ONLY for DE500 hardware */
-#define GEP_LNP 0x00000080 /* Link Pass (input) */
-#define GEP_SLNK 0x00000040 /* SYM LINK (input) */
-#define GEP_SDET 0x00000020 /* Signal Detect (input) */
-#define GEP_HRST 0x00000010 /* Hard RESET (to PHY) (output) */
-#define GEP_FDXD 0x00000008 /* Full Duplex Disable (output) */
-#define GEP_PHYL 0x00000004 /* PHY Loopback (output) */
-#define GEP_FLED 0x00000002 /* Force Activity LED on (output) */
-#define GEP_MODE 0x00000001 /* 0: 10Mb/s, 1: 100Mb/s */
-#define GEP_INIT 0x0000011f /* Setup inputs (0) and outputs (1) */
-#define GEP_CTRL 0x00000100 /* GEP control bit */
+#define GEP_LNP 0x00000080 /* Link Pass (input) */
+#define GEP_SLNK 0x00000040 /* SYM LINK (input) */
+#define GEP_SDET 0x00000020 /* Signal Detect (input) */
+#define GEP_HRST 0x00000010 /* Hard RESET (to PHY) (output) */
+#define GEP_FDXD 0x00000008 /* Full Duplex Disable (output) */
+#define GEP_PHYL 0x00000004 /* PHY Loopback (output) */
+#define GEP_FLED 0x00000002 /* Force Activity LED on (output) */
+#define GEP_MODE 0x00000001 /* 0: 10Mb/s, 1: 100Mb/s */
+#define GEP_INIT 0x0000011f /* Setup inputs (0) and outputs (1) */
+#define GEP_CTRL 0x00000100 /* GEP control bit */
+
+/*
+** SIA Register Defaults
+*/
+#define CSR13 0x00000001
+#define CSR14 0x0003ff7f /* Autonegotiation disabled */
+#define CSR15 0x00000008
/*
** SIA Status Register (DE4X5_SISR)
*/
-#define SISR_LPC 0xffff0000 /* Link Partner's Code Word */
-#define SISR_LPN 0x00008000 /* Link Partner Negotiable */
-#define SISR_ANS 0x00007000 /* Auto Negotiation Arbitration State */
-#define SISR_NSN 0x00000800 /* Non Stable NLPs Detected (DC21041) */
-#define SISR_TRF 0x00000800 /* Transmit Remote Fault */
-#define SISR_NSND 0x00000400 /* Non Stable NLPs Detected (DC21142) */
+#define SISR_LPC 0xffff0000 /* Link Partner's Code Word */
+#define SISR_LPN 0x00008000 /* Link Partner Negotiable */
+#define SISR_ANS 0x00007000 /* Auto Negotiation Arbitration State */
+#define SISR_NSN 0x00000800 /* Non Stable NLPs Detected (DC21041) */
+#define SISR_TRF 0x00000800 /* Transmit Remote Fault */
+#define SISR_NSND 0x00000400 /* Non Stable NLPs Detected (DC21142) */
#define SISR_ANR_FDS 0x00000400 /* Auto Negotiate Restart/Full Duplex Sel.*/
-#define SISR_TRA 0x00000200 /* 10BASE-T Receive Port Activity */
-#define SISR_NRA 0x00000200 /* Non Selected Port Receive Activity */
-#define SISR_ARA 0x00000100 /* AUI Receive Port Activity */
-#define SISR_SRA 0x00000100 /* Selected Port Receive Activity */
-#define SISR_DAO 0x00000080 /* PLL All One */
-#define SISR_DAZ 0x00000040 /* PLL All Zero */
-#define SISR_DSP 0x00000020 /* PLL Self-Test Pass */
-#define SISR_DSD 0x00000010 /* PLL Self-Test Done */
-#define SISR_APS 0x00000008 /* Auto Polarity State */
-#define SISR_LKF 0x00000004 /* Link Fail Status */
-#define SISR_NCR 0x00000002 /* Network Connection Error */
-#define SISR_PAUI 0x00000001 /* AUI_TP Indication */
-#define SISR_MRA 0x00000001 /* MII Receive Port Activity */
-
-#define ANS_NDIS 0x00000000 /* Nway disable */
-#define ANS_TDIS 0x00001000 /* Transmit Disable */
-#define ANS_ADET 0x00002000 /* Ability Detect */
-#define ANS_ACK 0x00003000 /* Acknowledge */
-#define ANS_CACK 0x00004000 /* Complete Acknowledge */
-#define ANS_NWOK 0x00005000 /* Nway OK - FLP Link Good */
-#define ANS_LCHK 0x00006000 /* Link Check */
+#define SISR_TRA 0x00000200 /* 10BASE-T Receive Port Activity */
+#define SISR_NRA 0x00000200 /* Non Selected Port Receive Activity */
+#define SISR_ARA 0x00000100 /* AUI Receive Port Activity */
+#define SISR_SRA 0x00000100 /* Selected Port Receive Activity */
+#define SISR_DAO 0x00000080 /* PLL All One */
+#define SISR_DAZ 0x00000040 /* PLL All Zero */
+#define SISR_DSP 0x00000020 /* PLL Self-Test Pass */
+#define SISR_DSD 0x00000010 /* PLL Self-Test Done */
+#define SISR_APS 0x00000008 /* Auto Polarity State */
+#define SISR_LKF 0x00000004 /* Link Fail Status */
+#define SISR_LS10 0x00000004 /* 10Mb/s Link Fail Status */
+#define SISR_NCR 0x00000002 /* Network Connection Error */
+#define SISR_LS100 0x00000002 /* 100Mb/s Link Fail Status */
+#define SISR_PAUI 0x00000001 /* AUI_TP Indication */
+#define SISR_MRA 0x00000001 /* MII Receive Port Activity */
+
+#define ANS_NDIS 0x00000000 /* Nway disable */
+#define ANS_TDIS 0x00001000 /* Transmit Disable */
+#define ANS_ADET 0x00002000 /* Ability Detect */
+#define ANS_ACK 0x00003000 /* Acknowledge */
+#define ANS_CACK 0x00004000 /* Complete Acknowledge */
+#define ANS_NWOK 0x00005000 /* Nway OK - FLP Link Good */
+#define ANS_LCHK 0x00006000 /* Link Check */
+
+#define SISR_RST 0x00000301 /* CSR12 reset */
+#define SISR_ANR 0x00001301 /* Autonegotiation restart */
/*
** SIA Connectivity Register (DE4X5_SICR)
#define DEBUG_OPEN 0x0040 /* Print de4x5_open() messages */
#define DEBUG_CLOSE 0x0080 /* Print de4x5_close() messages */
#define DEBUG_PCICFG 0x0100
+#define DEBUG_ALL 0x01ff
/*
** Miscellaneous
#define CLOSED 1 /* Ready for opening */
#define OPEN 2 /* Running */
+/*
+** Various wait times
+*/
+#define PDET_LINK_WAIT 1200 /* msecs to wait for link detect bits */
+#define ANS_FINISH_WAIT 1000 /* msecs to wait for link detect bits */
+
/*
** IEEE OUIs for various PHY vendor/chip combos - Reg 2 values only. Since
** the vendors seem split 50-50 on how to calculate the OUI register values
} else if (lp->useSROM && !lp->useMII) {\
omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\
omr |= (lp->fdx ? OMR_FDX : 0);\
- outl(omr | lp->infoblock_csr6, DE4X5_OMR);\
+ outl(omr | (lp->infoblock_csr6 & ~(OMR_SCR | OMR_HBD)), DE4X5_OMR);\
} else {\
omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\
omr |= (lp->fdx ? OMR_FDX : 0);\
} else if (lp->useSROM && !lp->useMII) {\
omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\
omr |= (lp->fdx ? OMR_FDX : 0);\
- outl(omr | lp->infoblock_csr6 | OMR_HBD, DE4X5_OMR);\
+ outl(omr | lp->infoblock_csr6, DE4X5_OMR);\
} else {\
omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\
omr |= (lp->fdx ? OMR_FDX : 0);\
#define DE4X5_GET_OMR 0x0c /* Get the OMR Register contents */
#define DE4X5_SET_OMR 0x0d /* Set the OMR Register contents */
#define DE4X5_GET_REG 0x0e /* Get the DE4X5 Registers */
+
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
DEVICE( MOTOROLA, MOTOROLA_MPC105,"MPC105 Eagle"),
DEVICE( MOTOROLA, MOTOROLA_MPC106,"MPC106 Grackle"),
DEVICE( MOTOROLA, MOTOROLA_RAVEN, "Raven"),
+ DEVICE( PROMISE, PROMISE_IDE_UDMA,"IDE Ultra DMA/33"),
DEVICE( PROMISE, PROMISE_5300, "DC5030"),
DEVICE( N9, N9_I128, "Imagine 128"),
DEVICE( N9, N9_I128_2, "Imagine 128v2"),
dep_tristate 'IOMEGA Parallel Port ZIP drive SCSI support' CONFIG_SCSI_PPA $CONFIG_SCSI $CONFIG_PARPORT
if [ "$CONFIG_SCSI_PPA" != "n" ]; then
int ' Pedantic EPP-checking' CONFIG_SCSI_PPA_HAVE_PEDANTIC 2 0 3
- int ' EPP timeout' CONFIG_SCSI_PPA_EPP_TIME 128
fi
fi
dep_tristate 'PAS16 SCSI support' CONFIG_SCSI_PAS16 $CONFIG_SCSI
* (c) 1995,1996 Grant R. Guenther, grant@torque.net,
* under the terms of the GNU Public License.
*
- */
-
-/* This driver was developed without the benefit of any technical
- * specifications for the interface. Instead, a modified version of
- * DOSemu was used to monitor the protocol used by the DOS driver
- * for this adapter. I have no idea how my programming model relates
- * to IOMEGA's design.
- *
- * IOMEGA's driver does not generate linked commands. I've never
- * observed a SCSI message byte in the protocol transactions, so
- * I am assuming that as long as linked commands are not used
- * we won't see any.
- *
- * For more information, see the file drivers/scsi/README.ppa.
- *
- */
-
-/*
- * this driver has been hacked by Matteo Frigo (athena@theory.lcs.mit.edu)
- * to support EPP and scatter-gather. [0.26-athena]
- *
- * additional hacks by David Campbell (campbell@tirian.che.curtin.edu.au)
- * in response to this driver "mis-behaving" on his machine.
- * Fixed EPP to handle "software" changing of EPP port data direction.
- * Chased down EPP timeouts
- * Made this driver "kernel version friendly" [0.28-athena]
- *
- * Really hacked it out of existance (David Campbell)
- * EPP timeouts no longer occur (yet better handling)
- * Probes known parallel ports
- * Autodetects EPP / ECP / PS2 / NIBBLE
- * Support for multiple devices (does anyone need this??)
- * [0.29-Curtin]
- * [ Stuff removed ]
- *
- * Modified PEDANTIC for less PEDANTIC drivers as people
- * were complaining about speed (I received a report indicating
- * that PEDANTIC is necessary for WinBond chipsets.
- * Updated config_ppa and Makefile
- * [0.36b-Curtin]
- *
- * First round of cleanups
- * Remove prior 1.3.34 kernel support
- * SMC support changed
- * ECP+EPP detection always invoked.
- * If compat mode => PS/2
- * else ecp_sync() called (ECP+EPP uses FIFO).
- * Added routine to detect interrupt channel for ECP (not used)
- * Changed version numbering
- * 1 Major number
- * 00 Minor revision number
- * ALPHA Expected stability (alpha, beta, stable)
- * [Curtin-1-00-ALPHA]
- * Second round of cleanups
- * Clean up timer queues
- * Fixed problem with non-detection of PS/2 ports
- * SMC ECP+EPP confirmed to work, remove option from config_ppa
- * [Curtin-1-01-ALPHA]
- *
- * Parport hits with a vengance!!
- * Several internal revisions have been made with huge amounts of
- * fixes including:
- * ioport_2_hostno removed (unique_id is quicker)
- * SMC compat option is history
- * Driver name / info hardwired
- * Played with inlines and saved 4k on module
- * Parport support
- * Using PnP Parport allows use of printer attached to
- * ZIP drive.
- * Numerous fixups for device registration and to allow
- * proper aborts.
- * Version jumps a few numbers here - considered BETA
- * Shipping Parport with monolithic driver :)
- * [Curtin-1-05-BETA]
- *
- * Fixed code to ensure SCSI abort will release the SCSI command
- * if the driver is STILL trying to claim the parport (PNP ver)
- * Now I have to fix the lp driver then there will NEVER be a
- * problem.
- * Got around to doing the ppa_queuecommand() clean up
- * Fixed bug relating to SMC EPP+ECP and monolithic driver
- * [Curtin-1-06-BETA]
- *
- * Where did the ppa_setup() code disappear to ??
- * Back in now...
- * Distribution of ppa now independent of parport (less work for me).
- * Also cleaned up the port detection to allow for variations on
- * IO aliasing (in an attempt to fix a few problems with some
- * machines...)
- * [Curtin-1-07-BETA]
- *
- * Rewrote detection code for monolithic driver and ported changes to
- * parport driver. Result is more stable detection of hardware and
- * better immunity to port aliasing (old XT cards).
- * Parport 0.16 (or better) is required for parport operation and
- * ECP+EPP modes, otherwise the latest parport edition is recommended.
- *
- * When using EPP and writing to disk CPU usage > 40%, while reading <10%.
- * This is due to ZIP drive IO scheduling, the drive does a verify after
- * write to ensure data integrity (removable media is ALWAYS questionable
- * since you never know where it has been).
- * Some fancy programing *MAY* fix the problem but at 30 Mb/min is just
- * over 10 sectors per jiffy.
- *
- * Hmm... I think I know a way but it will send the driver into
- * ALPHA state again.
- * [Curtin-1-08-STABLE]
+ * Current Maintainer: David Campbell (Perth, Western Australia, GMT+0800)
+ * campbell@gear.torque.net
+ * dcampbel@p01.as17.honeywell.com.au
*/
#include <linux/config.h>
/* The following #define is to avoid a clash with hosts.c */
#define PPA_CODE 1
-#include "ppa.h"
-/* batteries not included :-) */
-/*
- * modes in which the driver can operate
- */
-#define PPA_AUTODETECT 0 /* Autodetect mode */
-#define PPA_NIBBLE 1 /* work in standard 4 bit mode */
-#define PPA_PS2 2 /* PS/2 byte mode */
-#define PPA_EPP_8 3 /* EPP mode, 8 bit */
-#define PPA_EPP_16 4 /* EPP mode, 16 bit */
-#define PPA_EPP_32 5 /* EPP mode, 32 bit */
-#define PPA_UNKNOWN 6 /* Just in case... */
-
-static char *PPA_MODE_STRING[] =
-{
- "Autodetect",
- "SPP",
- "PS/2",
- "EPP 8 bit",
- "EPP 16 bit",
- "EPP 32 bit",
- "Unknown"};
+#include <linux/blk.h>
+#include <asm/io.h>
+#include <linux/parport.h>
+#include "sd.h"
+#include "hosts.h"
+int ppa_release(struct Scsi_Host *);
typedef struct {
- struct pardevice *dev; /* Parport device entry */
- int speed; /* General PPA delay constant */
- int speed_fast; /* Const for nibble/byte modes */
- int epp_speed; /* Reset time period */
- int mode; /* Transfer mode */
- int timeout; /* Number of timeouts */
- int host; /* Host number (for proc) */
- int abort_flag; /* Abort flag */
- int error_code; /* Error code */
- int ppa_failed; /* Failure flag */
- Scsi_Cmnd *cur_cmd; /* Current queued command */
- void (*done) (Scsi_Cmnd *); /* Done func for queuecommand */
- struct tq_struct ppa_tq; /* Polling interupt stuff */
- struct wait_queue *ppa_wait_q; /* Used for PnP stuff */
+ struct pardevice *dev; /* Parport device entry */
+ int base; /* Actual port address */
+ int mode; /* Transfer mode */
+ int host; /* Host number (for proc) */
+ Scsi_Cmnd *cur_cmd; /* Current queued command */
+ struct tq_struct ppa_tq; /* Polling interupt stuff */
+ unsigned long jstart; /* Jiffies at start */
+ unsigned int failed:1; /* Failure flag */
+ unsigned int p_busy:1; /* Parport sharing busy flag */
} ppa_struct;
-static void ppa_interrupt(void *data);
-/* I know that this is a mess but it works!! */
+#define PPA_EMPTY \
+{NULL, /* dev */ \
+-1, /* base */ \
+PPA_AUTODETECT, /* mode */ \
+-1, /* host */ \
+NULL, /* cur_cmd */ \
+{0, 0, ppa_interrupt, NULL}, \
+0, /* jstart */ \
+0, /* failed */ \
+0 /* p_busy */ \
+}
+
+#include "ppa.h"
+#include <linux/parport.h>
+
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#ifndef PARPORT_MODULES
+#define PARPORT_MODULES "parport_pc"
+#endif
+#endif
+
#define NO_HOSTS 4
static ppa_struct ppa_hosts[NO_HOSTS] =
-{
- {0, 6, 1, CONFIG_SCSI_PPA_EPP_TIME, PPA_AUTODETECT, 0, -1, 0, DID_ERROR, 1, NULL, NULL,
- {0, 0, ppa_interrupt, NULL}, NULL},
- {0, 6, 1, CONFIG_SCSI_PPA_EPP_TIME, PPA_AUTODETECT, 0, -1, 0, DID_ERROR, 1, NULL, NULL,
- {0, 0, ppa_interrupt, NULL}, NULL},
- {0, 6, 1, CONFIG_SCSI_PPA_EPP_TIME, PPA_AUTODETECT, 0, -1, 0, DID_ERROR, 1, NULL, NULL,
- {0, 0, ppa_interrupt, NULL}, NULL},
- {0, 6, 1, CONFIG_SCSI_PPA_EPP_TIME, PPA_AUTODETECT, 0, -1, 0, DID_ERROR, 1, NULL, NULL,
- {0, 0, ppa_interrupt, NULL}, NULL}
-};
-
-/* This is a global option */
-static int ppa_speed = -1; /* Set to >0 to act as a global value */
-static int ppa_speed_fast = -1; /* ditto.. */
-static int ppa_sg = SG_ALL; /* enable/disable scatter-gather. */
-
-/* other options */
-#define PPA_CAN_QUEUE 1 /* use "queueing" interface */
-#define PPA_BURST_SIZE 512 /* block size for bulk transfers */
-#define PPA_SELECT_TMO 5000 /* how long to wait for target ? */
-#define PPA_SPIN_TMO 500000 /* ppa_wait loop limiter */
-
-#define IN_EPP_MODE(x) (x == PPA_EPP_8 || x == PPA_EPP_16 || x == PPA_EPP_32)
-
-/* args to ppa_connect */
-#define CONNECT_EPP_MAYBE 1
-#define CONNECT_NORMAL 0
-
-#define PPA_BASE(x) ppa_hosts[(x)].dev->port->base
-
-/* Port IO - Sorry Grant but I prefer the following symbols */
-#define r_dtr(x) inb(PPA_BASE(x))
-#define r_str(x) inb(PPA_BASE(x)+1)
-#define r_ctr(x) inb(PPA_BASE(x)+2)
-#define r_epp(x) inb(PPA_BASE(x)+4)
-#define r_fifo(x) inb(PPA_BASE(x)+0x400)
-#define r_ecr(x) inb(PPA_BASE(x)+0x402)
-
-#define w_dtr(x,y) outb(y, PPA_BASE(x))
-#define w_str(x,y) outb(y, PPA_BASE(x)+1)
-#define w_ctr(x,y) outb(y, PPA_BASE(x)+2);\
- udelay( ppa_hosts[(x)].speed)
-#define w_epp(x,y) outb(y, PPA_BASE(x)+4)
-#define w_fifo(x,y) outb(y, PPA_BASE(x)+0x400)
-#define w_ecr(x,y) outb(y, PPA_BASE(x)+0x402)
-
-static void ppa_wakeup(void *ref)
-{
- ppa_struct *ppa_dev = (ppa_struct *) ref;
+{PPA_EMPTY, PPA_EMPTY, PPA_EMPTY, PPA_EMPTY};
- if (!ppa_dev->ppa_wait_q)
- return; /* Wake up whom ? */
+#define PPA_BASE(x) ppa_hosts[(x)].base
- /* Claim the Parport */
- if (parport_claim(ppa_dev->dev))
- return; /* Shouldn't happen */
+void ppa_wakeup(void *ref)
+{
+ ppa_struct *ppa_dev = (ppa_struct *) ref;
- wake_up(&ppa_dev->ppa_wait_q);
+ if (!ppa_dev->p_busy)
return;
+
+ if (parport_claim(ppa_dev->dev)) {
+ printk("ppa: bug in ppa_wakeup\n");
+ return;
+ }
+
+ ppa_dev->p_busy = 0;
+ ppa_dev->base = ppa_dev->dev->port->base;
+ if (ppa_dev->cur_cmd)
+ ppa_dev->cur_cmd->SCp.phase++;
+ return;
}
int ppa_release(struct Scsi_Host *host)
{
- int host_no = host->unique_id;
+ int host_no = host->unique_id;
- printk("Releasing ppa%i\n", host_no);
- parport_unregister_device(ppa_hosts[host_no].dev);
- return 0;
+ printk("Releasing ppa%i\n", host_no);
+ parport_unregister_device(ppa_hosts[host_no].dev);
+ return 0;
}
static int ppa_pb_claim(int host_no)
{
- if (parport_claim(ppa_hosts[host_no].dev)) {
- sleep_on(&ppa_hosts[host_no].ppa_wait_q);
- ppa_hosts[host_no].ppa_wait_q = NULL;
-
- /* Check to see if we were aborted or reset */
- if (ppa_hosts[host_no].dev->port->cad !=
- ppa_hosts[host_no].dev) {
- printk("Abort detected on ppa%i\n", host_no);
- return 1;
- }
- }
- return 0;
-}
+ if (parport_claim(ppa_hosts[host_no].dev)) {
+ ppa_hosts[host_no].p_busy = 1;
+ return 1;
+ }
-static void ppa_pb_release(int host_no)
-{
- parport_release(ppa_hosts[host_no].dev);
+ PPA_BASE(host_no) = ppa_hosts[host_no].dev->port->base;
+ if (ppa_hosts[host_no].cur_cmd)
+ ppa_hosts[host_no].cur_cmd->SCp.phase++;
+ return 0;
}
+#define ppa_pb_release(x) parport_release(ppa_hosts[(x)].dev)
+
+/***************************************************************************
+ * Parallel port probing routines *
+ ***************************************************************************/
+
+#ifdef MODULE
+Scsi_Host_Template driver_template = PPA;
+#include "scsi_module.c"
+#endif
+
+/*
+ * Start of Chipset kludges
+ */
-/* Placed here so everyone knows what ecp_sync does.. */
-static void ecp_sync(int host_no)
+int ppa_detect(Scsi_Host_Template * host)
{
- int i, r;
+ struct Scsi_Host *hreg;
+ int ports;
+ int i, nhosts, try_again;
+ struct parport *pb = parport_enumerate();
+
+ printk("ppa: Version %s\n", PPA_VERSION);
+ nhosts = 0;
+ try_again = 0;
+
+#ifdef CONFIG_KERNELD
+ if (!pb) {
+ request_module(PARPORT_MODULES);
+ pb = parport_enumerate();
+ }
+#endif
- r = r_ecr(host_no);
- if ((r & 0xe0) != 0x80)
- return;
+ if (!pb) {
+ printk("ppa: parport reports no devices.\n");
+ return 0;
+ }
+ retry_entry:
+ for (i = 0; pb; i++, pb = pb->next) {
+ int modes, ppb;
- for (i = 0; i < 100; i++) {
- r = r_ecr(host_no);
- if (r & 0x01)
- return;
- udelay(5);
- }
+ ppa_hosts[i].dev =
+ parport_register_device(pb, "ppa", NULL, ppa_wakeup,
+ NULL, PARPORT_DEV_TRAN, (void *) &ppa_hosts[i]);
- printk("ppa: ECP sync failed as data still present in FIFO.\n");
-}
+ /* Claim the bus so it remembers what we do to the control
+ * registers. [ CTR and ECP ]
+ */
+ if (ppa_pb_claim(i))
+ while (ppa_hosts[i].p_busy)
+ schedule(); /* Whe can safe schedule() here */
+ ppb = PPA_BASE(i);
+ w_ctr(ppb, 0x0c);
+ modes = ppa_hosts[i].dev->port->modes;
+
+ /* Mode detection works up the chain of speed
+ * This avoids a nasty if-then-else-if-... tree
+ */
+ ppa_hosts[i].mode = PPA_NIBBLE;
-static inline void ppa_d_pulse(int host_no, char b)
-{
- w_dtr(host_no, b);
- w_ctr(host_no, 0xc);
- w_ctr(host_no, 0xe);
- w_ctr(host_no, 0xc);
- w_ctr(host_no, 0x4);
- w_ctr(host_no, 0xc);
-}
+ if (modes & PARPORT_MODE_PCPS2)
+ ppa_hosts[i].mode = PPA_PS2;
-static void ppa_disconnect(int host_no)
-{
- ppa_d_pulse(host_no, 0);
- ppa_d_pulse(host_no, 0x3c);
- ppa_d_pulse(host_no, 0x20);
- ppa_d_pulse(host_no, 0xf);
+ if (modes & PARPORT_MODE_PCECPPS2) {
+ w_ecr(ppb, 0x20);
+ ppa_hosts[i].mode = PPA_PS2;
+ }
+ if (modes & PARPORT_MODE_PCECPEPP)
+ w_ecr(ppb, 0x80);
- ppa_pb_release(host_no);
-}
+ /* Done configuration */
+ ppa_pb_release(i);
-static inline void ppa_c_pulse(int host_no, char b)
-{
- w_dtr(host_no, b);
- w_ctr(host_no, 0x4);
- w_ctr(host_no, 0x6);
- w_ctr(host_no, 0x4);
- w_ctr(host_no, 0xc);
+ if (ppa_init(i)) {
+ parport_unregister_device(ppa_hosts[i].dev);
+ continue;
+ }
+ /* now the glue ... */
+ switch (ppa_hosts[i].mode) {
+ case PPA_NIBBLE:
+ ports = 3;
+ break;
+ case PPA_PS2:
+ ports = 3;
+ break;
+ case PPA_EPP_8:
+ case PPA_EPP_16:
+ case PPA_EPP_32:
+ ports = 8;
+ break;
+ default: /* Never gets here */
+ continue;
+ }
+
+ host->can_queue = PPA_CAN_QUEUE;
+ host->sg_tablesize = ppa_sg;
+ hreg = scsi_register(host, 0);
+ hreg->io_port = pb->base;
+ hreg->n_io_port = ports;
+ hreg->dma_channel = -1;
+ hreg->unique_id = i;
+ ppa_hosts[i].host = hreg->host_no;
+ nhosts++;
+ }
+ if (nhosts == 0) {
+ if (try_again == 1)
+ return 0;
+ try_again = 1;
+ goto retry_entry;
+ } else
+ return 1; /* return number of hosts detected */
}
-static int ppa_connect(int host_no, int flag)
+/* This is to give the ppa driver a way to modify the timings (and other
+ * parameters) by writing to the /proc/scsi/ppa/0 file.
+ * Very simple method really... (To simple, no error checking :( )
+ * Reason: Kernel hackers HATE having to unload and reload modules for
+ * testing...
+ * Also gives a method to use a script to obtain optimum timings (TODO)
+ */
+
+static inline int ppa_strncmp(const char *a, const char *b, int len)
{
- int retv = ppa_pb_claim(host_no);
-
- ppa_c_pulse(host_no, 0);
- ppa_c_pulse(host_no, 0x3c);
- ppa_c_pulse(host_no, 0x20);
- if ((flag == CONNECT_EPP_MAYBE) &&
- IN_EPP_MODE(ppa_hosts[host_no].mode))
- ppa_c_pulse(host_no, 0xcf);
- else
- ppa_c_pulse(host_no, 0x8f);
+ int loop;
+ for (loop = 0; loop < len; loop++)
+ if (a[loop] != b[loop])
+ return 1;
- return retv;
+ return 0;
}
-static void ppa_do_reset(int host_no)
+static inline int ppa_proc_write(int hostno, char *buffer, int length)
{
- /*
- * SCSI reset taken from ppa_init and checked with
- * Iomega document that Grant has (via email :(
- */
- ppa_pb_claim(host_no);
- ppa_disconnect(host_no);
-
- ppa_connect(host_no, CONNECT_NORMAL);
-
- w_ctr(host_no, 0x6);
- w_ctr(host_no, 0x4);
- w_dtr(host_no, 0x40);
- w_ctr(host_no, 0x8);
- udelay(50);
- w_ctr(host_no, 0xc);
-
- ppa_disconnect(host_no);
+ unsigned long x;
+
+ if ((length > 5) && (ppa_strncmp(buffer, "mode=", 5) == 0)) {
+ x = simple_strtoul(buffer + 5, NULL, 0);
+ ppa_hosts[hostno].mode = x;
+ return length;
+ }
+ printk("ppa /proc: invalid variable\n");
+ return (-EINVAL);
}
-static char ppa_select(int host_no, int initiator, int target)
+int ppa_proc_info(char *buffer, char **start, off_t offset,
+ int length, int hostno, int inout)
{
- char r;
- int k;
+ int i;
+ int len = 0;
- r = r_str(host_no); /* TODO */
+ for (i = 0; i < 4; i++)
+ if (ppa_hosts[i].host == hostno)
+ break;
- w_dtr(host_no, (1 << target));
- w_ctr(host_no, 0xe);
- w_ctr(host_no, 0xc);
- w_dtr(host_no, (1 << initiator));
- w_ctr(host_no, 0x8);
+ if (inout)
+ return ppa_proc_write(i, buffer, length);
- k = 0;
- while (!(r = (r_str(host_no) & 0xf0)) && (k++ < PPA_SELECT_TMO))
- barrier();
+ len += sprintf(buffer + len, "Version : %s\n", PPA_VERSION);
+ len += sprintf(buffer + len, "Parport : %s\n", ppa_hosts[i].dev->port->name);
+ len += sprintf(buffer + len, "Mode : %s\n", PPA_MODE_STRING[ppa_hosts[i].mode]);
- if (k >= PPA_SELECT_TMO)
- return 0;
+ /* Request for beyond end of buffer */
+ if (offset > length)
+ return 0;
- return r;
+ *start = buffer + offset;
+ len -= offset;
+ if (len > length)
+ len = length;
+ return len;
}
+/* end of ppa.c */
+static int device_check(int host_no);
-static void ppa_fail(int host_no, int error_code)
+#if PPA_DEBUG > 0
+#define ppa_fail(x,y) printk("ppa: ppa_fail(%i) from %s at line %d\n",\
+ y, __FUNCTION__, __LINE__); ppa_fail_func(x,y);
+static inline void ppa_fail_func(int host_no, int error_code)
+#else
+static inline void ppa_fail(int host_no, int error_code)
+#endif
{
- ppa_hosts[host_no].error_code = error_code;
- ppa_hosts[host_no].ppa_failed = 1;
- ppa_disconnect(host_no);
+ /* If we fail a device then we trash status / message bytes */
+ if (ppa_hosts[host_no].cur_cmd) {
+ ppa_hosts[host_no].cur_cmd->result = error_code << 16;
+ ppa_hosts[host_no].failed = 1;
+ }
}
/*
* doesn't appear to be designed to support interrupts. We spin on
* the 0x80 ready bit.
*/
-static char ppa_wait(int host_no)
+static unsigned char ppa_wait(int host_no)
{
- int k;
- char r;
-
- k = 0;
- while (!((r = r_str(host_no)) & 0x80)
- && (k++ < PPA_SPIN_TMO) && !ppa_hosts[host_no].abort_flag)
- barrier();
-
- /* check if we were interrupted */
- if (ppa_hosts[host_no].abort_flag) {
- if (ppa_hosts[host_no].abort_flag == 1)
- ppa_fail(host_no, DID_ABORT);
- else {
- ppa_do_reset(host_no);
- ppa_fail(host_no, DID_RESET);
- }
- return 0;
- }
- /* check if timed out */
- if (k >= PPA_SPIN_TMO) {
- ppa_fail(host_no, DID_TIME_OUT);
- return 0; /* command timed out */
- }
- /*
- * return some status information.
- * Semantics: 0xc0 = ZIP wants more data
- * 0xd0 = ZIP wants to send more data
- * 0xf0 = end of transfer, ZIP is sending status
- */
+ int k;
+ unsigned short ppb = PPA_BASE(host_no);
+ unsigned char r;
+
+ k = PPA_SPIN_TMO;
+ do {
+ r = r_str(ppb);
+ k--;
+ udelay(1);
+ }
+ while (!(r & 0x80) && (k));
+
+ /*
+ * return some status information.
+ * Semantics: 0xc0 = ZIP wants more data
+ * 0xd0 = ZIP wants to send more data
+ * 0xe0 = ZIP is expecting SCSI command data
+ * 0xf0 = end of transfer, ZIP is sending status
+ */
+ if (k)
return (r & 0xf0);
-}
+ /* Counter expired - Time out occured */
+ ppa_fail(host_no, DID_TIME_OUT);
+ printk("ppa timeout in ppa_wait\n");
+ return 0; /* command timed out */
+}
-/*
- * This is based on a trace of what the Iomega DOS 'guest' driver does.
- * I've tried several different kinds of parallel ports with guest and
- * coded this to react in the same ways that it does.
- *
- * The return value from this function is just a hint about where the
- * handshaking failed.
- *
+/*
+ * output a string, in whatever mode is available, according to the
+ * PPA protocol.
*/
-static int ppa_init(int host_no)
+static inline void epp_reset(unsigned short ppb)
{
- if (ppa_pb_claim(host_no))
- return 1;
- ppa_disconnect(host_no);
+ int i;
- ppa_connect(host_no, CONNECT_NORMAL);
+ i = r_str(ppb);
+ w_str(ppb, i);
+ w_str(ppb, i & 0xfe);
+}
- w_ctr(host_no, 0x6);
- if ((r_str(host_no) & 0xf0) != 0xf0) {
- ppa_pb_release(host_no);
- return 2;
- }
- w_ctr(host_no, 0x4);
- if ((r_str(host_no) & 0xf0) != 0x80) {
- ppa_pb_release(host_no);
- return 3;
- }
- /* This is a SCSI reset signal */
- w_dtr(host_no, 0x40);
- w_ctr(host_no, 0x8);
- udelay(50);
- w_ctr(host_no, 0xc);
+static inline void ecp_sync(unsigned short ppb)
+{
+ int i;
- ppa_disconnect(host_no);
+ if ((r_ecr(ppb) & 0xe0) != 0x80)
+ return;
- return 0;
+ for (i = 0; i < 100; i++) {
+ if (r_ecr(ppb) & 0x01)
+ return;
+ udelay(5);
+ }
+ printk("ppa: ECP sync failed as data still present in FIFO.\n");
}
-/*
- * check the epp status. After a EPP transfer, it should be true that
- * 1) the TIMEOUT bit (SPP_STR.0) is clear
- * 2) the READY bit (SPP_STR.7) is set
+/*
+ * Here is the asm code for the SPP/PS2 protocols for the i386.
+ * This has been optimised for speed on 386/486 machines. There will
+ * be very little improvement on the current 586+ machines as it is the
+ * IO statements which will limit throughput.
*/
-static int ppa_check_epp_status(int host_no)
+#ifdef __i386__
+#define BYTE_OUT(reg) \
+ " movb " #reg ",%%al\n" \
+ " outb %%al,(%%dx)\n" \
+ " addl $2,%%edx\n" \
+ " movb $0x0e,%%al\n" \
+ " outb %%al,(%%dx)\n" \
+ " movb $0x0c,%%al\n" \
+ " outb %%al,(%%dx)\n" \
+ " subl $2,%%edx\n"
+
+static inline int ppa_byte_out(unsigned short base, char *buffer, unsigned int len)
{
- char r;
- r = r_str(host_no);
-
- if (r & 1) {
- /* EPP timeout, according to the PC87332 manual */
- /* Semantics of clearing EPP timeout bit.
- * PC87332 - reading SPP_STR does it...
- * SMC - write 1 to EPP timeout bit
- * Others - (???) write 0 to EPP timeout bit
- */
- w_str(host_no, r);
- w_str(host_no, r & 0xfe);
- ppa_hosts[host_no].timeout++;
- printk("PPA: EPP timeout on port 0x%04x\n",
- PPA_BASE(host_no));
- ppa_fail(host_no, DID_BUS_BUSY);
- return 0;
- }
- if (!(r & 0x80)) {
- ppa_fail(host_no, DID_ERROR);
- return 0;
- }
- return 1;
+ /*
+ * %eax scratch
+ * %ebx Data to transfer
+ * %ecx Counter (Don't touch!!)
+ * %edx Port
+ * %esi Source buffer (mem pointer)
+ *
+ * In case you are wondering what the last line of the asm does...
+ * <output allocation> : <input allocation> : <trashed registers>
+ */
+ asm("shr $2,%%ecx\n" \
+ " jz .no_more_bulk_bo\n" \
+ " .align 4\n" \
+ ".loop_bulk_bo:\n" \
+ " movl (%%esi),%%ebx\n" \
+ BYTE_OUT(%%bl) \
+ BYTE_OUT(%%bh) \
+ " rorl $16,%%ebx\n" \
+ BYTE_OUT(%%bl) \
+ BYTE_OUT(%%bh) \
+ " addl $4,%%esi\n" \
+ " loop .loop_bulk_bo\n" \
+ " .align 4\n" \
+ ".no_more_bulk_bo:" \
+ : "=S"(buffer): "c"(len), "d"(base), "S"(buffer):"eax", "ebx", "ecx");
+
+ asm("andl $3,%%ecx\n" \
+ " jz .no_more_loose_bo\n" \
+ " .align 4\n" \
+ ".loop_loose_bo:\n" \
+ BYTE_OUT((%%esi)) \
+ " incl %%esi\n" \
+ " loop .loop_loose_bo\n" \
+ ".no_more_loose_bo:\n" \
+ : /* no output */ : "c"(len), "d"(base), "S"(buffer):"eax", "ebx", "ecx");
+ return 1; /* All went well - we hope! */
}
-static inline int ppa_force_epp_byte(int host_no, char x)
+#define BYTE_IN(reg) \
+ " inb (%%dx),%%al\n" \
+ " movb %%al," #reg "\n" \
+ " addl $2,%%edx\n" \
+ " movb $0x27,%%al\n" \
+ " outb %%al,(%%dx)\n" \
+ " movb $0x25,%%al\n" \
+ " outb %%al,(%%dx)\n" \
+ " subl $2,%%edx\n"
+
+static inline int ppa_byte_in(unsigned short base, char *buffer, int len)
{
-/* This routine forces a byte down the EPP data port whether the
- * device is ready or not...
- */
- char r;
-
- w_epp(host_no, x);
-
- r = r_str(host_no);
- if (!(r & 1))
- return 1;
-
- if (ppa_hosts[host_no].epp_speed > 0) {
- /* EPP timeout, according to the PC87332 manual */
- /* Semantics of clearing EPP timeout bit.
- * PC87332 - reading SPP_STR does it...
- * SMC - write 1 to EPP timeout bit
- * Others - (???) write 0 to EPP timeout bit
- */
- w_str(host_no, r);
- w_str(host_no, r & 0xfe);
-
- /* Take a deep breath, count to 10 and then... */
- udelay(ppa_hosts[host_no].epp_speed);
-
- /* Second time around */
- w_epp(host_no, x);
-
- r = r_str(host_no);
- }
- if (r & 1) {
- w_str(host_no, r);
- w_str(host_no, r & 0xfe);
- ppa_hosts[host_no].timeout++;
- printk("PPA: warning: EPP timeout\n");
- ppa_fail(host_no, DID_BUS_BUSY);
- return 0;
- } else
- return 1;
+ /*
+ * %eax scratch
+ * %ebx Data to transfer
+ * %ecx Counter (Don't touch!!)
+ * %edx Port
+ * %esi Source buffer (mem pointer)
+ *
+ * In case you are wondering what the last line of the asm does...
+ * <output allocation> : <input allocation> : <trashed registers>
+ */
+ asm("shr $2,%%ecx\n" \
+ " jz .no_more_bulk_bi\n" \
+ " .align 4\n" \
+ ".loop_bulk_bi:\n" \
+ BYTE_IN(%%bl) \
+ BYTE_IN(%%bh) \
+ " rorl $16,%%ebx\n" \
+ BYTE_IN(%%bl) \
+ BYTE_IN(%%bh) \
+ " rorl $16,%%ebx\n" \
+ " movl %%ebx,(%%esi)\n" \
+ " addl $4,%%esi\n" \
+ " loop .loop_bulk_bi\n" \
+ " .align 4\n" \
+ ".no_more_bulk_bi:" \
+ : "=S"(buffer): "c"(len), "d"(base), "S"(buffer):"eax", "ebx", "ecx");
+
+ asm("andl $3,%%ecx\n" \
+ " jz .no_more_loose_bi\n" \
+ " .align 4\n" \
+ ".loop_loose_bi:\n" \
+ BYTE_IN((%%esi)) \
+ " incl %%esi\n" \
+ " loop .loop_loose_bi\n" \
+ ".no_more_loose_bi:\n" \
+ : /* no output */ : "c"(len), "d"(base), "S"(buffer):"eax", "ebx", "ecx");
+ return 1; /* All went well - we hope! */
}
-static inline int ppa_send_command_epp(Scsi_Cmnd * cmd)
+#define NIBBLE_IN(reg) \
+ " incl %%edx\n" \
+ " movb $0x04,%%al\n" \
+ " outb %%al,(%%dx)\n" \
+ " decl %%edx\n" \
+ " inb (%%dx),%%al\n" \
+ " andb $0xf0,%%al\n" \
+ " movb %%al," #reg "\n" \
+ " incl %%edx\n" \
+ " movb $0x06,%%al\n" \
+ " outb %%al,(%%dx)\n" \
+ " decl %%edx\n" \
+ " inb (%%dx),%%al\n" \
+ " shrb $4,%%al\n" \
+ " orb %%al," #reg "\n"
+
+static inline int ppa_nibble_in(unsigned short str_p, char *buffer, int len)
{
- int host_no = cmd->host->unique_id;
- int k;
+ /*
+ * %eax scratch
+ * %ebx Data to transfer
+ * %ecx Counter (Don't touch!!)
+ * %edx Port
+ * %esi Source buffer (mem pointer)
+ *
+ * In case you are wondering what the last line of the asm does...
+ * <output allocation> : <input allocation> : <trashed registers>
+ */
+ asm("shr $2,%%ecx\n" \
+ " jz .no_more_bulk_ni\n" \
+ " .align 4\n" \
+ ".loop_bulk_ni:\n" \
+ NIBBLE_IN(%%bl) \
+ NIBBLE_IN(%%bh) \
+ " rorl $16,%%ebx\n" \
+ NIBBLE_IN(%%bl) \
+ NIBBLE_IN(%%bh) \
+ " rorl $16,%%ebx\n" \
+ " movl %%ebx,(%%esi)\n" \
+ " addl $4,%%esi\n" \
+ " loop .loop_bulk_ni\n" \
+ " .align 4\n" \
+ ".no_more_bulk_ni:" \
+ : "=S"(buffer): "c"(len), "d"(str_p), "S"(buffer):"eax", "ebx", "ecx");
+
+ asm("andl $3,%%ecx\n" \
+ " jz .no_more_loose_ni\n" \
+ " .align 4\n" \
+ ".loop_loose_ni:\n" \
+ NIBBLE_IN((%%esi)) \
+ " incl %%esi\n" \
+ " loop .loop_loose_ni\n" \
+ ".no_more_loose_ni:\n" \
+ : /* no output */ : "c"(len), "d"(str_p), "S"(buffer):"eax", "ebx", "ecx");
+ return 1; /* All went well - we hope! */
+}
+#else /* Old style C routines */
- w_ctr(host_no, 0x4);
- for (k = 0; k < cmd->cmd_len; k++) { /* send the command */
- if (!ppa_force_epp_byte(host_no, cmd->cmnd[k]))
- return 0;
- }
- w_ctr(host_no, 0xc);
- ecp_sync(host_no);
- return 1;
+static inline int ppa_byte_out(unsigned short base, const char *buffer, int len)
+{
+ unsigned short ctr_p = base + 2;
+ int i;
+
+ for (i = len; i; i--) {
+ outb(*buffer++, base);
+ outb(0xe, ctr_p);
+ outb(0xc, ctr_p);
+ }
+ return 1; /* All went well - we hope! */
}
-static inline int ppa_send_command_normal(Scsi_Cmnd * cmd)
+static inline int ppa_byte_in(unsigned short base, char *buffer, int len)
{
- int host_no = cmd->host->unique_id;
- int k;
+ unsigned short ctr_p = base + 2;
+ int i;
+
+ for (i = len; i; i--) {
+ *buffer++ = inb(base);
+ outb(0x27, ctr_p);
+ outb(0x25, ctr_p);
+ }
+ return 1; /* All went well - we hope! */
+}
- w_ctr(host_no, 0xc);
+static inline int ppa_nibble_in(unsigned short str_p, char *buffer, int len)
+{
+ unsigned short ctr_p = str_p + 1;
+ unsigned char h, l;
+ int i;
+
+ for (i = len; i; i--) {
+ outb(0x4, ctr_p);
+ h = inb(str_p);
+ outb(0x6, ctr_p);
+ l = inb(str_p);
+ *buffer++ = (h & 0xf0) | ((l & 0xf0) >> 4);
+ }
+ return 1; /* All went well - we hope! */
+}
+#endif
- for (k = 0; k < cmd->cmd_len; k++) { /* send the command */
- if (!ppa_wait(host_no))
- return 0;
- w_dtr(host_no, cmd->cmnd[k]);
- w_ctr(host_no, 0xe);
- w_ctr(host_no, 0xc);
- }
- return 1;
+static inline int ppa_epp_out(unsigned short epp_p, unsigned short str_p, const char *buffer, int len)
+{
+ int i;
+ for (i = len; i; i--) {
+ outb(*buffer++, epp_p);
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 2
+ if (inb(str_p) & 0x01)
+ return 0;
+#endif
+ }
+ return 1;
}
-static int ppa_start(Scsi_Cmnd * cmd)
+static int ppa_out(int host_no, char *buffer, int len)
{
- int host_no = cmd->host->unique_id;
+ int r;
+ unsigned short ppb = PPA_BASE(host_no);
- /*
- * by default, the command failed,
- * unless explicitly completed in ppa_completion().
- */
- ppa_hosts[host_no].error_code = DID_ERROR;
- ppa_hosts[host_no].abort_flag = 0;
- ppa_hosts[host_no].ppa_failed = 0;
-
- if (cmd->target == PPA_INITIATOR) {
- ppa_hosts[host_no].error_code = DID_BAD_TARGET;
- ppa_hosts[host_no].ppa_failed = 1;
- return 0;
- }
- if (ppa_connect(host_no, CONNECT_EPP_MAYBE))
- return 0;
+ r = ppa_wait(host_no);
- if (!ppa_select(host_no, PPA_INITIATOR, cmd->target)) {
- ppa_fail(host_no, DID_NO_CONNECT);
- return 0;
- }
- if (IN_EPP_MODE(ppa_hosts[host_no].mode))
- return ppa_send_command_epp(cmd);
+ if ((r & 0x50) != 0x40) {
+ ppa_fail(host_no, DID_ERROR);
+ return 0;
+ }
+ switch (ppa_hosts[host_no].mode) {
+ case PPA_NIBBLE:
+ case PPA_PS2:
+ /* 8 bit output, with a loop */
+ r = ppa_byte_out(ppb, buffer, len);
+ break;
+
+ case PPA_EPP_32:
+ case PPA_EPP_16:
+ case PPA_EPP_8:
+ epp_reset(ppb);
+ w_ctr(ppb, 0x4);
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 1
+ r = ppa_epp_out(ppb + 4, ppb + 1, buffer, len);
+#else
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC == 0
+ if (!(((long) buffer | len) & 0x03))
+ outsl(ppb + 4, buffer, len >> 2);
else
- return ppa_send_command_normal(cmd);
+#endif
+ outsb(ppb + 4, buffer, len);
+ w_ctr(ppb, 0xc);
+ r = !(r_str(ppb) & 0x01);
+#endif
+ w_ctr(ppb, 0xc);
+ ecp_sync(ppb);
+ break;
+
+ default:
+ printk("PPA: bug in ppa_out()\n");
+ r = 0;
+ }
+ return r;
}
-/*
- * output a string, in whatever mode is available, according to the
- * PPA protocol.
- */
-static inline int ppa_outs(int host_no, char *buffer, int len)
+static inline int ppa_epp_in(int epp_p, int str_p, char *buffer, int len)
{
- int k;
-#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 0
- int r;
+ int i;
+ for (i = len; i; i--) {
+ *buffer++ = inb(epp_p);
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 2
+ if (inb(str_p) & 0x01)
+ return 0;
#endif
+ }
+ return 1;
+}
- switch (ppa_hosts[host_no].mode) {
- case PPA_NIBBLE:
- case PPA_PS2:
- /* 8 bit output, with a loop */
- for (k = len; k; k--) {
- w_dtr(host_no, *buffer++);
- w_ctr(host_no, 0xe);
- w_ctr(host_no, 0xc);
- }
- return 1; /* assume transfer went OK */
+static int ppa_in(int host_no, char *buffer, int len)
+{
+ int r;
+ unsigned short ppb = PPA_BASE(host_no);
-#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 0
- case PPA_EPP_32:
-#if CONFIG_SCSI_PPA_HAVE_PEDANTIC < 2
- w_ctr(host_no, 0x4);
- for (k = len; k; k -= 4) {
- w_epp(host_no, *buffer++);
- w_epp(host_no, *buffer++);
- w_epp(host_no, *buffer++);
- w_epp(host_no, *buffer++);
- r = ppa_check_epp_status(host_no);
- if (!r)
- return r;
- }
- w_ctr(host_no, 0xc);
- ecp_sync(host_no);
- return 1;
-#endif
- case PPA_EPP_16:
-#if CONFIG_SCSI_PPA_HAVE_PEDANTIC < 3
- w_ctr(host_no, 0x4);
- for (k = len; k; k -= 2) {
- w_epp(host_no, *buffer++);
- w_epp(host_no, *buffer++);
- r = ppa_check_epp_status(host_no);
- if (!r)
- return r;
- }
- w_ctr(host_no, 0xc);
- ecp_sync(host_no);
- return 1;
-#endif
- case PPA_EPP_8:
- w_ctr(host_no, 0x4);
- for (k = len; k; k--) {
- w_epp(host_no, *buffer++);
- r = ppa_check_epp_status(host_no);
- if (!r)
- return r;
- }
- w_ctr(host_no, 0xc);
- ecp_sync(host_no);
- return 1;
+ r = ppa_wait(host_no);
+
+ if ((r & 0x50) != 0x50) {
+ ppa_fail(host_no, DID_ERROR);
+ return 0;
+ }
+ switch (ppa_hosts[host_no].mode) {
+ case PPA_NIBBLE:
+ /* 4 bit input, with a loop */
+ r = ppa_nibble_in(ppb + 1, buffer, len);
+ w_ctr(ppb, 0xc);
+ break;
+
+ case PPA_PS2:
+ /* 8 bit input, with a loop */
+ w_ctr(ppb, 0x25);
+ r = ppa_byte_in(ppb, buffer, len);
+ w_ctr(ppb, 0x4);
+ w_ctr(ppb, 0xc);
+ break;
+
+ case PPA_EPP_32:
+ case PPA_EPP_16:
+ case PPA_EPP_8:
+ epp_reset(ppb);
+ w_ctr(ppb, 0x24);
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 1
+ r = ppa_epp_in(ppb + 4, ppb + 1, buffer, len);
#else
- case PPA_EPP_32:
- case PPA_EPP_16:
- case PPA_EPP_8:
- w_ctr(host_no, 0x4);
- switch (ppa_hosts[host_no].mode) {
- case PPA_EPP_8:
- outsb(PPA_BASE(host_no) + 0x04,
- buffer, len);
- break;
- case PPA_EPP_16:
- outsw(PPA_BASE(host_no) + 0x04,
- buffer, len / 2);
- break;
- case PPA_EPP_32:
- outsl(PPA_BASE(host_no) + 0x04,
- buffer, len / 4);
- break;
- }
- k = ppa_check_epp_status(host_no);
- w_ctr(host_no, 0xc);
- ecp_sync(host_no);
- return k;
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC == 0
+ if (!(((long) buffer | len) & 0x03))
+ insl(ppb + 4, buffer, len >> 2);
+ else
+#endif
+ insb(ppb + 4, buffer, len);
+ w_ctr(ppb, 0x2c);
+ r = !(r_str(ppb) & 0x01);
#endif
+ w_ctr(ppb, 0x2c);
+ ecp_sync(ppb);
+ break;
+
+ default:
+ printk("PPA: bug in ppa_ins()\n");
+ r = 0;
+ break;
+ }
+ return r;
+}
- default:
- printk("PPA: bug in ppa_outs()\n");
- }
- return 0;
+/* end of ppa_io.h */
+static inline void ppa_d_pulse(unsigned short ppb, unsigned char b)
+{
+ w_dtr(ppb, b);
+ w_ctr(ppb, 0xc);
+ w_ctr(ppb, 0xe);
+ w_ctr(ppb, 0xc);
+ w_ctr(ppb, 0x4);
+ w_ctr(ppb, 0xc);
}
-static inline int ppa_outb(int host_no, char d)
+static void ppa_disconnect(int host_no)
{
- int k;
+ unsigned short ppb = PPA_BASE(host_no);
- switch (ppa_hosts[host_no].mode) {
- case PPA_NIBBLE:
- case PPA_PS2:
- w_dtr(host_no, d);
- w_ctr(host_no, 0xe);
- w_ctr(host_no, 0xc);
- return 1; /* assume transfer went OK */
+ ppa_d_pulse(ppb, 0);
+ ppa_d_pulse(ppb, 0x3c);
+ ppa_d_pulse(ppb, 0x20);
+ ppa_d_pulse(ppb, 0xf);
+}
- case PPA_EPP_8:
- case PPA_EPP_16:
- case PPA_EPP_32:
- w_ctr(host_no, 0x4);
- w_epp(host_no, d);
- k = ppa_check_epp_status(host_no);
- w_ctr(host_no, 0xc);
- ecp_sync(host_no);
- return k;
-
- default:
- printk("PPA: bug in ppa_outb()\n");
- }
+static inline void ppa_c_pulse(unsigned short ppb, unsigned char b)
+{
+ w_dtr(ppb, b);
+ w_ctr(ppb, 0x4);
+ w_ctr(ppb, 0x6);
+ w_ctr(ppb, 0x4);
+ w_ctr(ppb, 0xc);
+}
+
+static inline void ppa_connect(int host_no, int flag)
+{
+ unsigned short ppb = PPA_BASE(host_no);
+
+ ppa_c_pulse(ppb, 0);
+ ppa_c_pulse(ppb, 0x3c);
+ ppa_c_pulse(ppb, 0x20);
+ if ((flag == CONNECT_EPP_MAYBE) &&
+ IN_EPP_MODE(ppa_hosts[host_no].mode))
+ ppa_c_pulse(ppb, 0xcf);
+ else
+ ppa_c_pulse(ppb, 0x8f);
+}
+
+static int ppa_select(int host_no, int target)
+{
+ int k;
+ unsigned short ppb = PPA_BASE(host_no);
+
+ /*
+ * Bit 6 (0x40) is the device selected bit.
+ * First we must wait till the current device goes off line...
+ */
+ k = PPA_SELECT_TMO;
+ do {
+ k--;
+ } while ((r_str(ppb) & 0x40) && (k));
+ if (!k)
return 0;
+
+ w_dtr(ppb, (1 << target));
+ w_ctr(ppb, 0xe);
+ w_ctr(ppb, 0xc);
+ w_dtr(ppb, 0x80); /* This is NOT the initator */
+ w_ctr(ppb, 0x8);
+
+ k = PPA_SELECT_TMO;
+ do {
+ k--;
+ }
+ while (!(r_str(ppb) & 0x40) && (k));
+ if (!k)
+ return 0;
+
+ return 1;
}
-static inline int ppa_ins(int host_no, char *buffer, int len)
+/*
+ * This is based on a trace of what the Iomega DOS 'guest' driver does.
+ * I've tried several different kinds of parallel ports with guest and
+ * coded this to react in the same ways that it does.
+ *
+ * The return value from this function is just a hint about where the
+ * handshaking failed.
+ *
+ */
+static int ppa_init(int host_no)
{
- int k, h, l;
-#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 0
- int r;
+ int retv;
+ unsigned short ppb = PPA_BASE(host_no);
+
+#if defined(CONFIG_PARPORT) || defined(CONFIG_PARPORT_MODULE)
+ if (ppa_pb_claim(host_no))
+ while (ppa_hosts[host_no].p_busy)
+ schedule(); /* Whe can safe schedule() here */
#endif
- switch (ppa_hosts[host_no].mode) {
- case PPA_NIBBLE:
- /* 4 bit input, with a loop */
- for (k = len; k; k--) {
- w_ctr(host_no, 0x4);
- h = r_str(host_no);
- w_ctr(host_no, 0x6);
- l = r_str(host_no);
- *buffer++ = ((l >> 4) & 0x0f) + (h & 0xf0);
- }
- w_ctr(host_no, 0xc);
- return 1; /* assume transfer went OK */
+ ppa_disconnect(host_no);
+ ppa_connect(host_no, CONNECT_NORMAL);
- case PPA_PS2:
- /* 8 bit input, with a loop */
- for (k = len; k; k--) {
- w_ctr(host_no, 0x25);
- *buffer++ = r_dtr(host_no);
- w_ctr(host_no, 0x27);
- }
- w_ctr(host_no, 0x5);
- w_ctr(host_no, 0x4);
- w_ctr(host_no, 0xc);
- return 1; /* assume transfer went OK */
+ retv = 2; /* Failed */
-#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 0
- case PPA_EPP_32:
-#if CONFIG_SCSI_PPA_HAVE_PEDANTIC < 2
- w_ctr(host_no, 0x24);
- for (k = len; k; k -= 4) {
- *buffer++ = r_epp(host_no);
- *buffer++ = r_epp(host_no);
- *buffer++ = r_epp(host_no);
- *buffer++ = r_epp(host_no);
- r = ppa_check_epp_status(host_no);
- if (!r)
- return r;
- }
- w_ctr(host_no, 0x2c);
- ecp_sync(host_no);
- return 1;
-#endif
- case PPA_EPP_16:
-#if CONFIG_SCSI_PPA_HAVE_PEDANTIC < 3
- w_ctr(host_no, 0x24);
- for (k = len; k; k -= 2) {
- *buffer++ = r_epp(host_no);
- *buffer++ = r_epp(host_no);
- r = ppa_check_epp_status(host_no);
- if (!r)
- return r;
- }
- w_ctr(host_no, 0x2c);
- ecp_sync(host_no);
- return 1;
-#endif
- case PPA_EPP_8:
- w_ctr(host_no, 0x24);
- for (k = len; k; k--) {
- *buffer++ = r_epp(host_no);
- r = ppa_check_epp_status(host_no);
- if (!r)
- return r;
- }
- w_ctr(host_no, 0x2c);
- ecp_sync(host_no);
- return 1;
- break;
-#else
- case PPA_EPP_8:
- case PPA_EPP_16:
- case PPA_EPP_32:
- w_ctr(host_no, 0x24);
- switch (ppa_hosts[host_no].mode) {
- case PPA_EPP_8:
- insb(PPA_BASE(host_no) + 0x04,
- buffer, len);
- break;
- case PPA_EPP_16:
- insw(PPA_BASE(host_no) + 0x04,
- buffer, len / 2);
- break;
- case PPA_EPP_32:
- insl(PPA_BASE(host_no) + 0x04,
- buffer, len / 4);
- break;
- }
- k = ppa_check_epp_status(host_no);
- w_ctr(host_no, 0x2c);
- ecp_sync(host_no);
- return k;
-#endif
+ w_ctr(ppb, 0xe);
+ if ((r_str(ppb) & 0x08) == 0x08)
+ retv--;
- default:
- printk("PPA: bug in ppa_ins()\n");
- }
- return 0;
+ w_ctr(ppb, 0xc);
+ if ((r_str(ppb) & 0x08) == 0x00)
+ retv--;
+
+ /* This is a SCSI BUS reset signal */
+ if (!retv) {
+ w_dtr(ppb, 0x40);
+ w_ctr(ppb, 0x08);
+ udelay(30);
+ w_ctr(ppb, 0x0c);
+ udelay(1000); /* Allow devices to settle down */
+ }
+ ppa_disconnect(host_no);
+ udelay(1000); /* Another delay to allow devices to settle */
+
+ if (!retv)
+ retv = device_check(host_no);
+
+ ppa_pb_release(host_no);
+ return retv;
}
-static int ppa_inb(int host_no, char *buffer)
+static inline int ppa_send_command(Scsi_Cmnd * cmd)
{
- int h, l, k;
+ int host_no = cmd->host->unique_id;
+ int k;
- switch (ppa_hosts[host_no].mode) {
- case PPA_NIBBLE:
- /* 4 bit input */
- w_ctr(host_no, 0x4);
- h = r_str(host_no);
- w_ctr(host_no, 0x6);
- l = r_str(host_no);
- *buffer = ((l >> 4) & 0x0f) + (h & 0xf0);
- w_ctr(host_no, 0xc);
- return 1; /* assume transfer went OK */
+ w_ctr(PPA_BASE(host_no), 0x0c);
- case PPA_PS2:
- /* 8 bit input */
- w_ctr(host_no, 0x25);
- *buffer++ = r_dtr(host_no);
- w_ctr(host_no, 0x27);
- w_ctr(host_no, 0x5);
- w_ctr(host_no, 0x4);
- w_ctr(host_no, 0xc);
- return 1; /* assume transfer went OK */
-
- case PPA_EPP_8:
- case PPA_EPP_16:
- case PPA_EPP_32:
- w_ctr(host_no, 0x24);
- *buffer = r_epp(host_no);
- k = ppa_check_epp_status(host_no);
- w_ctr(host_no, 0xc);
- ecp_sync(host_no);
- return k;
-
- default:
- printk("PPA: bug in ppa_inb()\n");
- }
- return 0;
+ for (k = 0; k < cmd->cmd_len; k++)
+ if (!ppa_out(host_no, &cmd->cmnd[k], 1))
+ return 0;
+ return 1;
}
/*
*/
static int ppa_completion(Scsi_Cmnd * cmd)
{
- int host_no = cmd->host->unique_id;
-
- char r, l, h, v;
- int dir, cnt, blen, fast, bulk, status;
- char *buffer;
- struct scatterlist *sl;
- int current_segment, nsegment;
-
- v = cmd->cmnd[0];
- bulk = ((v == READ_6) ||
- (v == READ_10) ||
- (v == WRITE_6) ||
- (v == WRITE_10));
-
- /* code for scatter/gather: */
- if (cmd->use_sg) {
- /* if many buffers are available, start filling the first */
- sl = (struct scatterlist *) cmd->request_buffer;
- blen = sl->length;
- buffer = sl->address;
- } else {
- /* else fill the only available buffer */
- sl = NULL;
- buffer = cmd->request_buffer;
- blen = cmd->request_bufflen;
- }
- current_segment = 0;
- nsegment = cmd->use_sg;
-
- cnt = 0;
-
- /* detect transfer direction */
- dir = 0;
- if (!(r = ppa_wait(host_no)))
- return 0;
- if (r == (char) 0xc0)
- dir = 1; /* d0 = read c0 = write f0 = status */
-
- while (r != (char) 0xf0) {
- if (((r & 0xc0) != 0xc0) || (cnt >= blen)) {
- ppa_fail(host_no, DID_ERROR);
- return 0;
- }
- /* determine if we should use burst I/O */
- fast = (bulk && ((blen - cnt) >= PPA_BURST_SIZE) &&
- ((((long)buffer + cnt)) & 0x3) == 0);
-
- if (fast) {
- if (dir)
- status = ppa_outs(host_no, &buffer[cnt], PPA_BURST_SIZE);
- else
- status = ppa_ins(host_no, &buffer[cnt], PPA_BURST_SIZE);
- cnt += PPA_BURST_SIZE;
- } else {
- if (dir)
- status = ppa_outb(host_no, buffer[cnt]);
- else
- status = ppa_inb(host_no, &buffer[cnt]);
- cnt++;
- }
+ /* Return codes:
+ * -1 Error
+ * 0 Told to schedule
+ * 1 Finished data transfer
+ */
+ int host_no = cmd->host->unique_id;
+ unsigned short ppb = PPA_BASE(host_no);
+ unsigned long start_jiffies = jiffies;
+
+ unsigned char r, v;
+ int fast, bulk, status;
+
+ v = cmd->cmnd[0];
+ bulk = ((v == READ_6) ||
+ (v == READ_10) ||
+ (v == WRITE_6) ||
+ (v == WRITE_10));
+
+ /*
+ * We only get here if the drive is ready to comunicate,
+ * hence no need for a full ppa_wait.
+ */
+ r = (r_str(ppb) & 0xf0);
+
+ while (r != (unsigned char) 0xf0) {
+ /*
+ * If we have been running for more than a full timer tick
+ * then take a rest.
+ */
+ if (jiffies > start_jiffies + 1)
+ return 0;
- if (!status || !(r = ppa_wait(host_no)))
- return 0;
-
- if (sl && cnt == blen) {
- /* if scatter/gather, advance to the next segment */
- if (++current_segment < nsegment) {
- ++sl;
- blen = sl->length;
- buffer = sl->address;
- cnt = 0;
- }
- /*
- * the else case will be captured by the (cnt >= blen)
- * test above.
- */
- }
+ if (((r & 0xc0) != 0xc0) || (cmd->SCp.this_residual <= 0)) {
+ ppa_fail(host_no, DID_ERROR);
+ return -1; /* ERROR_RETURN */
}
+ /* determine if we should use burst I/O */ fast = (bulk && (cmd->SCp.this_residual >= PPA_BURST_SIZE))
+ ? PPA_BURST_SIZE : 1;
- /* read status and message bytes */
- if (!ppa_inb(host_no, &l)) /* read status byte */
- return 0;
- if (!(ppa_wait(host_no)))
- return 0;
- if (!ppa_inb(host_no, &h)) /* read message byte */
- return 0;
+ if (r == (unsigned char) 0xc0)
+ status = ppa_out(host_no, cmd->SCp.ptr, fast);
+ else
+ status = ppa_in(host_no, cmd->SCp.ptr, fast);
- ppa_disconnect(host_no);
+ cmd->SCp.ptr += fast;
+ cmd->SCp.this_residual -= fast;
- ppa_hosts[host_no].error_code = DID_OK;
- return (h << 8) | (l & STATUS_MASK);
+ if (!status) {
+ ppa_fail(host_no, DID_BUS_BUSY);
+ return -1; /* ERROR_RETURN */
+ }
+ if (cmd->SCp.buffer && !cmd->SCp.this_residual) {
+ /* if scatter/gather, advance to the next segment */
+ if (cmd->SCp.buffers_residual--) {
+ cmd->SCp.buffer++;
+ cmd->SCp.this_residual = cmd->SCp.buffer->length;
+ cmd->SCp.ptr = cmd->SCp.buffer->address;
+ }
+ }
+ /* Now check to see if the drive is ready to comunicate */
+ r = (r_str(ppb) & 0xf0);
+ /* If not, drop back down to the scheduler and wait a timer tick */
+ if (!(r & 0x80))
+ return 0;
+ }
+ return 1; /* FINISH_RETURN */
}
/* deprecated synchronous interface */
-
int ppa_command(Scsi_Cmnd * cmd)
{
- int host_no = cmd->host->unique_id;
- int s;
-
- sti();
- s = 0;
- if (ppa_start(cmd))
- if (ppa_wait(host_no))
- s = ppa_completion(cmd);
- return s + (ppa_hosts[host_no].error_code << 16);
+ static int first_pass = 1;
+ int host_no = cmd->host->unique_id;
+
+ if (first_pass) {
+ printk("ppa: using non-queuing interface\n");
+ first_pass = 0;
+ }
+ if (ppa_hosts[host_no].cur_cmd) {
+ printk("PPA: bug in ppa_command\n");
+ return 0;
+ }
+ ppa_hosts[host_no].failed = 0;
+ ppa_hosts[host_no].jstart = jiffies;
+ ppa_hosts[host_no].cur_cmd = cmd;
+ cmd->result = DID_ERROR << 16; /* default return code */
+ cmd->SCp.phase = 0;
+
+ ppa_pb_claim(host_no);
+
+ while (ppa_engine(&ppa_hosts[host_no], cmd))
+ schedule();
+
+ if (cmd->SCp.phase) /* Only disconnect if we have connected */
+ ppa_disconnect(cmd->host->unique_id);
+
+ ppa_pb_release(host_no);
+ ppa_hosts[host_no].cur_cmd = 0;
+ return cmd->result;
}
-/* pseudo-interrupt queueing interface */
/*
* Since the PPA itself doesn't generate interrupts, we use
* the scheduler's task queue to generate a stream of call-backs and
*/
static void ppa_interrupt(void *data)
{
- ppa_struct *tmp = (ppa_struct *) data;
- Scsi_Cmnd *cmd = tmp->cur_cmd;
- void (*done) (Scsi_Cmnd *) = tmp->done;
- int host_no = cmd->host->unique_id;
-
- if (!cmd) {
- printk("PPA: bug in ppa_interrupt\n");
- return;
- }
- /* First check for any errors that may of occured
- * Here we check for internal errors
- */
- if (tmp->ppa_failed) {
- printk("PPA: ppa_failed bug: ppa_error_code = %d\n",
- tmp->error_code);
- cmd->result = DID_ERROR << 16;
- tmp->cur_cmd = 0;
- done(cmd);
- return;
+ ppa_struct *tmp = (ppa_struct *) data;
+ Scsi_Cmnd *cmd = tmp->cur_cmd;
+
+ if (!cmd) {
+ printk("PPA: bug in ppa_interrupt\n");
+ return;
+ }
+ if (ppa_engine(tmp, cmd)) {
+ tmp->ppa_tq.data = (void *) tmp;
+ tmp->ppa_tq.sync = 0;
+ queue_task(&tmp->ppa_tq, &tq_timer);
+ return;
+ }
+ /* Command must of completed hence it is safe to let go... */
+#if PPA_DEBUG > 0
+ switch ((cmd->result >> 16) & 0xff) {
+ case DID_OK:
+ break;
+ case DID_NO_CONNECT:
+ printk("ppa: no device at SCSI ID %i\n", cmd->target);
+ break;
+ case DID_BUS_BUSY:
+ printk("ppa: BUS BUSY - EPP timeout detected\n");
+ break;
+ case DID_TIME_OUT:
+ printk("ppa: unknown timeout\n");
+ break;
+ case DID_ABORT:
+ printk("ppa: told to abort\n");
+ break;
+ case DID_PARITY:
+ printk("ppa: parity error (???)\n");
+ break;
+ case DID_ERROR:
+ printk("ppa: internal driver error\n");
+ break;
+ case DID_RESET:
+ printk("ppa: told to reset device\n");
+ break;
+ case DID_BAD_INTR:
+ printk("ppa: bad interrupt (???)\n");
+ break;
+ default:
+ printk("ppa: bad return code (%02x)\n", (cmd->result >> 16) & 0xff);
+ }
+#endif
+
+ ppa_disconnect(cmd->host->unique_id);
+ ppa_pb_release(cmd->host->unique_id);
+ tmp->cur_cmd = 0;
+ cmd->scsi_done(cmd);
+ return;
+}
+
+static int ppa_engine(ppa_struct * tmp, Scsi_Cmnd * cmd)
+{
+ int host_no = cmd->host->unique_id;
+ unsigned short ppb = PPA_BASE(host_no);
+ unsigned char l = 0, h = 0;
+ int retv;
+
+ /* First check for any errors that may of occured
+ * Here we check for internal errors
+ */
+ if (tmp->failed)
+ return 0;
+
+ switch (cmd->SCp.phase) {
+ case 0: /* Phase 0 - Waiting for parport */
+ if ((jiffies - tmp->jstart) > HZ) {
+ /*
+ * We waited more than a second
+ * for parport to call us
+ */
+ ppa_fail(host_no, DID_BUS_BUSY);
+ return 0;
}
- /* Occasionally the mid level driver will abort a SCSI
- * command because we are taking to long, if this occurs
- * we should abort the command.
- */
- if (tmp->abort_flag) {
- ppa_disconnect(host_no);
- if (tmp->abort_flag == 1)
- cmd->result = DID_ABORT << 16;
- else {
- ppa_do_reset(host_no);
- cmd->result = DID_RESET << 16;
+ return 1; /* wait that ppa_wakeup claims parport */
+ case 1: /* Phase 1 - Connected */
+ { /* Perform a sanity check for cable unplugged */
+ int retv = 2; /* Failed */
+
+ ppa_connect(host_no, CONNECT_EPP_MAYBE);
+
+ w_ctr(ppb, 0xe);
+ if ((r_str(ppb) & 0x08) == 0x08)
+ retv--;
+
+ w_ctr(ppb, 0xc);
+ if ((r_str(ppb) & 0x08) == 0x00)
+ retv--;
+
+ if (retv)
+ if ((jiffies - tmp->jstart) > (1 * HZ)) {
+ printk("ppa: Parallel port cable is unplugged!!\n");
+ ppa_fail(host_no, DID_BUS_BUSY);
+ return 0;
+ } else {
+ ppa_disconnect(host_no);
+ return 1; /* Try again in a jiffy */
}
- tmp->cur_cmd = 0;
- done(cmd);
- return;
+ cmd->SCp.phase++;
}
- /* Check to see if the device is now free, if not
- * then throw this function onto the scheduler queue
- * to be called back in a jiffy.
- * (i386: 1 jiffy = 0.01 seconds)
- */
- if (!(r_str(host_no) & 0x80)) {
- tmp->ppa_tq.data = (void *) tmp;
- queue_task(&tmp->ppa_tq, &tq_scheduler);
- return;
+
+ case 2: /* Phase 2 - We are now talking to the scsi bus */
+ if (!ppa_select(host_no, cmd->target)) {
+ ppa_fail(host_no, DID_NO_CONNECT);
+ return 0;
}
- /* Device is now free and no errors have occured so
- * it is safe to do the data phase
- */
- cmd->result = ppa_completion(cmd) + (tmp->error_code << 16);
- tmp->cur_cmd = 0;
- done(cmd);
- return;
-}
+ cmd->SCp.phase++;
-int ppa_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
-{
- int host_no = cmd->host->unique_id;
+ case 3: /* Phase 3 - Ready to accept a command */
+ w_ctr(ppb, 0x0c);
+ if (!(r_str(ppb) & 0x80))
+ return 1;
+
+ if (!ppa_send_command(cmd))
+ return 0;
+ cmd->SCp.phase++;
- if (ppa_hosts[host_no].cur_cmd) {
- printk("PPA: bug in ppa_queuecommand\n");
- return 0;
+ case 4: /* Phase 4 - Setup scatter/gather buffers */
+ if (cmd->use_sg) {
+ /* if many buffers are available, start filling the first */
+ cmd->SCp.buffer = (struct scatterlist *) cmd->request_buffer;
+ cmd->SCp.this_residual = cmd->SCp.buffer->length;
+ cmd->SCp.ptr = cmd->SCp.buffer->address;
+ } else {
+ /* else fill the only available buffer */
+ cmd->SCp.buffer = NULL;
+ cmd->SCp.this_residual = cmd->request_bufflen;
+ cmd->SCp.ptr = cmd->request_buffer;
+ }
+ cmd->SCp.buffers_residual = cmd->use_sg;
+ cmd->SCp.phase++;
+
+ case 5: /* Phase 5 - Data transfer stage */
+ w_ctr(ppb, 0x0c);
+ if (!(r_str(ppb) & 0x80))
+ return 1;
+
+ retv = ppa_completion(cmd);
+ if (retv == -1)
+ return 0;
+ if (retv == 0)
+ return 1;
+ cmd->SCp.phase++;
+
+ case 6: /* Phase 6 - Read status/message */
+ cmd->result = DID_OK << 16;
+ /* Check for data overrun */
+ if (ppa_wait(host_no) != (unsigned char) 0xf0) {
+ ppa_fail(host_no, DID_ERROR);
+ return 0;
}
- sti();
- ppa_hosts[host_no].cur_cmd = cmd;
- ppa_hosts[host_no].done = done;
-
- if (!ppa_start(cmd)) {
- cmd->result = ppa_hosts[host_no].error_code << 16;
- ppa_hosts[host_no].cur_cmd = 0;
- done(cmd);
- return 0;
+ if (ppa_in(host_no, &l, 1)) { /* read status byte */
+ /* Check for optional message byte */
+ if (ppa_wait(host_no) == (unsigned char) 0xf0)
+ ppa_in(host_no, &h, 1);
+ cmd->result = (DID_OK << 16) + (h << 8) + (l & STATUS_MASK);
}
- ppa_interrupt(ppa_hosts + host_no);
+ return 0; /* Finished */
+ break;
+ default:
+ printk("ppa: Invalid scsi phase\n");
+ }
+ return 0;
+}
+
+int ppa_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
+{
+ int host_no = cmd->host->unique_id;
+
+ if (ppa_hosts[host_no].cur_cmd) {
+ printk("PPA: bug in ppa_queuecommand\n");
return 0;
+ }
+ ppa_hosts[host_no].failed = 0;
+ ppa_hosts[host_no].jstart = jiffies;
+ ppa_hosts[host_no].cur_cmd = cmd;
+ cmd->scsi_done = done;
+ cmd->result = DID_ERROR << 16; /* default return code */
+ cmd->SCp.phase = 0; /* bus free */
+
+ ppa_pb_claim(host_no);
+
+ ppa_hosts[host_no].ppa_tq.data = ppa_hosts + host_no;
+ ppa_hosts[host_no].ppa_tq.sync = 0;
+ queue_task(&ppa_hosts[host_no].ppa_tq, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+
+ return 0;
}
/*
*/
int ppa_biosparam(Disk * disk, kdev_t dev, int ip[])
{
- ip[0] = 0x40;
- ip[1] = 0x20;
+ ip[0] = 0x40;
+ ip[1] = 0x20;
+ ip[2] = (disk->capacity + 1) / (ip[0] * ip[1]);
+ if (ip[2] > 1024) {
+ ip[0] = 0xff;
+ ip[1] = 0x3f;
ip[2] = (disk->capacity + 1) / (ip[0] * ip[1]);
- if (ip[2] > 1024) {
- ip[0] = 0xff;
- ip[1] = 0x3f;
- ip[2] = (disk->capacity + 1) / (ip[0] * ip[1]);
- if (ip[2] > 1023)
- ip[2] = 1023;
- }
- return 0;
+ if (ip[2] > 1023)
+ ip[2] = 1023;
+ }
+ return 0;
}
int ppa_abort(Scsi_Cmnd * cmd)
{
- int host_no = cmd->host->unique_id;
-
- ppa_hosts[host_no].abort_flag = 1;
- ppa_hosts[host_no].error_code = DID_ABORT;
- if (ppa_hosts[host_no].ppa_wait_q)
- wake_up(&ppa_hosts[host_no].ppa_wait_q);
-
- return SCSI_ABORT_SNOOZE;
+ /*
+ * There is no method for aborting commands since Iomega
+ * have tied the SCSI_MESSAGE line high in the interface
+ */
+
+ switch (cmd->SCp.phase) {
+ case 0: /* Do not have access to parport */
+ case 1: /* Have not connected to interface */
+ cmd->result = DID_ABORT;
+ cmd->done(cmd);
+ return SCSI_ABORT_SUCCESS;
+ break;
+ default: /* SCSI command sent, can not abort */
+ return SCSI_ABORT_BUSY;
+ break;
+ }
}
int ppa_reset(Scsi_Cmnd * cmd, unsigned int x)
{
- int host_no = cmd->host->unique_id;
-
- ppa_hosts[host_no].abort_flag = 2;
- ppa_hosts[host_no].error_code = DID_RESET;
- if (ppa_hosts[host_no].ppa_wait_q)
- wake_up(&ppa_hosts[host_no].ppa_wait_q);
-
- return SCSI_RESET_PUNT;
+ int host_no = cmd->host->unique_id;
+ int ppb = PPA_BASE(host_no);
+
+ /*
+ * PHASE1:
+ * Bring the interface crashing down on whatever is running
+ * hopefully this will kill the request.
+ * Bring back up the interface, reset the drive (and anything
+ * attached for that manner)
+ */
+ if (cmd)
+ if (cmd->SCp.phase)
+ ppa_disconnect(cmd->host->unique_id);
+
+ ppa_connect(host_no, CONNECT_NORMAL);
+ w_dtr(ppb, 0x40);
+ w_ctr(ppb, 0x8);
+ udelay(30);
+ w_ctr(ppb, 0xc);
+ udelay(1000); /* delay for devices to settle down */
+ ppa_disconnect(host_no);
+ udelay(1000); /* Additional delay to allow devices to settle down */
+
+ /*
+ * PHASE2:
+ * Sanity check for the sake of mid-level driver
+ */
+ if (!cmd) {
+ printk("ppa bus reset called for invalid command.\n");
+ return SCSI_RESET_NOT_RUNNING;
+ }
+ /*
+ * PHASE3:
+ * Flag the current command as having died due to reset
+ */
+ ppa_connect(host_no, CONNECT_NORMAL);
+ ppa_fail(host_no, DID_RESET);
+
+ /* Since the command was already on the timer queue ppa_interrupt
+ * will be called shortly.
+ */
+ return SCSI_RESET_PENDING;
}
-
-
-/***************************************************************************
- * Parallel port probing routines *
- ***************************************************************************/
-
-
-#ifdef MODULE
-Scsi_Host_Template driver_template = PPA;
-#include "scsi_module.c"
-#endif
-
-/*
- * Start of Chipset kludges
- */
-
-int ppa_detect(Scsi_Host_Template * host)
-{
- struct Scsi_Host *hreg;
- int rs;
- int ports;
- int i, nhosts;
- struct parport *pb = parport_enumerate();
-
- printk("PPA driver version: %s\n", PPA_VERSION);
- nhosts = 0;
-
- for (i = 0; pb; i++, pb=pb->next) {
- int modes = pb->modes;
-
- /* We only understand PC-style ports */
- if (modes & PARPORT_MODE_PCSPP) {
-
- /* transfer global values here */
- if (ppa_speed >= 0)
- ppa_hosts[i].speed = ppa_speed;
- if (ppa_speed_fast >= 0)
- ppa_hosts[i].speed_fast = ppa_speed_fast;
-
- ppa_hosts[i].dev = parport_register_device(pb, "ppa",
- NULL, ppa_wakeup, NULL,
- PARPORT_DEV_TRAN, (void *) &ppa_hosts[i]);
-
- /* Claim the bus so it remembers what we do to the
- * control registers. [ CTR and ECP ]
- */
- ppa_pb_claim(i);
- w_ctr(i, 0x0c);
-
- ppa_hosts[i].mode = PPA_NIBBLE;
- if (modes & (PARPORT_MODE_PCEPP | PARPORT_MODE_PCECPEPP)) {
- ppa_hosts[i].mode = PPA_EPP_32;
- printk("PPA: Parport [ PCEPP ]\n");
- } else if (modes & PARPORT_MODE_PCECP) {
- w_ecr(i, 0x20);
- ppa_hosts[i].mode = PPA_PS2;
- printk("PPA: Parport [ PCECP in PS2 submode ]\n");
- } else if (modes & PARPORT_MODE_PCPS2) {
- ppa_hosts[i].mode = PPA_PS2;
- printk("PPA: Parport [ PCPS2 ]\n");
- }
- /* Done configuration */
- ppa_pb_release(i);
-
- rs = ppa_init(i);
- if (rs) {
- parport_unregister_device(ppa_hosts[i].dev);
- continue;
- }
- /* now the glue ... */
- switch (ppa_hosts[i].mode) {
- case PPA_NIBBLE:
- case PPA_PS2:
- ports = 3;
- break;
- case PPA_EPP_8:
- case PPA_EPP_16:
- case PPA_EPP_32:
- ports = 8;
- break;
- default: /* Never gets here */
- continue;
- }
-
- host->can_queue = PPA_CAN_QUEUE;
- host->sg_tablesize = ppa_sg;
- hreg = scsi_register(host, 0);
- hreg->io_port = pb->base;
- hreg->n_io_port = ports;
- hreg->dma_channel = -1;
- hreg->unique_id = i;
- ppa_hosts[i].host = hreg->host_no;
- nhosts++;
- }
- }
- if (nhosts == 0)
- return 0;
- else
- return 1; /* return number of hosts detected */
-}
-
-/* This is to give the ppa driver a way to modify the timings (and other
- * parameters) by writing to the /proc/scsi/ppa/0 file.
- * Very simple method really... (To simple, no error checking :( )
- * Reason: Kernel hackers HATE having to unload and reload modules for
- * testing...
- * Also gives a method to use a script to obtain optimum timings (TODO)
- */
-
-static int ppa_strncmp(const char *a, const char *b, int len)
+static int device_check(int host_no)
{
- int loop;
- for (loop = 0; loop < len; loop++)
- if (a[loop] != b[loop])
- return 1;
-
- return 0;
-}
-
-static int ppa_proc_write(int hostno, char *buffer, int length)
-{
- unsigned long x;
- const char *inv_num = "ppa /proc entry passed invalid number\n";
-
- if ((length > 15) && (ppa_strncmp(buffer, "ppa_speed_fast=", 15) == 0)) {
- x = simple_strtoul(buffer + 15, NULL, 0);
- if (x <= ppa_hosts[hostno].speed)
- ppa_hosts[hostno].speed_fast = x;
- else
- printk(inv_num);
- return length;
- }
- if ((length > 10) && (ppa_strncmp(buffer, "ppa_speed=", 10) == 0)) {
- x = simple_strtoul(buffer + 10, NULL, 0);
- if (x >= ppa_hosts[hostno].speed_fast)
- ppa_hosts[hostno].speed = x;
- else
- printk(inv_num);
- return length;
+ /* This routine looks for a device and then attempts to use EPP
+ to send a command. If all goes as planned then EPP is available. */
+
+ static char cmd[6] =
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ int loop, old_mode, status, k, ppb = PPA_BASE(host_no);
+ unsigned char l;
+
+ old_mode = ppa_hosts[host_no].mode;
+ for (loop = 0; loop < 8; loop++) {
+ /* Attempt to use EPP for Test Unit Ready */
+ if ((ppb & 0x0007) == 0x0000)
+ ppa_hosts[host_no].mode = PPA_EPP_32;
+
+ second_pass:
+ ppa_connect(host_no, CONNECT_EPP_MAYBE);
+ /* Select SCSI device */
+ if (!ppa_select(host_no, loop)) {
+ ppa_disconnect(host_no);
+ continue;
}
- if ((length > 10) && (ppa_strncmp(buffer, "epp_speed=", 10) == 0)) {
- x = simple_strtoul(buffer + 10, NULL, 0);
- ppa_hosts[hostno].epp_speed = x;
- return length;
+ printk("ppa: Found device at ID %i, Attempting to use %s\n", loop,
+ PPA_MODE_STRING[ppa_hosts[host_no].mode]);
+
+ /* Send SCSI command */
+ status = 1;
+ w_ctr(ppb, 0x0c);
+ for (l = 0; (l < 6) && (status); l++)
+ status = ppa_out(host_no, cmd, 1);
+
+ if (!status) {
+ ppa_disconnect(host_no);
+ ppa_connect(host_no, CONNECT_EPP_MAYBE);
+ w_dtr(ppb, 0x40);
+ w_ctr(ppb, 0x08);
+ udelay(30);
+ w_ctr(ppb, 0x0c);
+ udelay(1000);
+ ppa_disconnect(host_no);
+ udelay(1000);
+ if (ppa_hosts[host_no].mode == PPA_EPP_32) {
+ ppa_hosts[host_no].mode = old_mode;
+ goto second_pass;
+ }
+ printk("ppa: Unable to establish communication, aborting driver load.\n");
+ return 1;
}
- if ((length > 12) && (ppa_strncmp(buffer, "epp_timeout=", 12) == 0)) {
- x = simple_strtoul(buffer + 12, NULL, 0);
- ppa_hosts[hostno].timeout = x;
- return length;
+ w_ctr(ppb, 0x0c);
+ k = 1000000; /* 1 Second */
+ do {
+ l = r_str(ppb);
+ k--;
+ udelay(1);
+ } while (!(l & 0x80) && (k));
+
+ l &= 0xf0;
+
+ if (l != 0xf0) {
+ ppa_disconnect(host_no);
+ ppa_connect(host_no, CONNECT_EPP_MAYBE);
+ w_dtr(ppb, 0x40);
+ w_ctr(ppb, 0x08);
+ udelay(30);
+ w_ctr(ppb, 0x0c);
+ udelay(1000);
+ ppa_disconnect(host_no);
+ udelay(1000);
+ if (ppa_hosts[host_no].mode == PPA_EPP_32) {
+ ppa_hosts[host_no].mode = old_mode;
+ goto second_pass;
+ }
+ printk("ppa: Unable to establish communication, aborting driver load.\n");
+ return 1;
}
- if ((length > 5) && (ppa_strncmp(buffer, "mode=", 5) == 0)) {
- x = simple_strtoul(buffer + 5, NULL, 0);
- ppa_hosts[hostno].mode = x;
- return length;
- }
- printk("ppa /proc: invalid variable\n");
- return (-EINVAL);
-}
-
-int ppa_proc_info(char *buffer, char **start, off_t offset,
- int length, int hostno, int inout)
-{
- int i;
- int size, len = 0;
- off_t begin = 0;
- off_t pos = 0;
-
- for (i = 0; i < 4; i++)
- if (ppa_hosts[i].host == hostno)
- break;
-
- if (inout)
- return ppa_proc_write(i, buffer, length);
-
- size = sprintf(buffer + len, "Version : %s\n", PPA_VERSION);
- len += size;
- pos = begin + len;
- size = sprintf(buffer + len, "Parport : %s\n",
- ppa_hosts[i].dev->port->name);
- len += size;
- pos = begin + len;
-
- size = sprintf(buffer + len, "Mode : %s\n",
- PPA_MODE_STRING[ppa_hosts[i].mode]);
- len += size;
- pos = begin + len;
-
- size = sprintf(buffer + len, "\nTiming Parameters\n");
- len += size;
- pos = begin + len;
-
- size = sprintf(buffer + len, "ppa_speed %i\n",
- ppa_hosts[i].speed);
- len += size;
- pos = begin + len;
-
- size = sprintf(buffer + len, "ppa_speed_fast %i\n",
- ppa_hosts[i].speed_fast);
- len += size;
- pos = begin + len;
-
- if (IN_EPP_MODE(ppa_hosts[i].mode)) {
- size = sprintf(buffer + len, "epp_speed %i\n",
- ppa_hosts[i].epp_speed);
- len += size;
- pos = begin + len;
-
- size = sprintf(buffer + len, "\nInternal Counters\n");
- len += size;
- pos = begin + len;
-
- size = sprintf(buffer + len, "epp_timeout %i\n",
- ppa_hosts[i].timeout);
- len += size;
- pos = begin + len;
- }
- *start = buffer + (offset + begin);
- len -= (offset - begin);
- if (len > length)
- len = length;
- return len;
+ ppa_disconnect(host_no);
+ printk("ppa: Communication established with ID %i using %s\n", loop,
+ PPA_MODE_STRING[ppa_hosts[host_no].mode]);
+ return 0;
+ }
+ printk("ppa: No devices found, aborting driver load.\n");
+ return 1;
}
-/* end of ppa.c */
* the Iomega ZIP drive
*
* (c) 1996 Grant R. Guenther grant@torque.net
+ * David Campbell campbell@torque.net
+ *
+ * All comments to David.
*/
#ifndef _PPA_H
#define _PPA_H
-#define PPA_VERSION "Curtin 1-08-BETA"
-
-/* This driver reqires a 1.3.37 kernel or higher!! */
+#define PPA_VERSION "1.39"
/* Use the following to enable certain chipset support
* Default is PEDANTIC = 3
*/
-
-#include <linux/config.h>
-
#ifndef CONFIG_SCSI_PPA_HAVE_PEDANTIC
#define CONFIG_SCSI_PPA_HAVE_PEDANTIC 3
#endif
-#ifndef CONFIG_SCSI_PPA_EPP_TIME
-#define CONFIG_SCSI_PPA_EPP_TIME 64
-#endif
+/*
+ * this driver has been hacked by Matteo Frigo (athena@theory.lcs.mit.edu)
+ * to support EPP and scatter-gather. [0.26-athena]
+ *
+ * additional hacks by David Campbell
+ * in response to this driver "mis-behaving" on his machine.
+ * Fixed EPP to handle "software" changing of EPP port data direction.
+ * Chased down EPP timeouts
+ * Made this driver "kernel version friendly" [0.28-athena]
+ *
+ * [ Stuff removed ]
+ *
+ * Compiled against 2.1.53.
+ * Rebuilt ppa_abort() function, should handle unplugged cable.
+ * [1.35s]
+ *
+ * PPA now auto probes for EPP on base address which are aligned on
+ * 8 byte boundaries (0x278 & 0x378) using the attached devices.
+ * This hopefully avoids the nasty problem of trying to detect EPP.
+ * Tested on 2.1.53 [1.36]
+ *
+ * The id_probe utility no longer performs read/write tests.
+ * Additional code included for checking the Intel ECP bug
+ * (Bit 0 of STR stuck low which fools the EPP detection routine)
+ * [1.37]
+ *
+ * Oops! Got the bit sign mixed up for the Intel bug check.
+ * Found that an additional delay is required during SCSI resets
+ * to allow devices to settle down.
+ * [1.38]
+ *
+ * Fixed all problems in the parport sharing scheme. Now ppa can be safe
+ * used with lp or other parport devices on the same parallel port.
+ * 1997 by Andrea Arcangeli <arcangeli@mbox.queen.it>
+ * [1.39]
+ */
/* ------ END OF USER CONFIGURABLE PARAMETERS ----- */
+#ifdef PPA_CODE
#include <linux/stddef.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
#include <linux/blk.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
#include <asm/io.h>
#include "sd.h"
#include "hosts.h"
-#include <linux/parport.h>
/* batteries not included :-) */
-#define PPA_INITIATOR 7
+/*
+ * modes in which the driver can operate
+ */
+#define PPA_AUTODETECT 0 /* Autodetect mode */
+#define PPA_NIBBLE 1 /* work in standard 4 bit mode */
+#define PPA_PS2 2 /* PS/2 byte mode */
+#define PPA_EPP_8 3 /* EPP mode, 8 bit */
+#define PPA_EPP_16 4 /* EPP mode, 16 bit */
+#define PPA_EPP_32 5 /* EPP mode, 32 bit */
+#define PPA_UNKNOWN 6 /* Just in case... */
+
+static char *PPA_MODE_STRING[] =
+{
+ "Autodetect",
+ "SPP",
+ "PS/2",
+ "EPP 8 bit",
+ "EPP 16 bit",
+ "EPP 32 bit",
+ "Unknown"};
+
+/* This is a global option */
+int ppa_sg = SG_ALL; /* enable/disable scatter-gather. */
+
+/* other options */
+#define PPA_CAN_QUEUE 0 /* use "queueing" interface */
+#define PPA_BURST_SIZE 512 /* data burst size */
+#define PPA_SELECT_TMO 5000 /* how long to wait for target ? */
+#define PPA_SPIN_TMO 50000 /* ppa_wait loop limiter */
+#define PPA_DEBUG 0 /* debuging option */
+#define IN_EPP_MODE(x) (x == PPA_EPP_8 || x == PPA_EPP_16 || x == PPA_EPP_32)
+
+/* args to ppa_connect */
+#define CONNECT_EPP_MAYBE 1
+#define CONNECT_NORMAL 0
+
+#define r_dtr(x) (unsigned char)inb((x))
+#define r_str(x) (unsigned char)inb((x)+1)
+#define r_ctr(x) (unsigned char)inb((x)+2)
+#define r_epp(x) (unsigned char)inb((x)+4)
+#define r_fifo(x) (unsigned char)inb((x)+0x400)
+#define r_ecr(x) (unsigned char)inb((x)+0x402)
+
+#define w_dtr(x,y) outb(y, (x))
+#define w_str(x,y) outb(y, (x)+1)
+#define w_ctr(x,y) outb(y, (x)+2)
+#define w_epp(x,y) outb(y, (x)+4)
+#define w_fifo(x,y) outb(y, (x)+0x400)
+#define w_ecr(x,y) outb(y, (x)+0x402)
+
+static int ppa_engine(ppa_struct *, Scsi_Cmnd *);
+static int ppa_in(int, char *, int);
+static int ppa_init(int);
+static void ppa_interrupt(void *);
+static int ppa_out(int, char *, int);
+
+struct proc_dir_entry proc_scsi_ppa =
+{PROC_SCSI_PPA, 3, "ppa", S_IFDIR | S_IRUGO | S_IXUGO, 2};
+#else
+extern struct proc_dir_entry proc_scsi_ppa;
+#define ppa_release 0
+#endif
int ppa_detect(Scsi_Host_Template *);
const char *ppa_info(struct Scsi_Host *);
int ppa_abort(Scsi_Cmnd *);
int ppa_reset(Scsi_Cmnd *, unsigned int);
int ppa_proc_info(char *, char **, off_t, int, int, int);
-int ppa_biosparam(Disk *, kdev_t, int*);
-int ppa_release(struct Scsi_Host *);
-
-#ifndef MODULE
-#ifdef PPA_CODE
-#define SKIP_PROC_DIR
-#endif
-#endif
-
-#ifndef SKIP_PROC_DIR
-struct proc_dir_entry proc_scsi_ppa =
-{PROC_SCSI_PPA, 3, "ppa", S_IFDIR | S_IRUGO | S_IXUGO, 2};
-#endif /* !PPA_CODE => hosts.c */
+int ppa_biosparam(Disk *, kdev_t, int *);
#define PPA { /* next */ 0, \
/* usage_count */ 0, \
/* proc_dir */ &proc_scsi_ppa, \
/* proc_info */ ppa_proc_info, \
- /* name */ "Iomega ZIP/JAZ Traveller", \
+ /* name */ "Iomega parport ZIP drive", \
/* detect */ ppa_detect, \
/* release */ ppa_release, \
/* info */ 0, \
/* slave_attach */ 0, \
/* bios_param */ ppa_biosparam, \
/* can_queue */ 0, \
- /* this_id */ PPA_INITIATOR, \
+ /* this_id */ -1, \
/* sg_tablesize */ SG_ALL, \
/* cmd_per_lun */ 1, \
/* present */ 0, \
SETUP_DEVICE setupDevice[8];
} SETUP, *PSETUP;
-#endif
\ No newline at end of file
+#endif
+
* x is some number, It will let you specify a default
* transfer rate if handshaking isn't working correctly.
*
+ * -DOLDCNTDATASCEME There is a new sceme to set the CONTROL
+ * and DATA reigsters which complies more closely
+ * with the SCSI2 standard. This hopefully eliminates
+ * the need to swap the order these registers are
+ * 'messed' with. It makes the following two options
+ * obsolete. To reenable the old sceme define this.
+ *
* The following to options are patches from the SCSI.HOWTO
*
* -DSWAPSTAT This will swap the definitions for STAT_MSG and STAT_CD.
unsigned char message = 0;
register unsigned char status_read;
+#ifndef OLDCNTDATASCEME
+ volatile unsigned char tmp_data;
+ volatile unsigned char tmp_control;
+#endif
unsigned transfersize = 0, underflow = 0;
incommand = 0;
* try this with a SCSI protocol or logic analyzer to see what is
* going on.
*/
+#ifdef OLDCNTDATASCEME
#ifdef SWAPCNTDATA
cli();
WRITE_CONTROL (BASE_CMD | CMD_DRVR_ENABLE | CMD_SEL |
(reselect ? CMD_ATTN : 0));
sti ();
#endif
+#else
+ tmp_data = (unsigned char) ((1 << target) | (controller_type == SEAGATE
+? 0x80 : 0x40));
+ tmp_control = BASE_CMD | CMD_DRVR_ENABLE | CMD_SEL |
+ (reselect ? CMD_ATTN : 0) | CMD_BSY;
+ WRITE_CONTROL(tmp_data);
+ WRITE_DATA(tmp_control);
+ tmp_control ^= CMD_BSY;
+ WRITE_CONTROL(tmp_control);
+#endif /* OLDCNTDATASCEME */
while (!((status_read = STATUS) & STAT_BSY) && (jiffies < clock)
&& !st0x_aborted)
#if 0 && (DEBUG & PHASE_SELECTION)
Copyright 1992 - 1997 Kai Makisara
email Kai.Makisara@metla.fi
- Last modified: Tue May 27 22:29:00 1997 by makisara@home
+ Last modified: Wed Nov 5 23:39:52 1997 by makisara@home
Some small formal changes - aeb, 950809
*/
}
else
(STp->buffer)->last_result = SCpnt->result;
-#if 0
- if ((STp->buffer)->writing) {
- /* Process errors before releasing request */
- (STp->buffer)->last_result_fatal = st_chk_result(SCpnt);
- SCpnt->request.rq_status = RQ_INACTIVE;
- }
- else
- SCpnt->request.rq_status = RQ_SCSI_DONE;
-#else
+
SCpnt->request.rq_status = RQ_SCSI_DONE;
(STp->buffer)->last_SCpnt = SCpnt;
-#endif
#if DEBUG
STp->write_pending = 0;
}
if ((STp->buffer)->last_result_fatal != 0) {
- if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
- (SCpnt->sense_buffer[2] & 0x0f) == NO_TAPE) {
+ if ((STp->device)->scsi_level >= SCSI_2 &&
+ (SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
+ (SCpnt->sense_buffer[2] & 0x0f) == NOT_READY &&
+ SCpnt->sense_buffer[12] == 0x3a) { /* Check ASC */
STp->ready = ST_NO_TAPE;
} else
STp->ready = ST_NOT_READY;
if (!SCpnt)
goto out;
- SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */
-
if ((STp->buffer)->last_result_fatal != 0 &&
((SCpnt->sense_buffer[0] & 0x70) != 0x70 ||
(SCpnt->sense_buffer[2] & 0x4f) != 0x40 ||
SCpnt->sense_buffer[5] |
SCpnt->sense_buffer[6]) == 0))) {
/* Filter out successful write at EOM */
+ SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */
printk(KERN_ERR "st%d: Error on write filemark.\n", dev);
if (result == 0)
result = (-EIO);
}
else {
+ SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */
if (STps->drv_file >= 0)
STps->drv_file++ ;
STps->drv_block = 0;
}
STp = &(scsi_tapes[dev]);
- if (STp->ready != ST_READY)
- return (-EIO);
+ if (STp->ready != ST_READY) {
+ if (STp->ready == ST_NO_TAPE)
+ return (-ENOMEDIUM);
+ else
+ return (-EIO);
+ }
STm = &(STp->modes[STp->current_mode]);
if (!STm->defined)
return (-ENXIO);
}
else {
SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */
- SCpnt = NULL;
+ SCpnt = *aSCpnt = NULL;
if (transfer == blks) { /* We did not get anything, error */
printk(KERN_NOTICE "st%d: Incorrect block size.\n", dev);
if (STps->drv_block >= 0)
}
STp = &(scsi_tapes[dev]);
- if (STp->ready != ST_READY)
- return (-EIO);
+ if (STp->ready != ST_READY) {
+ if (STp->ready == ST_NO_TAPE)
+ return (-ENOMEDIUM);
+ else
+ return (-EIO);
+ }
STm = &(STp->modes[STp->current_mode]);
if (!STm->defined)
return (-ENXIO);
if ((STp->buffer)->buffer_bytes > 0) {
#if DEBUG
if (debugging && STps->eof != ST_NOEOF)
- printk(ST_DEB_MSG "st%d: EOF up (%d). Left %d, needed %ld.\n", dev,
+ printk(ST_DEB_MSG "st%d: EOF up (%d). Left %d, needed %d.\n", dev,
STps->eof, (STp->buffer)->buffer_bytes, count - total);
#endif
transfer = (STp->buffer)->buffer_bytes < count - total ?
int dev = TAPE_NR(inode->i_rdev);
STp = &(scsi_tapes[dev]);
- if (STp->ready != ST_READY && cmd_in != MTLOAD)
- return (-EIO);
+ if (STp->ready != ST_READY && cmd_in != MTLOAD) {
+ if (STp->ready == ST_NO_TAPE)
+ return (-ENOMEDIUM);
+ else
+ return (-EIO);
+ }
timeout = STp->long_timeout;
STps = &(STp->ps[STp->partition]);
fileno = STps->drv_file;
if (!SCpnt)
return (-EBUSY);
+ SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */
STps->drv_block = STps->drv_file = (-1);
STps->eof = ST_NOEOF;
if ((STp->buffer)->last_result_fatal != 0) {
STps->drv_block = STps->drv_file = 0;
result = 0;
}
- SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */
return result;
}
unsigned capacity;
struct wait_queue * waiting;
Scsi_Device* device;
- Scsi_Cmnd SCpnt;
struct semaphore sem;
ST_buffer * buffer;
-#
-# Sound driver configuration
-#
-#--------
-# There is another confic script which is compatible with rest of
-# the kernel. It can be activated by running 'make mkscript' in this
-# directory. Please note that this is an _experimental_ feature which
-# doesn't work with all cards (PSS, SM Wave, AudioTriX Pro, Maui).
-#--------
-#
-$MAKE -C drivers/sound config || exit 1
+bool 'ProAudioSpectrum 16 support' CONFIG_PAS
+bool '100%% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support' CONFIG_SB
+bool 'Generic OPL2/OPL3 FM synthesizer support' CONFIG_ADLIB
+bool 'Gravis Ultrasound support' CONFIG_GUS
+bool 'MPU-401 support (NOT for SB16)' CONFIG_MPU401
+bool 'PSS (ECHO-ADI2111) support' CONFIG_PSS
+bool '16 bit sampling option of GUS (_NOT_ GUS MAX)' CONFIG_GUS16
+bool 'GUS MAX support' CONFIG_GUSMAX
+bool 'Microsoft Sound System support' CONFIG_MSS
+bool 'Ensoniq SoundScape support' CONFIG_SSCAPE
+bool 'MediaTrix AudioTrix Pro support' CONFIG_TRIX
+bool 'Support for OPTi MAD16 and/or Mozart based cards' CONFIG_MAD16
+bool 'Support for Crystal CS4232 based (PnP) cards' CONFIG_CS4232
+bool 'Support for Turtle Beach Wave Front (Maui, Tropez) synthesizers' CONFIG_MAUI
+bool 'Yamaha OPL3-SA1 audio controller' CONFIG_OPL3SA1
+bool 'SoftOSS software wave table engine' CONFIG_SOFTOSS
+bool 'FM synthesizer (YM3812/OPL-3) support' CONFIG_YM3812
+
+if [ "$CONFIG_AEDSP16" = "y" ]; then
+hex 'I/O base for Audio Excel DSP 16 220 or 240' AEDSP16_BASE 220
+fi
+
+if [ "$CONFIG_SB" = "y" ]; then
+hex 'I/O base for SB Check from manual of the card' SBC_BASE 220
+fi
+
+if [ "$CONFIG_SB" = "y" ]; then
+int 'Sound Blaster IRQ Check from manual of the card' SBC_IRQ 7
+fi
+
+if [ "$CONFIG_SB" = "y" ]; then
+int 'Sound Blaster DMA 0, 1 or 3' SBC_DMA 1
+fi
+
+if [ "$CONFIG_SB" = "y" ]; then
+int 'Sound Blaster 16 bit DMA (SB16, Jazz16, SMW) 5, 6 or 7 (use 1 for 8 bit cards)' SB_DMA2 5
+fi
+
+if [ "$CONFIG_SB" = "y" ]; then
+hex 'MPU401 I/O base of SB16, Jazz16 and ES1688 Check from manual of the card' SB_MPU_BASE 330
+fi
+
+
+if [ "$CONFIG_SB" = "y" ]; then
+comment 'MPU401 IRQ is only required with Jazz16, SM Wave and ESS1688.'
+fi
+
+
+if [ "$CONFIG_SB" = "y" ]; then
+comment 'Enter -1 to the following question if you have something else such as SB16/32.'
+fi
+
+if [ "$CONFIG_SB" = "y" ]; then
+int 'SB MPU401 IRQ (Jazz16, SM Wave and ES1688) Check from manual of the card' SB_MPU_IRQ -1
+fi
+
+if [ "$CONFIG_PAS" = "y" ]; then
+int 'PAS16 IRQ 3, 4, 5, 7, 9, 10, 11, 12, 14 or 15' PAS_IRQ 10
+fi
+
+if [ "$CONFIG_PAS" = "y" ]; then
+int 'PAS16 DMA 0, 1, 3, 5, 6 or 7' PAS_DMA 3
+fi
+
+if [ "$CONFIG_GUS" = "y" ]; then
+hex 'I/O base for GUS 210, 220, 230, 240, 250 or 260' GUS_BASE 220
+fi
+
+if [ "$CONFIG_GUS" = "y" ]; then
+int 'GUS IRQ 3, 5, 7, 9, 11, 12 or 15' GUS_IRQ 15
+fi
+
+if [ "$CONFIG_GUS" = "y" ]; then
+int 'GUS DMA 1, 3, 5, 6 or 7' GUS_DMA 6
+fi
+
+if [ "$CONFIG_GUS" = "y" ]; then
+int 'Second DMA channel for GUS 1, 3, 5, 6 or 7' GUS_DMA2 -1
+fi
+
+if [ "$CONFIG_GUS16" = "y" ]; then
+hex 'I/O base for the 16 bit daughtercard of GUS 530, 604, E80 or F40' GUS16_BASE 530
+fi
+
+if [ "$CONFIG_GUS16" = "y" ]; then
+int 'GUS 16 bit daughtercard IRQ 3, 4, 5, 7, or 9' GUS16_IRQ 7
+fi
+
+if [ "$CONFIG_GUS16" = "y" ]; then
+int 'GUS DMA 0, 1 or 3' GUS16_DMA 3
+fi
+
+if [ "$CONFIG_MPU401" = "y" ]; then
+hex 'I/O base for MPU401 Check from manual of the card' MPU_BASE 330
+fi
+
+if [ "$CONFIG_MPU401" = "y" ]; then
+int 'MPU401 IRQ Check from manual of the card' MPU_IRQ 9
+fi
+
+
+if [ "$CONFIG_MAUI" = "y" ]; then
+comment 'ERROR! You have to use old sound configuration method with Maui.'
+fi
+
+if [ "$CONFIG_MAUI" = "y" ]; then
+hex 'I/O base for Maui 210, 230, 260, 290, 300, 320, 338 or 330' MAUI_BASE 330
+fi
+
+if [ "$CONFIG_MAUI" = "y" ]; then
+int 'Maui IRQ 5, 9, 12 or 15' MAUI_IRQ 9
+fi
+
+if [ "$CONFIG_UART6850" = "y" ]; then
+hex 'I/O base for UART 6850 MIDI port (Unknown)' U6850_BASE 0
+fi
+
+if [ "$CONFIG_UART6850" = "y" ]; then
+int 'UART6850 IRQ (Unknown)' U6850_IRQ -1
+fi
+
+
+if [ "$CONFIG_PSS" = "y" ]; then
+comment 'ERROR! You have to use old sound configuration method with PSS cards.'
+fi
+
+if [ "$CONFIG_PSS" = "y" ]; then
+hex 'PSS I/O base 220 or 240' PSS_BASE 220
+fi
+
+if [ "$CONFIG_PSS" = "y" ]; then
+hex 'PSS audio I/O base 530, 604, E80 or F40' PSS_MSS_BASE 530
+fi
+
+if [ "$CONFIG_PSS" = "y" ]; then
+int 'PSS audio IRQ 7, 9, 10 or 11' PSS_MSS_IRQ 11
+fi
+
+if [ "$CONFIG_PSS" = "y" ]; then
+int 'PSS audio DMA 0, 1 or 3' PSS_MSS_DMA 3
+fi
+
+if [ "$CONFIG_PSS" = "y" ]; then
+hex 'PSS MIDI I/O base ' PSS_MPU_BASE 330
+fi
+
+if [ "$CONFIG_PSS" = "y" ]; then
+int 'PSS MIDI IRQ 3, 4, 5, 7 or 9' PSS_MPU_IRQ 9
+fi
+
+if [ "$CONFIG_MSS" = "y" ]; then
+hex 'MSS/WSS I/O base 530, 604, E80 or F40' MSS_BASE 530
+fi
+
+if [ "$CONFIG_MSS" = "y" ]; then
+int 'MSS/WSS IRQ 7, 9, 10 or 11' MSS_IRQ 11
+fi
+if [ "$CONFIG_MSS" = "y" ]; then
+int 'MSS/WSS DMA 0, 1 or 3' MSS_DMA 3
+fi
+
+if [ "$CONFIG_MSS" = "y" ]; then
+int 'MSS/WSS second DMA (if possible) 0, 1 or 3' MSS_DMA2 -1
+fi
+
+if [ "$CONFIG_SSCAPE" = "y" ]; then
+hex 'SoundScape MIDI I/O base 320, 330, 340 or 350' SSCAPE_BASE 330
+fi
+
+if [ "$CONFIG_SSCAPE" = "y" ]; then
+int 'SoundScape MIDI IRQ ' SSCAPE_IRQ 9
+fi
+
+if [ "$CONFIG_SSCAPE" = "y" ]; then
+int 'SoundScape initialization DMA 0, 1 or 3' SSCAPE_DMA 3
+fi
+
+if [ "$CONFIG_SSCAPE" = "y" ]; then
+hex 'SoundScape audio I/O base 534, 608, E84 or F44' SSCAPE_MSS_BASE 534
+fi
+
+if [ "$CONFIG_SSCAPE" = "y" ]; then
+int 'SoundScape audio IRQ 7, 9, 10 or 11' SSCAPE_MSS_IRQ 11
+fi
+
+
+if [ "$CONFIG_TRIX" = "y" ]; then
+comment 'ERROR! You have to use old sound configuration method with OPL3-SA1.'
+fi
+
+if [ "$CONFIG_TRIX" = "y" ]; then
+hex 'OPL3-SA1 audio I/O base 530, 604, E80 or F40' TRIX_BASE 530
+fi
+
+if [ "$CONFIG_TRIX" = "y" ]; then
+int 'OPL3-SA1 audio IRQ 7, 9, 10 or 11' TRIX_IRQ 11
+fi
+
+if [ "$CONFIG_TRIX" = "y" ]; then
+int 'OPL3-SA1 audio DMA 0, 1 or 3' TRIX_DMA 0
+fi
+
+if [ "$CONFIG_TRIX" = "y" ]; then
+int 'OPL3-SA1 second (duplex) DMA 0, 1 or 3' TRIX_DMA2 3
+fi
+
+if [ "$CONFIG_TRIX" = "y" ]; then
+hex 'OPL3-SA1 MIDI I/O base 330, 370, 3B0 or 3F0' TRIX_MPU_BASE 330
+fi
+
+if [ "$CONFIG_TRIX" = "y" ]; then
+int 'OPL3-SA1 MIDI IRQ 3, 4, 5, 7 or 9' TRIX_MPU_IRQ 9
+fi
+
+if [ "$CONFIG_TRIX" = "y" ]; then
+hex 'OPL3-SA1 SB I/O base 220, 210, 230, 240, 250, 260 or 270' TRIX_SB_BASE 220
+fi
+
+if [ "$CONFIG_TRIX" = "y" ]; then
+int 'OPL3-SA1 SB IRQ 3, 4, 5 or 7' TRIX_SB_IRQ 7
+fi
+
+if [ "$CONFIG_TRIX" = "y" ]; then
+int 'OPL3-SA1 SB DMA 1 or 3' TRIX_SB_DMA 1
+fi
+
+if [ "$CONFIG_OPL3SA1" = "y" ]; then
+hex 'OPL3-SA1 audio I/O base 530, 604, E80 or F40' OPL3SA1_BASE 530
+fi
+
+if [ "$CONFIG_OPL3SA1" = "y" ]; then
+int 'OPL3-SA1 audio IRQ 7, 9, 10 or 11' OPL3SA1_IRQ 11
+fi
+
+if [ "$CONFIG_OPL3SA1" = "y" ]; then
+int 'OPL3-SA1 audio DMA 0, 1 or 3' OPL3SA1_DMA 0
+fi
+
+if [ "$CONFIG_OPL3SA1" = "y" ]; then
+int 'OPL3-SA1 second (duplex) DMA 0, 1 or 3' OPL3SA1_DMA2 3
+fi
+
+if [ "$CONFIG_OPL3SA1" = "y" ]; then
+hex 'OPL3-SA1 MIDI I/O base 330, 370, 3B0 or 3F0' OPL3SA1_MPU_BASE 330
+fi
+
+if [ "$CONFIG_OPL3SA1" = "y" ]; then
+int 'OPL3-SA1 MIDI IRQ 3, 4, 5, 7 or 9' OPL3SA1_MPU_IRQ 9
+fi
+
+if [ "$CONFIG_CS4232" = "y" ]; then
+hex 'CS4232 audio I/O base 530, 604, E80 or F40' CS4232_BASE 530
+fi
+
+if [ "$CONFIG_CS4232" = "y" ]; then
+int 'CS4232 audio IRQ 5, 7, 9, 11, 12 or 15' CS4232_IRQ 11
+fi
+
+if [ "$CONFIG_CS4232" = "y" ]; then
+int 'CS4232 audio DMA 0, 1 or 3' CS4232_DMA 0
+fi
+
+if [ "$CONFIG_CS4232" = "y" ]; then
+int 'CS4232 second (duplex) DMA 0, 1 or 3' CS4232_DMA2 3
+fi
+
+if [ "$CONFIG_CS4232" = "y" ]; then
+hex 'CS4232 MIDI I/O base 330, 370, 3B0 or 3F0' CS4232_MPU_BASE 330
+fi
+
+if [ "$CONFIG_CS4232" = "y" ]; then
+int 'CS4232 MIDI IRQ 5, 7, 9, 11, 12 or 15' CS4232_MPU_IRQ 9
+fi
+
+if [ "$CONFIG_MAD16" = "y" ]; then
+hex 'MAD16 audio I/O base 530, 604, E80 or F40' MAD16_BASE 530
+fi
+
+if [ "$CONFIG_MAD16" = "y" ]; then
+int 'MAD16 audio IRQ 7, 9, 10 or 11' MAD16_IRQ 11
+fi
+
+if [ "$CONFIG_MAD16" = "y" ]; then
+int 'MAD16 audio DMA 0, 1 or 3' MAD16_DMA 3
+fi
+
+if [ "$CONFIG_MAD16" = "y" ]; then
+int 'MAD16 second (duplex) DMA 0, 1 or 3' MAD16_DMA2 0
+fi
+
+if [ "$CONFIG_MAD16" = "y" ]; then
+hex 'MAD16 MIDI I/O base 300, 310, 320 or 330 (0 disables)' MAD16_MPU_BASE 330
+fi
+
+if [ "$CONFIG_MAD16" = "y" ]; then
+int 'MAD16 MIDI IRQ 5, 7, 9 or 10' MAD16_MPU_IRQ 9
+fi
+
+if [ "$CONFIG_SOFTOSS" = "y" ]; then
+int 'Sampling rate for SoftOSS 8000 to 48000' SOFTOSS_RATE 22050
+fi
+
+if [ "$CONFIG_SOFTOSS" = "y" ]; then
+int 'Max # of concurrent voices for SoftOSS 4 to 32' SOFTOSS_VOICES 32
+fi
+#
+$MAKE -C drivers/sound kernelconfig || exit 1
bool 'Additional low level drivers' CONFIG_LOWLEVEL_SOUND
if [ "$CONFIG_LOWLEVEL_SOUND" = "y" ]; then
/*----------------------------------------------------------------
* system configuration
*----------------------------------------------------------------*/
+#define AWE_NEW_KERNEL_INTERFACE
/* if you're using obsolete VoxWare 3.0.x on Linux 1.2.x (or FreeBSD),
* define the following line.
return 0;
}
-#ifndef __LITTLE_ENDIAN
-static int sync_block_swab32 (struct inode * inode, u32 * block, int wait)
-{
- struct buffer_head * bh;
-
- if (!le32_to_cpu(*block))
- return 0;
- bh = get_hash_table (inode->i_dev, le32_to_cpu(*block), blocksize);
- if (!bh)
- return 0;
- if (wait && buffer_req(bh) && !buffer_uptodate(bh)) {
- brelse (bh);
- return -1;
- }
- if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) {
- brelse (bh);
- return 0;
- }
- ll_rw_block (WRITE, 1, &bh);
- bh->b_count--;
- return 0;
-}
-#else
-#define sync_block_swab32 sync_block
-#endif
-
-
-static int sync_iblock (struct inode * inode, u32 * iblock,
- struct buffer_head ** bh, int wait)
-{
- int rc, tmp;
-
- *bh = NULL;
- tmp = *iblock;
- if (!tmp)
- return 0;
- rc = sync_block (inode, iblock, wait);
- if (rc)
- return rc;
- *bh = bread (inode->i_dev, tmp, blocksize);
- if (!*bh)
- return -1;
- return 0;
-}
-
-#ifndef __LITTLE_ENDIAN
-static int sync_iblock_swab32 (struct inode * inode, u32 * iblock,
- struct buffer_head ** bh, int wait)
-{
- int rc, tmp;
-
- *bh = NULL;
- tmp = le32_to_cpu(*iblock);
- if (!tmp)
- return 0;
- rc = sync_block_swab32 (inode, iblock, wait);
- if (rc)
- return rc;
- *bh = bread (inode->i_dev, tmp, blocksize);
- if (!*bh)
- return -1;
- return 0;
-}
-#else
-#define sync_iblock_swab32 sync_iblock
-#endif
static int sync_direct (struct inode * inode, int wait)
{
return err;
}
-static int sync_indirect (struct inode * inode, u32 * iblock, int wait)
-{
- int i;
- struct buffer_head * ind_bh;
- int rc, err = 0;
-
- rc = sync_iblock (inode, iblock, &ind_bh, wait);
- if (rc || !ind_bh)
- return rc;
-
- for (i = 0; i < addr_per_block; i++) {
- rc = sync_block_swab32 (inode,
- ((u32 *) ind_bh->b_data) + i,
- wait);
- if (rc)
- err = rc;
- }
- brelse (ind_bh);
- return err;
-}
-
-#ifndef __LITTLE_ENDIAN
-static __inline__ int sync_indirect_swab32 (struct inode * inode, u32 * iblock, int wait)
-{
- int i;
- struct buffer_head * ind_bh;
- int rc, err = 0;
-
- rc = sync_iblock_swab32 (inode, iblock, &ind_bh, wait);
- if (rc || !ind_bh)
- return rc;
-
- for (i = 0; i < addr_per_block; i++) {
- rc = sync_block_swab32 (inode,
- ((u32 *) ind_bh->b_data) + i,
- wait);
- if (rc)
- err = rc;
- }
- brelse (ind_bh);
- return err;
-}
-#else
-#define sync_indirect_swab32 sync_indirect
-#endif
-
-static int sync_dindirect (struct inode * inode, u32 * diblock, int wait)
-{
- int i;
- struct buffer_head * dind_bh;
- int rc, err = 0;
-
- rc = sync_iblock (inode, diblock, &dind_bh, wait);
- if (rc || !dind_bh)
- return rc;
-
- for (i = 0; i < addr_per_block; i++) {
- rc = sync_indirect_swab32 (inode,
- ((u32 *) dind_bh->b_data) + i,
- wait);
- if (rc)
- err = rc;
- }
- brelse (dind_bh);
- return err;
-}
-
-#ifndef __LITTLE_ENDIAN
-static __inline__ int sync_dindirect_swab32 (struct inode * inode, u32 * diblock, int wait)
-{
- int i;
- struct buffer_head * dind_bh;
- int rc, err = 0;
-
- rc = sync_iblock_swab32 (inode, diblock, &dind_bh, wait);
- if (rc || !dind_bh)
- return rc;
-
- for (i = 0; i < addr_per_block; i++) {
- rc = sync_indirect_swab32 (inode,
- ((u32 *) dind_bh->b_data) + i,
- wait);
- if (rc)
- err = rc;
- }
- brelse (dind_bh);
- return err;
-}
-#else
-#define sync_dindirect_swab32 sync_dindirect
-#endif
-
-static int sync_tindirect (struct inode * inode, u32 * tiblock, int wait)
-{
- int i;
- struct buffer_head * tind_bh;
- int rc, err = 0;
-
- rc = sync_iblock (inode, tiblock, &tind_bh, wait);
- if (rc || !tind_bh)
- return rc;
-
- for (i = 0; i < addr_per_block; i++) {
- rc = sync_dindirect_swab32 (inode,
- ((u32 *) tind_bh->b_data) + i,
- wait);
- if (rc)
- err = rc;
- }
- brelse (tind_bh);
- return err;
-}
-
/*
* File may be NULL when we are called. Perhaps we shouldn't
* even pass file to fsync ?
+ *
+ * This currently falls back to synching the whole device when
+ * the file is larger than can fit directly in the inode. This
+ * is because dirty-buffer handling is indexed by the device
+ * of the buffer, which makes it much faster to sync the whole
+ * device than to sync just one large file.
*/
int ext2_sync_file(struct file * file, struct dentry *dentry)
*/
goto skip;
+ if (inode->i_size > EXT2_NDIR_BLOCKS*blocksize) {
+ err = fsync_dev(inode->i_dev);
+ goto skip;
+ }
+
for (wait=0; wait<=1; wait++)
{
err |= sync_direct (inode, wait);
- err |= sync_indirect (inode,
- inode->u.ext2_i.i_data+EXT2_IND_BLOCK,
- wait);
- err |= sync_dindirect (inode,
- inode->u.ext2_i.i_data+EXT2_DIND_BLOCK,
- wait);
- err |= sync_tindirect (inode,
- inode->u.ext2_i.i_data+EXT2_TIND_BLOCK,
- wait);
}
skip:
err |= ext2_sync_inode (inode);
if (!pnt)
return 0;
- i = strlen(pnt)+1;
+ i = strlen(pnt);
if (i > buflen)
i = buflen;
if (copy_to_user(buffer, pnt, i))
ncp_delete_dentry /* d_delete(struct dentry *) */
};
+
+/*
+ * XXX: It would be better to use the tolower from linux/ctype.h,
+ * but _ctype is needed and it is not exported.
+ */
+#define tolower(c) (((c) >= 'A' && (c) <= 'Z') ? (c)-('A'-'a') : (c))
+
+
static int
ncp_hash_dentry(struct dentry *dentry, struct qstr *this)
{
- char al[NCP_MAXPATHLEN];
unsigned long hash;
int i;
- memcpy(al,this->name,this->len);
- al[this->len] = 0;
-
- if (!ncp_preserve_case(dentry->d_inode))
- str_lower(al);
-
hash = init_name_hash();
for (i=0; i<this->len ; i++)
- hash = partial_name_hash(al[i],hash);
+ hash = partial_name_hash(tolower(this->name[i]),hash);
this->hash = end_name_hash(hash);
return 0;
static int
ncp_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
{
- char al[NCP_MAXPATHLEN],bl[NCP_MAXPATHLEN];
+ int i;
if (a->len != b->len) return 1;
- if (ncp_preserve_case(dentry->d_inode))
+ if (ncp_case_sensitive(dentry->d_inode))
return strncmp(a->name, b->name, a->len);
- memcpy (al,a->name,a->len);
- memcpy (bl,b->name,b->len);
- al[a->len] = bl[b->len] = 0;
+ for (i=0; i<a->len; i++)
+ if (tolower(a->name[i]) != tolower(b->name[i]))
+ return 1;
- str_lower(al);
- str_lower(bl);
-
- return strcmp(al,bl);
+ return 0;
}
/*
static ino_t
find_inode_number(struct dentry *dir, struct qstr *name)
{
+ unsigned long hash;
+ int i;
struct dentry * dentry;
ino_t ino = 0;
-
- name->hash = full_name_hash(name->name, name->len);
+
+ hash = init_name_hash();
+ for (i=0; i<name->len ; i++)
+ hash = partial_name_hash(tolower(name->name[i]),hash);
+ name->hash = end_name_hash(hash);
+
dentry = d_lookup(dir, name);
if (dentry)
{
struct ncpfs_inode_info finfo;
__u8 __name[dentry->d_name.len + 1];
- printk("ncp_lookup_validate called\n");
-
+ if (!dentry->d_inode) {
+ DPRINTK(KERN_DEBUG "ncp_lookup_validate: called with dentry->d_inode already NULL.\n");
+ return 0;
+ }
+
if (!dir || !S_ISDIR(dir->i_mode)) {
printk(KERN_WARNING "ncp_lookup_validate: inode is NULL or not a directory.\n");
goto finished;
unsigned long tmp;
int bufsize;
int pos;
- mm_segment_t fs;
+ unsigned long fs;
page = __get_free_page(GFP_KERNEL);
if (!page)
struct file *file;
struct inode *inode;
struct socket *sock;
- mm_segment_t fs;
+ unsigned long fs;
int result;
char *start = server->packet;
poll_table wait_table;
#define NFS_MAX_AGE 10*HZ /* max age for dentry validation */
-#ifndef shrink_dcache_parent
-#define shrink_dcache_parent(dentry) shrink_dcache_sb((dentry)->d_sb)
-#endif
-
/* needed by smbfs as well ... move to dcache? */
extern void nfs_renew_times(struct dentry *);
static int nfs_safe_remove(struct dentry *);
-static int nfs_dir_open(struct inode * inode, struct file * file);
+static int nfs_dir_open(struct inode *, struct file *);
static ssize_t nfs_dir_read(struct file *, char *, size_t, loff_t *);
static int nfs_readdir(struct file *, void *, filldir_t);
static int nfs_lookup(struct inode *, struct dentry *);
if (age > NFS_MAX_AGE)
d_drop(dentry);
}
+
+#ifdef NFS_PARANOIA
+ /*
+ * Sanity check: if the dentry has been unhashed and the
+ * inode still has users, we could have problems ...
+ */
+ if (list_empty(&dentry->d_hash) && dentry->d_inode) {
+ struct inode *inode = dentry->d_inode;
+ if (inode->i_count > 1) {
+printk("nfs_dentry_delete: %s/%s: ino=%ld, count=%d, nlink=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name,
+inode->i_ino, inode->i_count, inode->i_nlink);
+ }
+ }
+#endif
}
static struct dentry_operations nfs_dentry_operations = {
{
struct inode *dir = dentry->d_parent->d_inode;
struct inode *inode = dentry->d_inode;
- int error;
+ int error, rehash = 0;
error = -EBUSY;
if (inode) {
#endif
goto out;
}
+ /*
+ * Unhash the dentry while we remove the file ...
+ */
+ if (!list_empty(&dentry->d_hash)) {
+ d_drop(dentry);
+ rehash = 1;
+ }
error = nfs_proc_remove(NFS_SERVER(dir),
NFS_FH(dir), dentry->d_name.name);
+ /*
+ * ... then restore the hashed state. This ensures that the
+ * dentry can't become busy after having its file deleted.
+ */
+ if (rehash) {
+ d_add(dentry, inode);
+ }
#ifdef NFS_PARANOIA
if (dentry->d_count > 1)
printk("nfs_safe_remove: %s/%s busy after delete?? d_count=%d\n",
nfs_invalidate_dircache(dir);
if (inode && inode->i_nlink)
inode->i_nlink --;
+ d_delete(dentry);
}
out:
return error;
error = nfs_safe_remove(dentry);
if (!error) {
nfs_renew_times(dentry);
- d_delete(dentry);
}
}
out:
static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry)
{
- struct inode *inode = old_dentry->d_inode;
- int update = 1, error;
+ struct inode *old_inode = old_dentry->d_inode;
+ struct inode *new_inode = new_dentry->d_inode;
+ int error, rehash = 0, update = 1;
#ifdef NFS_DEBUG_VERBOSE
printk("nfs_rename: old %s/%s, count=%d, new %s/%s, count=%d\n",
if (old_dentry->d_name.len > NFS_MAXNAMLEN ||
new_dentry->d_name.len > NFS_MAXNAMLEN)
goto out;
+
+ /*
+ * First check whether the target is busy ... we can't
+ * safely do _any_ rename if the target is in use.
+ */
+ if (new_dentry->d_count > 1) {
+ if (new_inode && S_ISDIR(new_inode->i_mode))
+ shrink_dcache_parent(new_dentry);
+ }
+ error = -EBUSY;
+ if (new_dentry->d_count > 1) {
+#ifdef NFS_PARANOIA
+printk("nfs_rename: target %s/%s busy, d_count=%d\n",
+new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count);
+#endif
+ goto out;
+ }
+
/*
* Check for within-directory rename ... no complications.
*/
/*
* Cross-directory move ... check whether it's a file.
*/
- error = -EBUSY;
- if (S_ISREG(inode->i_mode)) {
- if (NFS_WRITEBACK(inode)) {
+ if (S_ISREG(old_inode->i_mode)) {
+ if (NFS_WRITEBACK(old_inode)) {
#ifdef NFS_PARANOIA
printk("nfs_rename: %s/%s has pending writes\n",
old_dentry->d_parent->d_name.name, old_dentry->d_name.name);
#endif
- nfs_flush_dirty_pages(inode, 0, 0, 0);
- if (NFS_WRITEBACK(inode)) {
+ nfs_flush_dirty_pages(old_inode, 0, 0, 0);
+ if (NFS_WRITEBACK(old_inode)) {
#ifdef NFS_PARANOIA
printk("nfs_rename: %s/%s has pending writes after flush\n",
old_dentry->d_parent->d_name.name, old_dentry->d_name.name);
#endif
goto out;
}
-
if (new_dentry->d_count > 1) {
#ifdef NFS_PARANOIA
printk("nfs_rename: new dentry %s/%s busy, d_count=%d\n",
}
d_drop(old_dentry);
- d_drop(new_dentry);
update = 0;
do_rename:
+ /*
+ * We must prevent any new references to the target while
+ * the rename is in progress, so we unhash the dentry.
+ */
+ if (!list_empty(&new_dentry->d_hash)) {
+ d_drop(new_dentry);
+ rehash = 1;
+ }
error = nfs_proc_rename(NFS_SERVER(old_dir),
NFS_FH(old_dir), old_dentry->d_name.name,
NFS_FH(new_dir), new_dentry->d_name.name);
+ if (rehash) {
+ d_add(new_dentry, new_inode);
+ }
+#ifdef NFS_PARANOIA
+if (new_dentry->d_count > 1)
+printk("nfs_rename: %s/%s busy after rename, d_count=%d\n",
+new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count);
+#endif
if (!error) {
nfs_invalidate_dircache(new_dir);
nfs_invalidate_dircache(old_dir);
* Big trouble! The inode has become a different object.
*/
#ifdef NFS_PARANOIA
-printk("nfs_refresh_inode: mode changed, %07o to %07o\n",
-inode->i_mode, fattr->mode);
+printk("nfs_refresh_inode: inode %ld mode changed, %07o to %07o\n",
+inode->i_ino, inode->i_mode, fattr->mode);
#endif
fattr->mode = inode->i_mode; /* save mode */
make_bad_inode(inode);
inode->i_mode = fattr->mode; /* restore mode */
- inode->i_nlink = 0;
/*
* No need to worry about unhashing the dentry, as the
* lookup validation will know that the inode is bad.
: "edx" (inp)
: "eax", "ecx", "edx", "edi" );
/* If this fails, it means that any user program may lock CPU hard. Too bad. */
- if (res != 12345678) printk( "Bad.\n" );
+ if (res != 12345678) printk( "Buggy.\n" );
else printk( "Ok.\n" );
#endif
}
*/
extern int pentium_f00f_bug;
+extern void trap_init_f00f_bug(void);
+
__initfunc(static void check_pentium_f00f(void))
{
/*
* Pentium and Pentium MMX
*/
- printk("checking for F00F bug ...");
- if(x86==5 && !memcmp(x86_vendor_id, "GenuineIntel", 12))
- {
- extern void trap_init_f00f_bug(void);
-
- printk(KERN_INFO "\nIntel Pentium/[MMX] F0 0F bug detected - turning on workaround.\n");
+ pentium_f00f_bug = 0;
+ if (x86==5 && !memcmp(x86_vendor_id, "GenuineIntel", 12)) {
+ printk(KERN_INFO "Intel Pentium with F0 0F bug - workaround enabled.\n");
pentium_f00f_bug = 1;
trap_init_f00f_bug();
- } else {
- printk(KERN_INFO " no F0 0F bug in this CPU, great!\n");
- pentium_f00f_bug = 0;
}
}
:"ax","dx")
#define set_intr_gate(n,addr) \
- _set_gate(&idt[n],14,0,addr)
+ _set_gate(idt+(n),14,0,addr)
#define set_trap_gate(n,addr) \
- _set_gate(&idt[n],15,0,addr)
+ _set_gate(idt+(n),15,0,addr)
#define set_system_gate(n,addr) \
- _set_gate(&idt[n],15,3,addr)
+ _set_gate(idt+(n),15,3,addr)
#define set_call_gate(a,addr) \
_set_gate(a,12,3,addr)
#ifndef _LINUX_HEAD_H
#define _LINUX_HEAD_H
-typedef struct desc_struct {
+struct desc_struct {
unsigned long a,b;
-} desc_table[256];
+};
-extern desc_table idt,gdt;
+extern struct desc_struct idt_table[],gdt_table[];
+extern struct desc_struct *idt, *gdt;
+
+struct Xgt_desc_struct {
+ unsigned short size;
+ unsigned long address __attribute__((packed));
+};
+
+#define idt_descr (*(struct Xgt_desc_struct *)((char *)&idt - 2))
+#define gdt_descr (*(struct Xgt_desc_struct *)((char *)&gdt - 2))
#define GDT_NUL 0
#define GDT_CODE 1
#define ATARIMOUSE_MINOR 5
#define SUN_MOUSE_MINOR 6
#define PC110PAD_MINOR 9
-#define RADIO_MINOR 129
#define RTC_MINOR 135
#define SUN_OPENPROM_MINOR 139
#define NVRAM_MINOR 144
+#define RADIO_MINOR 152
#define MISC_DYNAMIC_MINOR 255
extern int misc_init(void);
static inline int ncp_preserve_case(struct inode *i)
{
- /* If we can get case-sensitive server lookups working, then
- *
- * return (ncp_namespace(i) == NW_NS_OS2);
- */
- return 0;
+ return (ncp_namespace(i) == NW_NS_OS2);
}
+static inline int ncp_case_sensitive(struct inode *i)
+{
+ return 0;
+}
+
#endif /* __KERNEL__ */
#endif /* _LINUX_NCP_FS_H */
#define PCI_DEVICE_ID_MOTOROLA_RAVEN 0x4801
#define PCI_VENDOR_ID_PROMISE 0x105a
+#define PCI_DEVICE_ID_PROMISE_IDE_UDMA 0x4d33
#define PCI_DEVICE_ID_PROMISE_5300 0x5300
#define PCI_VENDOR_ID_N9 0x105d
EXPORT_SYMBOL(get_cached_page);
EXPORT_SYMBOL(put_cached_page);
EXPORT_SYMBOL(shrink_dcache_sb);
+EXPORT_SYMBOL(shrink_dcache_parent);
#if !defined(CONFIG_NFSD) && defined(CONFIG_NFSD_MODULE)
EXPORT_SYMBOL(do_nfsservctl);
tristate 'The IPX protocol' CONFIG_IPX
if [ "$CONFIG_IPX" != "n" ]; then
bool 'Full internal IPX network' CONFIG_IPX_INTERN
- if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- bool 'IPX Type 20 Routing' CONFIG_IPX_PPROP_ROUTING
- fi
fi
tristate 'Appletalk DDP' CONFIG_ATALK
tristate 'Amateur Radio AX.25 Level 2' CONFIG_AX25
}
}
-#ifdef CONFIG_IPX_PPROP_ROUTING
if( ipx->ipx_type == IPX_TYPE_PPROP && ipx->ipx_tctrl < 8 && skb->pkt_type == PACKET_HOST )
{
int i;
}
}
-#endif
if (!ipx->ipx_dest.net)
ipx->ipx_dest.net = intrfc->if_netnum;