diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-06-22 23:05:57 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-06-22 23:05:57 +0000 |
commit | 51d3b7814cdccef9188240fe0cbd8d97ff2c7470 (patch) | |
tree | 5cbb01d0323d4f63ade66bdf48ba4a91aaa6df16 /drivers | |
parent | 52273a23c9a84336b93a35e4847fc88fac7eb0e4 (diff) |
Merge with Linux 2.3.7.
WARNING: 2.3.7 is known to eat filesystems for breakfast and little
children for lunch, so if you try this on your machine make backups
first ...
Diffstat (limited to 'drivers')
75 files changed, 6374 insertions, 3008 deletions
diff --git a/drivers/acorn/block/Config.in b/drivers/acorn/block/Config.in index 545581525..6ed6cf6a3 100644 --- a/drivers/acorn/block/Config.in +++ b/drivers/acorn/block/Config.in @@ -4,15 +4,12 @@ mainmenu_option next_comment comment 'Acorn-specific block devices' -bool ' Support expansion card IDE interfaces' CONFIG_BLK_DEV_IDE_CARDS -if [ "$CONFIG_BLK_DEV_IDE_CARDS" = "y" ]; then - dep_tristate ' ICS IDE interface support' CONFIG_BLK_DEV_IDE_ICSIDE $CONFIG_BLK_DEV_IDE - dep_tristate ' RapIDE interface support' CONFIG_BLK_DEV_IDE_RAPIDE $CONFIG_BLK_DEV_IDE -fi - -tristate 'MFM harddisk support' CONFIG_BLK_DEV_MFM -if [ "$CONFIG_BLK_DEV_MFM" != "n" ]; then - bool ' Autodetect hard drive geometry' CONFIG_BLK_DEV_MFM_AUTODETECT +if [ "$CONFIG_ARCH_ARC" = "y" -o "$CONFIG_ARCH_A5K" = "y" ]; then + tristate 'Old Archimedes floppy (1772) support' CONFIG_BLK_DEV_FD1772 + tristate 'MFM harddisk support' CONFIG_BLK_DEV_MFM + if [ "$CONFIG_BLK_DEV_MFM" != "n" ]; then + bool ' Autodetect hard drive geometry' CONFIG_BLK_DEV_MFM_AUTODETECT + fi fi endmenu diff --git a/drivers/acorn/block/Makefile b/drivers/acorn/block/Makefile index c4016ca0d..4db3192b6 100644 --- a/drivers/acorn/block/Makefile +++ b/drivers/acorn/block/Makefile @@ -14,29 +14,11 @@ L_OBJS := M_OBJS := MOD_LIST_NAME := ACORN_BLOCK_MODULES -ifeq ($(CONFIG_ARCH_ARC),y) - ifeq ($(CONFIG_BLK_DEV_FD),y) - L_OBJS += fd1772.o fd1772dma.o - else - ifeq ($(CONFIG_BLK_DEV_FD),m) - M_OBJS += fd1772_mod.o - endif - endif -endif - -ifeq ($(CONFIG_BLK_DEV_IDE_ICSIDE),y) - L_OBJS += ide-ics.o -else - ifeq ($(CONFIG_BLK_DEV_IDE_ICSIDE),m) - M_OBJS += ide-ics.o - endif -endif - -ifeq ($(CONFIG_BLK_DEV_IDE_RAPIDE),y) - L_OBJS += ide-rapide.o +ifeq ($(CONFIG_BLK_DEV_FD1772),y) + L_OBJS += fd1772.o fd1772dma.o else - ifeq ($(CONFIG_BLK_DEV_IDE_RAPIDE),m) - M_OBJS += ide-rapide.o + ifeq ($(CONFIG_BLK_DEV_FD1772),m) + M_OBJS += fd1772_mod.o endif endif diff --git a/drivers/acorn/block/fd1772.c b/drivers/acorn/block/fd1772.c index 02a2307d6..8ee368ac9 100644 --- a/drivers/acorn/block/fd1772.c +++ b/drivers/acorn/block/fd1772.c @@ -114,6 +114,8 @@ * I wish I knew why that timer didn't work..... * * 16/11/96 - Fiddled and frigged for 2.0.18 + * + * DAG 30/01/99 - Started frobbing for 2.2.1 */ #include <linux/sched.h> @@ -136,14 +138,14 @@ #include <asm/dma.h> #include <asm/hardware.h> #include <asm/io.h> +#include <asm/ioc.h> #include <asm/irq.h> -#include <asm/irq-no.h> #include <asm/pgtable.h> #include <asm/segment.h> #define MAJOR_NR FLOPPY_MAJOR #define FLOPPY_DMA 0 -#include "blk.h" +#include <linux/blk.h> /* Note: FD_MAX_UNITS could be redefined to 2 for the Atari (with * little additional rework in this file). But I'm not yet sure if diff --git a/drivers/acorn/block/fd1772dma.S b/drivers/acorn/block/fd1772dma.S index f145628e7..796443544 100644 --- a/drivers/acorn/block/fd1772dma.S +++ b/drivers/acorn/block/fd1772dma.S @@ -4,45 +4,45 @@ .text - .global _fdc1772_dataaddr -_fdc1772_fiqdata: + .global fdc1772_dataaddr +fdc1772_fiqdata: @ Number of bytes left to DMA - .global _fdc1772_bytestogo -_fdc1772_bytestogo: + .global fdc1772_bytestogo +fdc1772_bytestogo: .word 0 @ Place to put/get data from in DMA - .global _fdc1772_dataaddr -_fdc1772_dataaddr: + .global fdc1772_dataaddr +fdc1772_dataaddr: .word 0 - .global _fdc1772_fdc_int_done -_fdc1772_fdc_int_done: + .global fdc1772_fdc_int_done +fdc1772_fdc_int_done: .word 0 - .global _fdc1772_comendstatus -_fdc1772_comendstatus: + .global fdc1772_comendstatus +fdc1772_comendstatus: .word 0 @ We hang this off DMA channel 1 - .global _fdc1772_comendhandler -_fdc1772_comendhandler: + .global fdc1772_comendhandler +fdc1772_comendhandler: mov r8,#IOC_BASE ldrb r9,[r8,#0x34] @ IOC FIQ status tst r9,#2 subeqs pc,r14,#4 @ should I leave a space here orr r9,r8,#0x10000 @ FDC base - adr r8,_fdc1772_fdc_int_done + adr r8,fdc1772_fdc_int_done ldrb r10,[r9,#0] @ FDC status mov r9,#1 @ Got a FIQ flag stmia r8,{r9,r10} subs pc,r14,#4 - .global _fdc1772_dma_read -_fdc1772_dma_read: + .global fdc1772_dma_read +fdc1772_dma_read: mov r8,#IOC_BASE ldrb r9,[r8,#0x34] @ IOC FIQ status tst r9,#1 - beq _fdc1772_dma_read_notours + beq fdc1772_dma_read_notours orr r8,r8,#0x10000 @ FDC base ldrb r10,[r8,#0xc] @ Read from FDC data reg (also clears interrupt) ldmia r11,{r8,r9} @@ -51,19 +51,19 @@ _fdc1772_dma_read: strplb r10,[r9],#1 @ Store the data and increment the pointer stmplia r11,{r8,r9} @ Update count/pointers @ Handle any other interrupts if there are any -_fdc1772_dma_read_notours: +fdc1772_dma_read_notours: @ Cant branch because this code has been copied down to the FIQ vector ldr pc,[pc,#-4] - .word _fdc1772_comendhandler - .global _fdc1772_dma_read_end -_fdc1772_dma_read_end: + .word fdc1772_comendhandler + .global fdc1772_dma_read_end +fdc1772_dma_read_end: - .global _fdc1772_dma_write -_fdc1772_dma_write: + .global fdc1772_dma_write +fdc1772_dma_write: mov r8,#IOC_BASE ldrb r9,[r8,#0x34] @ IOC FIQ status tst r9,#1 - beq _fdc1772_dma_write_notours + beq fdc1772_dma_write_notours orr r8,r8,#0x10000 @ FDC base ldmia r11,{r9,r10} subs r9,r9,#1 @ One less byte to go @@ -72,23 +72,23 @@ _fdc1772_dma_write: strplb r12,[r8,#0xc] @ write it to FDC data reg stmplia r11,{r9,r10} @ Update count and pointer - should clear interrupt @ Handle any other interrupts -_fdc1772_dma_write_notours: +fdc1772_dma_write_notours: @ Cant branch because this code has been copied down to the FIQ vector ldr pc,[pc,#-4] - .word _fdc1772_comendhandler + .word fdc1772_comendhandler - .global _fdc1772_dma_write_end -_fdc1772_dma_write_end: + .global fdc1772_dma_write_end +fdc1772_dma_write_end: @ Setup the FIQ R11 to point to the data and store the count, address @ for this dma @ R0=count @ R1=address - .global _fdc1772_setupdma -_fdc1772_setupdma: + .global fdc1772_setupdma +fdc1772_setupdma: @ The big job is flipping in and out of FIQ mode - adr r2,_fdc1772_fiqdata @ This is what we really came here for + adr r2,fdc1772_fiqdata @ This is what we really came here for stmia r2,{r0,r1} mov r3, pc teqp pc,#0x0c000001 @ Disable FIQs, IRQs and switch to FIQ mode diff --git a/drivers/acorn/block/ide-ics.c b/drivers/acorn/block/ide-ics.c deleted file mode 100644 index 24ac28094..000000000 --- a/drivers/acorn/block/ide-ics.c +++ /dev/null @@ -1,295 +0,0 @@ -/* - * linux/arch/arm/drivers/block/ide-ics.c - * - * Copyright (c) 1996,1997 Russell King. - * - * Changelog: - * 08-06-1996 RMK Created - * 12-09-1997 RMK Added interrupt enable/disable - */ - -#include <linux/string.h> -#include <linux/module.h> -#include <linux/ioport.h> -#include <linux/malloc.h> -#include <linux/blkdev.h> -#include <linux/errno.h> -#include <linux/hdreg.h> - -#include <asm/ecard.h> -#include <asm/io.h> - -#include "../../block/ide.h" - -/* - * Maximum number of interfaces per card - */ -#define MAX_IFS 2 - -#define ICS_IDENT_OFFSET 0x8a0 - -#define ICS_ARCIN_V5_INTRSTAT 0x000 -#define ICS_ARCIN_V5_INTROFFSET 0x001 -#define ICS_ARCIN_V5_IDEOFFSET 0xa00 -#define ICS_ARCIN_V5_IDEALTOFFSET 0xae0 -#define ICS_ARCIN_V5_IDESTEPPING 4 - -#define ICS_ARCIN_V6_IDEOFFSET_1 0x800 -#define ICS_ARCIN_V6_INTROFFSET_1 0x880 -#define ICS_ARCIN_V6_INTRSTAT_1 0x8a4 -#define ICS_ARCIN_V6_IDEALTOFFSET_1 0x8e0 -#define ICS_ARCIN_V6_IDEOFFSET_2 0xc00 -#define ICS_ARCIN_V6_INTROFFSET_2 0xc80 -#define ICS_ARCIN_V6_INTRSTAT_2 0xca4 -#define ICS_ARCIN_V6_IDEALTOFFSET_2 0xce0 -#define ICS_ARCIN_V6_IDESTEPPING 4 - -static const card_ids icside_cids[] = { - { MANU_ICS, PROD_ICS_IDE }, - { 0xffff, 0xffff } -}; - -typedef enum { - ics_if_unknown, - ics_if_arcin_v5, - ics_if_arcin_v6 -} iftype_t; - -static struct expansion_card *ec[MAX_ECARDS]; -static int result[MAX_ECARDS][MAX_IFS]; - - -/* ---------------- Version 5 PCB Support Functions --------------------- */ -/* Prototype: icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr) - * Purpose : enable interrupts from card - */ -static void icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr) -{ - unsigned int memc_port = (unsigned int)ec->irq_data; - outb (0, memc_port + ICS_ARCIN_V5_INTROFFSET); -} - -/* Prototype: icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr) - * Purpose : disable interrupts from card - */ -static void icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr) -{ - unsigned int memc_port = (unsigned int)ec->irq_data; - inb (memc_port + ICS_ARCIN_V5_INTROFFSET); -} - -static const expansioncard_ops_t icside_ops_arcin_v5 = { - icside_irqenable_arcin_v5, - icside_irqdisable_arcin_v5, - NULL, - NULL -}; - - -/* ---------------- Version 6 PCB Support Functions --------------------- */ -/* Prototype: icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr) - * Purpose : enable interrupts from card - */ -static void icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr) -{ - unsigned int ide_base_port = (unsigned int)ec->irq_data; - outb (0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_1); - outb (0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_2); -} - -/* Prototype: icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr) - * Purpose : disable interrupts from card - */ -static void icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr) -{ - unsigned int ide_base_port = (unsigned int)ec->irq_data; - inb (ide_base_port + ICS_ARCIN_V6_INTROFFSET_1); - inb (ide_base_port + ICS_ARCIN_V6_INTROFFSET_2); -} - -static const expansioncard_ops_t icside_ops_arcin_v6 = { - icside_irqenable_arcin_v6, - icside_irqdisable_arcin_v6, - NULL, - NULL -}; - - - -/* Prototype: icside_identifyif (struct expansion_card *ec) - * Purpose : identify IDE interface type - * Notes : checks the description string - */ -static iftype_t icside_identifyif (struct expansion_card *ec) -{ - unsigned int addr; - iftype_t iftype; - int id = 0; - - iftype = ics_if_unknown; - - addr = ecard_address (ec, ECARD_IOC, ECARD_FAST) + ICS_IDENT_OFFSET; - - id = inb (addr) & 1; - id |= (inb (addr + 1) & 1) << 1; - id |= (inb (addr + 2) & 1) << 2; - id |= (inb (addr + 3) & 1) << 3; - - switch (id) { - case 0: /* A3IN */ - printk ("icside: A3IN unsupported\n"); - break; - - case 1: /* A3USER */ - printk ("icside: A3USER unsupported\n"); - break; - - case 3: /* ARCIN V6 */ - printk ("icside: detected ARCIN V6 in slot %d\n", ec->slot_no); - iftype = ics_if_arcin_v6; - break; - - case 15:/* ARCIN V5 (no id) */ - printk ("icside: detected ARCIN V5 in slot %d\n", ec->slot_no); - iftype = ics_if_arcin_v5; - break; - - default:/* we don't know - complain very loudly */ - printk ("icside: ***********************************\n"); - printk ("icside: *** UNKNOWN ICS INTERFACE id=%d ***\n", id); - printk ("icside: ***********************************\n"); - printk ("icside: please report this to: linux@arm.uk.linux.org\n"); - printk ("icside: defaulting to ARCIN V5\n"); - iftype = ics_if_arcin_v5; - break; - } - - return iftype; -} - -static int icside_register_port(unsigned long dataport, unsigned long ctrlport, int stepping, int irq) -{ - hw_regs_t hw; - int i; - - memset(&hw, 0, sizeof(hw)); - - for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { - hw.io_ports[i] = (ide_ioreg_t)dataport; - dataport += 1 << stepping; - } - hw.io_ports[IDE_CONTROL_OFFSET] = ctrlport; - hw.irq = irq; - - return ide_register_hw(&hw, NULL); -} - -/* Prototype: icside_register (struct expansion_card *ec) - * Purpose : register an ICS IDE card with the IDE driver - * Notes : we make sure that interrupts are disabled from the card - */ -static inline void icside_register (struct expansion_card *ec, int index) -{ - unsigned long port; - - result[index][0] = -1; - result[index][1] = -1; - - switch (icside_identifyif (ec)) { - case ics_if_unknown: - default: - printk ("** Warning: ICS IDE Interface unrecognised! **\n"); - break; - - case ics_if_arcin_v5: - port = ecard_address (ec, ECARD_MEMC, 0); - ec->irqaddr = ioaddr(port + ICS_ARCIN_V5_INTRSTAT); - ec->irqmask = 1; - ec->irq_data = (void *)port; - ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v5; - - /* - * Be on the safe side - disable interrupts - */ - inb (port + ICS_ARCIN_V5_INTROFFSET); - result[index][0] = icside_register_port(port + ICS_ARCIN_V5_IDEOFFSET, - port + ICS_ARCIN_V5_IDEALTOFFSET, - ICS_ARCIN_V5_IDESTEPPING, - ec->irq); - result[index][1] = -1; - break; - - case ics_if_arcin_v6: - port = ecard_address (ec, ECARD_IOC, ECARD_FAST); - ec->irqaddr = ioaddr(port + ICS_ARCIN_V6_INTRSTAT_1); - ec->irqmask = 1; - ec->irq_data = (void *)port; - ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v6; - - /* - * Be on the safe side - disable interrupts - */ - inb (port + ICS_ARCIN_V6_INTROFFSET_1); - inb (port + ICS_ARCIN_V6_INTROFFSET_2); - - result[index][0] = icside_register_port(port + ICS_ARCIN_V6_IDEOFFSET_1, - port + ICS_ARCIN_V6_IDEALTOFFSET_1, - ICS_ARCIN_V6_IDESTEPPING, - ec->irq); - result[index][1] = icside_register_port(port + ICS_ARCIN_V6_IDEOFFSET_2, - port + ICS_ARCIN_V6_IDEALTOFFSET_2, - ICS_ARCIN_V6_IDESTEPPING, - ec->irq); - break; - } -} - -int icside_init (void) -{ - int i; - - for (i = 0; i < MAX_ECARDS; i++) - ec[i] = NULL; - - ecard_startfind (); - - for (i = 0; ; i++) { - if ((ec[i] = ecard_find (0, icside_cids)) == NULL) - break; - - ecard_claim (ec[i]); - icside_register (ec[i], i); - } - - for (i = 0; i < MAX_ECARDS; i++) - if (ec[i] && result[i][0] < 0 && result[i][1] < 0) { - ecard_release (ec[i]); - ec[i] = NULL; - } - return 0; -} - -#ifdef MODULE -int init_module (void) -{ - return icside_init(); -} - -void cleanup_module (void) -{ - int i; - - for (i = 0; i < MAX_ECARDS; i++) - if (ec[i]) { - if (result[i][0] >= 0) - ide_unregister (result[i][0]); - - if (result[i][1] >= 0) - ide_unregister (result[i][1]); - - ecard_release (ec[i]); - ec[i] = NULL; - } -} -#endif - diff --git a/drivers/acorn/block/mfm.S b/drivers/acorn/block/mfm.S index 1efc5ece7..c90cbd41c 100644 --- a/drivers/acorn/block/mfm.S +++ b/drivers/acorn/block/mfm.S @@ -1,44 +1,43 @@ -@ Read/Write DMA code for the ST506/MFM hard drive controllers on the A400 -@ motherboard on ST506 podules. -@ (c) David Alan Gilbert (gilbertd@cs.man.ac.uk) 1996 +@ Read/Write DMA code for the ST506/MFM hard drive controllers on the A400 Acorn Archimedes +@ motherboard and on ST506 expansion podules. +@ (c) David Alan Gilbert (linux@treblig.org) 1996-1999 #include <asm/assembler.h> -_hdc63463_irqdata: +hdc63463_irqdata: @ Controller base address - .global _hdc63463_baseaddress -_hdc63463_baseaddress: + .global hdc63463_baseaddress +hdc63463_baseaddress: .word 0 - .global _hdc63463_irqpolladdress -_hdc63463_irqpolladdress: + .global hdc63463_irqpolladdress +hdc63463_irqpolladdress: .word 0 - .global _hdc63463_irqpollmask -_hdc63463_irqpollmask: + .global hdc63463_irqpollmask +hdc63463_irqpollmask: .word 0 @ where to read/write data from the kernel data space - .global _hdc63463_dataptr -_hdc63463_dataptr: + .global hdc63463_dataptr +hdc63463_dataptr: .word 0 @ Number of bytes left to transfer - .global _hdc63463_dataleft -_hdc63463_dataleft: + .global hdc63463_dataleft +hdc63463_dataleft: .word 0 @ ------------------------------------------------------------------------- @ hdc63463_writedma: DMA from host to controller @ internal reg usage: r0=hdc base address, r1=irq poll address, r2=poll mask @ r3=data ptr, r4=data left, r5,r6=temporary - .global _hdc63463_writedma -_hdc63463_writedma: + .global hdc63463_writedma +hdc63463_writedma: stmfd sp!,{r4-r7} - adr r5,_hdc63463_irqdata + adr r5,hdc63463_irqdata ldmia r5,{r0,r1,r2,r3,r4} - writedma_again: @ test number of remaining bytes to transfer @@ -89,12 +88,12 @@ writedma_loop: @ If we were too slow we had better go through again - DAG - took out with new interrupt routine @ sub r0,r0,#32+8 - @ adr r2,_hdc63463_irqdata + @ adr r2,hdc63463_irqdata @ ldr r2,[r2,#8] @ b writedma_again writedma_end: - adr r5,_hdc63463_irqdata+12 + adr r5,hdc63463_irqdata+12 stmia r5,{r3,r4} ldmfd sp!,{r4-r7} RETINSTR(mov,pc,lr) @@ -103,10 +102,10 @@ writedma_end: @ hdc63463_readdma: DMA from controller to host @ internal reg usage: r0=hdc base address, r1=irq poll address, r2=poll mask @ r3=data ptr, r4=data left, r5,r6=temporary - .global _hdc63463_readdma -_hdc63463_readdma: + .global hdc63463_readdma +hdc63463_readdma: stmfd sp!,{r4-r7} - adr r5,_hdc63463_irqdata + adr r5,hdc63463_irqdata ldmia r5,{r0,r1,r2,r3,r4} readdma_again: @@ -157,7 +156,7 @@ readdma_loop: @ b readdma_again readdma_end: - adr r5,_hdc63463_irqdata+12 + adr r5,hdc63463_irqdata+12 stmia r5,{r3,r4} ldmfd sp!,{r4-r7} RETINSTR(mov,pc,lr) diff --git a/drivers/acorn/block/mfmhd.c b/drivers/acorn/block/mfmhd.c index 219f10034..7a8ea6f8e 100644 --- a/drivers/acorn/block/mfmhd.c +++ b/drivers/acorn/block/mfmhd.c @@ -123,6 +123,7 @@ #include <asm/dma.h> #include <asm/hardware.h> #include <asm/ecard.h> +#include <asm/ioc.h> /* * This sort of stuff should be in a header file shared with ide.c, hd.c, xd.c etc @@ -261,7 +262,9 @@ static struct cont { void (*done) (int st); /* done handler */ } *cont = NULL; +#if 0 static struct tq_struct mfm_tq = {0, 0, (void (*)(void *)) NULL, 0}; +#endif int number_mfm_drives = 1; diff --git a/drivers/acorn/char/Config.in b/drivers/acorn/char/Config.in deleted file mode 100644 index ede0a8266..000000000 --- a/drivers/acorn/char/Config.in +++ /dev/null @@ -1,15 +0,0 @@ -if [ "$CONFIG_SERIAL" != "n" ]; then - tristate ' Atomwide serial port support' CONFIG_ATOMWIDE_SERIAL - tristate ' Dual serial port support' CONFIG_DUALSP_SERIAL -fi - -if [ "$CONFIG_MOUSE" = "y" ]; then - if [ "$CONFIG_ARCH_ACORN" = "y" ]; then - if [ "$CONFIG_ARCH_RPC" != "y" ]; then - define_bool CONFIG_KBDMOUSE y - else - define_bool CONFIG_RPCMOUSE y - fi - fi -fi - diff --git a/drivers/acorn/char/Makefile b/drivers/acorn/char/Makefile index ecbbfe90c..c3c6f67ba 100644 --- a/drivers/acorn/char/Makefile +++ b/drivers/acorn/char/Makefile @@ -9,20 +9,21 @@ # parent makes.. # -L_TARGET := acorn-char.a -M_OBJS := -L_OBJS := +L_TARGET := acorn-char.a +M_OBJS := +L_OBJS := -ifeq ($(MACHINE),rpc) - MOUSE_OBJS += mouse_rpc.o - L_OBJS += keyb_ps2.o -endif +L_OBJS_arc := keyb_arc.o +L_OBJS_a5k := keyb_arc.o +L_OBJS_rpc := keyb_ps2.o -ifeq ($(CONFIG_MOUSE),y) - LX_OBJS += $(MOUSE_OBJS) -else - ifeq ($(CONFIG_MOUSE),m) - MX_OBJS += $(MOUSE_OBJS) +ifeq ($(MACHINE),rpc) + ifeq ($(CONFIG_MOUSE),y) + LX_OBJS += mouse_rpc.o + else + ifeq ($(CONFIG_MOUSE),m) + MX_OBJS += mouse_rpc.o + endif endif endif @@ -42,4 +43,6 @@ else endif endif +L_OBJS += $(L_OBJS_$(MACHINE)) + include $(TOPDIR)/Rules.make diff --git a/drivers/acorn/char/keyb_arc.c b/drivers/acorn/char/keyb_arc.c new file mode 100644 index 000000000..01c496a2b --- /dev/null +++ b/drivers/acorn/char/keyb_arc.c @@ -0,0 +1,451 @@ +/* + * linux/arch/arm/drivers/char1/keyb_arc.c + * + * Acorn keyboard driver for ARM Linux. + * + * The Acorn keyboard appears to have a ***very*** buggy reset protocol - + * every reset behaves differently. We try to get round this by attempting + * a few things... + */ + +#include <linux/config.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/mm.h> +#include <linux/malloc.h> +#include <linux/ptrace.h> +#include <linux/signal.h> +#include <linux/timer.h> +#include <linux/random.h> +#include <linux/ctype.h> +#include <linux/init.h> +#include <linux/kbd_ll.h> +#include <linux/tty.h> +#include <linux/kbd_kern.h> +#include <linux/delay.h> + +#include <asm/bitops.h> +#include <asm/keyboard.h> +#include <asm/irq.h> +#include <asm/ioc.h> +#include <asm/hardware.h> + +#include "../../char/mouse.h" + +extern void kbd_reset_kdown(void); + +#define VERSION 108 + +#define KBD_REPORT_ERR +#define KBD_REPORT_UNKN + +#include <asm/io.h> +#include <asm/system.h> + +static char kbd_txval[4]; +static unsigned char kbd_txhead, kbd_txtail; +#define KBD_INCTXPTR(ptr) ((ptr) = ((ptr) + 1) & 3) +static int kbd_id = -1; +static struct wait_queue *kbd_waitq; +#ifdef CONFIG_KBDMOUSE +static int mousedev; +#endif + +/* + * Protocol codes to send the keyboard. + */ +#define HRST 0xff /* reset keyboard */ +#define RAK1 0xfe /* reset response */ +#define RAK2 0xfd /* reset response */ +#define BACK 0x3f /* Ack for first keyboard pair */ +#define SMAK 0x33 /* Last data byte ack (key scanning + mouse movement scanning) */ +#define MACK 0x32 /* Last data byte ack (mouse movement scanning) */ +#define SACK 0x31 /* Last data byte ack (key scanning) */ +#define NACK 0x30 /* Last data byte ack (no scanning, mouse data) */ +#define RQMP 0x22 /* Request mouse data */ +#define PRST 0x21 /* nothing */ +#define RQID 0x20 /* Request ID */ + +#define UP_FLAG 1 + +#ifdef CONFIG_MAGIC_SYSRQ +unsigned char a5kkbd_sysrq_xlate[] = +{ + 27, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + '`', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '0', '-', '=', '£', 127, 0, + 0, 0, 0, '/', '*', '#', 9, 'q', + 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', + 'p', '[', ']', '\\', 22, 23, 25, '7', + '8', '9', '-', 0, 'a', 's', 'd', 'f', + 'g', 'h', 'j', 'k', 'l', ';', '\'', 13, + '4', '5', '6', '+', 0, 0, 'z', 'x', + 'c', 'v', 'b', 'n', 'm', ',', '.', '/', + 0, 0, '1', '2', '3', 0, 0, ' ', + 0, 0, 0, 0, 0, '0', '.', 10, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +}; +#endif + +/* + * This array converts the scancode that we get from the keyboard to the + * real rows/columns on the A5000 keyboard. This might be keyboard specific... + * + * It is these values that we use to maintain the key down array. That way, we + * should pick up on the ghost key presses (which is what happens when you press + * three keys, and the keyboard thinks you have pressed four!) + * + * Row 8 (0x80+c) is actually a column with one key per row. It is isolated from + * the other keys, and can't cause these problems (its used for shift, ctrl, alt etc). + * + * Illegal scancodes are denoted by an 0xff (in other words, we don't know about + * them, and can't process them for ghosts). This does however, cause problems with + * autorepeat processing... + */ +static unsigned char scancode_2_colrow[256] = { + 0x01, 0x42, 0x32, 0x33, 0x43, 0x56, 0x5a, 0x6c, 0x7c, 0x5c, 0x5b, 0x6b, 0x7b, 0x84, 0x70, 0x60, + 0x11, 0x51, 0x62, 0x63, 0x44, 0x54, 0x55, 0x45, 0x46, 0x4a, 0x3c, 0x4b, 0x59, 0x49, 0x69, 0x79, + 0x83, 0x40, 0x30, 0x3b, 0x39, 0x38, 0x31, 0x61, 0x72, 0x73, 0x64, 0x74, 0x75, 0x65, 0x66, 0x6a, + 0x1c, 0x2c, 0x7a, 0x36, 0x48, 0x68, 0x78, 0x20, 0x2b, 0x29, 0x28, 0x81, 0x71, 0x22, 0x23, 0x34, + 0x24, 0x25, 0x35, 0x26, 0x3a, 0x0c, 0x2a, 0x76, 0x10, 0x1b, 0x19, 0x18, 0x82, 0xff, 0x21, 0x12, + 0x13, 0x14, 0x04, 0x05, 0x15, 0x16, 0x1a, 0x0a, 0x85, 0x77, 0x00, 0x0b, 0x09, 0x02, 0x80, 0x03, + 0x87, 0x86, 0x06, 0x17, 0x27, 0x07, 0x37, 0x08, 0xff, +}; + +#define BITS_PER_SHORT (8*sizeof(unsigned short)) +static unsigned short ghost_down[128/BITS_PER_SHORT]; + +static void a5kkbd_key(unsigned int keycode, unsigned int up_flag) +{ + unsigned int real_keycode; + + if (keycode > 0x72) { +#ifdef KBD_REPORT_UNKN + printk ("kbd: unknown scancode 0x%04x\n", keycode); +#endif + return; + } + if (keycode >= 0x70) { +#ifdef CONFIG_KBDMOUSE + if (mousedev >= 0) + switch (keycode) { + case 0x70: /* Left mouse button */ + busmouse_add_buttons(mousedev, 4, up_flag ? 4 : 0); + break; + + case 0x71: /* Middle mouse button */ + busmouse_add_buttons(mousedev, 2, up_flag ? 2 : 0); + break; + + case 0x72:/* Right mouse button */ + busmouse_add_buttons(mousedev, 1, up_flag ? 1 : 0); + break; + } +#endif + return; + } + + /* + * We have to work out if we accept this key press as a real key, or + * if it is a ghost. IE. If you press three keys, the keyboard will think + * that you've pressed a fourth: (@ = key down, # = ghost) + * + * 0 1 2 3 4 5 6 7 + * | | | | | | | | + * 0-+-+-+-+-+-+-+-+- + * | | | | | | | | + * 1-+-@-+-+-+-@-+-+- + * | | | | | | | | + * 2-+-+-+-+-+-+-+-+- + * | | | | | | | | + * 3-+-@-+-+-+-#-+-+- + * | | | | | | | | + * + * This is what happens when you have a matrix keyboard... + */ + + real_keycode = scancode_2_colrow[keycode]; + + if ((real_keycode & 0x80) == 0) { + int rr, kc = (real_keycode >> 4) & 7; + int cc; + unsigned short res, kdownkc; + + kdownkc = ghost_down[kc] | (1 << (real_keycode & 15)); + + for (rr = 0; rr < 128/BITS_PER_SHORT; rr++) + if (rr != kc && (res = ghost_down[rr] & kdownkc)) { + /* + * we have found a second row with at least one key pressed in the + * same column. + */ + for (cc = 0; res; res >>= 1) + cc += (res & 1); + if (cc > 1) + return; /* ignore it */ + } + if (up_flag) + clear_bit (real_keycode, ghost_down); + else + set_bit (real_keycode, ghost_down); + } + + handle_scancode(keycode, !up_flag); +} + +static inline void a5kkbd_sendbyte(unsigned char val) +{ + kbd_txval[kbd_txhead] = val; + KBD_INCTXPTR(kbd_txhead); + enable_irq(IRQ_KEYBOARDTX); +} + +static inline void a5kkbd_reset(void) +{ + int i; + + for (i = 0; i < NR_SCANCODES/BITS_PER_SHORT; i++) + ghost_down[i] = 0; + + kbd_reset_kdown(); +} + +void a5kkbd_leds(unsigned char leds) +{ + leds = ((leds & (1<<VC_SCROLLOCK))?4:0) | ((leds & (1<<VC_NUMLOCK))?2:0) | + ((leds & (1<<VC_CAPSLOCK))?1:0); + a5kkbd_sendbyte(leds); +} + +/* Keyboard states: + * 0 initial reset condition, receive HRST, send RRAK1 + * 1 Sent RAK1, wait for RAK1, send RRAK2 + * 2 Sent RAK2, wait for RAK2, send SMAK or RQID + * 3 Sent RQID, expect KBID, send SMAK + * 4 Sent SMAK, wait for anything + * 5 Wait for second keyboard nibble for key pressed + * 6 Wait for second keyboard nibble for key released + * 7 Wait for second part of mouse data + * + * This function returns 1 when we successfully enter the IDLE state + * (and hence need to do some keyboard processing). + */ +#define KBD_INITRST 0 +#define KBD_RAK1 1 +#define KBD_RAK2 2 +#define KBD_ID 3 +#define KBD_IDLE 4 +#define KBD_KEYDOWN 5 +#define KBD_KEYUP 6 +#define KBD_MOUSE 7 + +static int handle_rawcode(unsigned int keyval) +{ + static signed char kbd_mousedx = 0; + signed char kbd_mousedy; + static unsigned char kbd_state = KBD_INITRST; + static unsigned char kbd_keyhigh = 0; + + if (keyval == HRST && kbd_state != KBD_INITRST && kbd_state != KBD_ID) { + a5kkbd_sendbyte (HRST); + a5kkbd_reset (); + kbd_state = KBD_INITRST; + } else switch(kbd_state) { + case KBD_INITRST: /* hard reset - sent HRST */ + if (keyval == HRST) { + a5kkbd_sendbyte (RAK1); + kbd_state = KBD_RAK1; + } else if (keyval == RAK1) { + /* Some A5000 keyboards are very fussy and don't follow Acorn's + * specs - this appears to fix them, but them it might stop + * them from being initialised. + * fix by Philip Blundell + */ + printk(KERN_DEBUG "keyboard sent early RAK1 -- ignored\n"); + } else + goto kbd_wontreset; + break; + + case KBD_RAK1: /* sent RAK1 - expect RAK1 and send RAK2 */ + if (keyval == RAK1) { + a5kkbd_sendbyte (RAK2); + kbd_state = KBD_RAK2; + } else + goto kbd_wontreset; + break; + + case KBD_RAK2: /* Sent RAK2 - expect RAK2 and send either RQID or SMAK */ + if (keyval == RAK2) { + if (kbd_id == -1) { + a5kkbd_sendbyte (NACK); + a5kkbd_sendbyte (RQID); + kbd_state = KBD_ID; + } else { + a5kkbd_sendbyte (SMAK); + kbd_state = KBD_IDLE; + } + } else + goto kbd_wontreset; + break; + + case KBD_ID: /* Sent RQID - expect KBID */ + if (keyval == HRST) { + kbd_id = -2; + a5kkbd_reset (); + a5kkbd_sendbyte (HRST); + kbd_state = KBD_INITRST; + wake_up (&kbd_waitq); + } else if ((keyval & 0xc0) == 0x80) { + kbd_id = keyval & 0x3f; + a5kkbd_sendbyte (SMAK); + kbd_state = KBD_IDLE; + wake_up (&kbd_waitq); + } + break; + + case KBD_IDLE: /* Send SMAK, ready for any reply */ + switch (keyval & 0xf0) { + default: /* 0x00 - 0x7f */ + kbd_mousedx = keyval & 0x40 ? keyval|0x80 : keyval; + kbd_state = KBD_MOUSE; + a5kkbd_sendbyte (BACK); + break; + + case 0x80: + case 0x90: + case 0xa0: + case 0xb0: + if (kbd_id == -1) + kbd_id = keyval & 0x3f; + break; + + case 0xc0: + kbd_keyhigh = keyval; + kbd_state = KBD_KEYDOWN; + a5kkbd_sendbyte (BACK); + break; + + case 0xd0: + kbd_keyhigh = keyval; + kbd_state = KBD_KEYUP; + a5kkbd_sendbyte (BACK); + break; + + case 0xe0: + case 0xf0: + goto kbd_error; + } + break; + + case KBD_KEYDOWN: + if ((keyval & 0xf0) != 0xc0) + goto kbd_error; + else { + kbd_state = KBD_IDLE; + a5kkbd_sendbyte (SMAK); + if (((kbd_keyhigh ^ keyval) & 0xf0) == 0) + a5kkbd_key ((keyval & 0x0f) | ((kbd_keyhigh << 4) & 0xf0), 0); + } + break; + + case KBD_KEYUP: + if ((keyval & 0xf0) != 0xd0) + goto kbd_error; + else { + kbd_state = KBD_IDLE; + a5kkbd_sendbyte (SMAK); + if (((kbd_keyhigh ^ keyval) & 0xf0) == 0) + a5kkbd_key ((keyval & 0x0f) | ((kbd_keyhigh << 4) & 0xf0), UP_FLAG); + } + break; + + case KBD_MOUSE: + if (keyval & 0x80) + goto kbd_error; + else { + kbd_state = KBD_IDLE; + a5kkbd_sendbyte (SMAK); + kbd_mousedy = (char)(keyval & 0x40 ? keyval | 0x80 : keyval); +#ifdef CONFIG_KBDMOUSE + if (mousedev >= 0) + busmouse_add_movement(mousedev, (int)kbd_mousedx, (int)kbd_mousedy); +#endif + } + } + return kbd_state == KBD_IDLE ? 1 : 0; + +kbd_wontreset: +#ifdef KBD_REPORT_ERR + printk ("kbd: keyboard won't reset (kbdstate %d, keyval %02X)\n", + kbd_state, keyval); +#endif + mdelay(1); + inb(IOC_KARTRX); + a5kkbd_sendbyte (HRST); + kbd_state = KBD_INITRST; + return 0; + +kbd_error: +#ifdef KBD_REPORT_ERR + printk ("kbd: keyboard out of sync - resetting\n"); +#endif + a5kkbd_sendbyte (HRST); + kbd_state = KBD_INITRST; + return 0; +} + +static void a5kkbd_rx(int irq, void *dev_id, struct pt_regs *regs) +{ + kbd_pt_regs = regs; + if (handle_rawcode(inb(IOC_KARTRX))) + mark_bh (KEYBOARD_BH); +} + +static void a5kkbd_tx(int irq, void *dev_id, struct pt_regs *regs) +{ + outb (kbd_txval[kbd_txtail], IOC_KARTTX); + KBD_INCTXPTR(kbd_txtail); + if (kbd_txtail == kbd_txhead) + disable_irq(irq); +} + +#ifdef CONFIG_KBDMOUSE +static struct busmouse a5kkbd_mouse = { + 6, "kbdmouse", NULL, NULL, 7 +}; +#endif + +__initfunc(void a5kkbd_init_hw (void)) +{ + unsigned long flags; + + save_flags_cli (flags); + if (request_irq (IRQ_KEYBOARDTX, a5kkbd_tx, 0, "keyboard", NULL) != 0) + panic("Could not allocate keyboard transmit IRQ!"); + disable_irq (IRQ_KEYBOARDTX); + if (request_irq (IRQ_KEYBOARDRX, a5kkbd_rx, 0, "keyboard", NULL) != 0) + panic("Could not allocate keyboard receive IRQ!"); + (void)inb(IOC_KARTRX); + restore_flags (flags); + + a5kkbd_sendbyte (HRST); /* send HRST (expect HRST) */ + + /* wait 1s for keyboard to initialise */ + interruptible_sleep_on_timeout(&kbd_waitq, HZ); + +#ifdef CONFIG_KBDMOUSE + mousedev = register_busmouse(&a5kkbd_mouse); + if (mousedev < 0) + printk(KERN_ERR "Unable to register mouse driver\n"); +#endif + + printk (KERN_INFO "Keyboard driver v%d.%02d. (", VERSION/100, VERSION%100); + if (kbd_id != -1) + printk ("id=%d ", kbd_id); + printk ("English)\n"); +} diff --git a/drivers/acorn/char/keyb_ps2.c b/drivers/acorn/char/keyb_ps2.c index 4af051f44..3fa8a03ad 100644 --- a/drivers/acorn/char/keyb_ps2.c +++ b/drivers/acorn/char/keyb_ps2.c @@ -25,6 +25,7 @@ #include <asm/irq.h> #include <asm/hardware.h> #include <asm/io.h> +#include <asm/iomd.h> #include <asm/system.h> extern void kbd_reset_kdown(void); @@ -221,14 +222,7 @@ unsigned char ps2kbd_sysrq_xlate[] = }; #endif -int ps2kbd_translate(unsigned char scancode, unsigned char *keycode_p, char *uf_p) -{ - *uf_p = scancode & 0200; - *keycode_p = scancode & 0x7f; - return 1; -} - -static void ps2kbd_key(unsigned int keycode, unsigned int up_flag) +static inline void ps2kbd_key(unsigned int keycode, unsigned int up_flag) { handle_scancode(keycode, !up_flag); } diff --git a/drivers/acorn/char/mouse_rpc.c b/drivers/acorn/char/mouse_rpc.c index a5c2b2bff..4eecd0cc1 100644 --- a/drivers/acorn/char/mouse_rpc.c +++ b/drivers/acorn/char/mouse_rpc.c @@ -1,5 +1,5 @@ /* - * linux/drivers/char/rpcmouse.c + * linux/drivers/char/mouse_rpc.c * * Copyright (C) 1996-1998 Russell King * @@ -16,6 +16,7 @@ #include <asm/hardware.h> #include <asm/irq.h> #include <asm/io.h> +#include <asm/iomd.h> #include "../../char/mouse.h" diff --git a/drivers/acorn/char/serial-card.c b/drivers/acorn/char/serial-card.c index 23a625658..a1ea1c61f 100644 --- a/drivers/acorn/char/serial-card.c +++ b/drivers/acorn/char/serial-card.c @@ -33,14 +33,20 @@ #ifdef MODULE static int __serial_ports[NUM_SERIALS]; static int __serial_pcount; +static int __serial_addr[NUM_SERIALS]; static struct expansion_card *expcard[MAX_ECARDS]; #define ADD_ECARD(ec,card) expcard[(card)] = (ec) -#define ADD_PORT(port) __serial_ports[__serial_pcount++] = (port) +#define ADD_PORT(port,addr) \ + do { \ + __serial_ports[__serial_pcount] = (port); \ + __serial_addr[__serial_pcount] = (addr); \ + __serial_pcount += 1; \ + } while (0) #undef MY_INIT #define MY_INIT init_module #else #define ADD_ECARD(ec,card) -#define ADD_PORT(port) +#define ADD_PORT(port,addr) #endif static const card_ids serial_cids[] = { MY_CARD_LIST, { 0xffff, 0xffff } }; @@ -75,12 +81,15 @@ int MY_INIT (void) cardaddr = MY_BASE_ADDRESS(ec); for (port = 0; port < MY_NUMPORTS; port ++) { + unsigned long address; int line; - line = serial_register_onedev (MY_PORT_ADDRESS(port, cardaddr), ec->irq); + address = MY_PORT_ADDRESS(port, cardaddr); + + line = serial_register_onedev (address, ec->irq); if (line < 0) break; - ADD_PORT(line); + ADD_PORT(line, address); } if (port) { @@ -97,8 +106,10 @@ void cleanup_module (void) { int i; - for (i = 0; i < __serial_pcount; i++) - unregister_serial (__serial_ports[i]); + for (i = 0; i < __serial_pcount; i++) { + unregister_serial(__serial_ports[i]); + release_region(__serial_addr[i], 8); + } for (i = 0; i < MAX_ECARDS; i++) if (expcard[i]) diff --git a/drivers/acorn/net/ether1.c b/drivers/acorn/net/ether1.c index 1d2283f3d..31e7cc087 100644 --- a/drivers/acorn/net/ether1.c +++ b/drivers/acorn/net/ether1.c @@ -71,7 +71,7 @@ static char *version = "ether1 ethernet driver (c) 1995 Russell King v1.05\n"; #define BUS_16 16 #define BUS_8 8 -static const card_ids ether1_cids[] = { +static const card_ids __init ether1_cids[] = { { MANU_ACORN, PROD_ACORN_ETHER1 }, { 0xffff, 0xffff } }; @@ -128,7 +128,7 @@ ether1_inswb (unsigned int addr, void *data, unsigned int len) { int used; - addr = IO_BASE + (addr << 2); + addr = ioaddr(addr); __asm__ __volatile__( "subs %3, %3, #2 @@ -171,7 +171,7 @@ ether1_outswb (unsigned int addr, void *data, unsigned int len) { int used; - addr = IO_BASE + (addr << 2); + addr = ioaddr(addr); __asm__ __volatile__( "subs %3, %3, #2 @@ -659,12 +659,6 @@ ether1_probe1 (struct device *dev)) /* Fill in the fields of the device structure with ethernet values */ ether_setup (dev); -#ifndef CLAIM_IRQ_AT_OPEN - if (request_irq (dev->irq, ether1_interrupt, 0, "ether1", dev)) { - kfree (dev->priv); - return -EAGAIN; - } -#endif return 0; } @@ -759,18 +753,16 @@ static int ether1_open (struct device *dev) { struct ether1_priv *priv = (struct ether1_priv *)dev->priv; -#ifdef CLAIM_IRQ_AT_OPEN + if (request_irq (dev->irq, ether1_interrupt, 0, "ether1", dev)) return -EAGAIN; -#endif + MOD_INC_USE_COUNT; memset (&priv->stats, 0, sizeof (struct enet_statistics)); if (ether1_init_for_open (dev)) { -#ifdef CLAIM_IRQ_AT_OPEN free_irq (dev->irq, dev); -#endif MOD_DEC_USE_COUNT; return -EAGAIN; } @@ -1080,12 +1072,10 @@ ether1_interrupt (int irq, void *dev_id, struct pt_regs *regs) static int ether1_close (struct device *dev) { -#ifdef CLAIM_IRQ_AT_OPEN - free_irq (dev->irq, dev); -#endif - ether1_reset (dev); + free_irq(dev->irq, dev); + dev->start = 0; dev->tbusy = 0; @@ -1117,56 +1107,46 @@ ether1_setmulticastlist (struct device *dev) #ifdef MODULE -static char ethernames[MAX_ECARDS][9]; -static struct device *my_ethers[MAX_ECARDS]; -static struct expansion_card *ec[MAX_ECARDS]; +static struct ether_dev { + struct expansion_card *ec; + char name[9]; + struct device dev; +} ether_devs[MAX_ECARDS]; int init_module (void) { - int i; + struct expansion_card *ec; + int i, ret = -ENODEV; - for (i = 0; i < MAX_ECARDS; i++) { - my_ethers[i] = NULL; - ec[i] = NULL; - strcpy (ethernames[i], " "); - } + memset(ether_devs, 0, sizeof(ether_devs)); + ecard_startfind (); + ec = ecard_find(0, ether1_cids); i = 0; - ecard_startfind (); + while (ec && i < MAX_ECARDS) { + ecard_claim(ec); - do { - if ((ec[i] = ecard_find(0, ether1_cids)) == NULL) - break; + ether_devs[i].ec = ec; + ether_devs[i].dev.irq = ec->irq; + ether_devs[i].dev.base_addr = ecard_address(ec, ECARD_IOC, ECARD_FAST); + ether_devs[i].dev.init = ether1_probe; + ether_devs[i].dev.name = ether_devs[i].name; - my_ethers[i] = (struct device *)kmalloc (sizeof (struct device), GFP_KERNEL); - memset (my_ethers[i], 0, sizeof (struct device)); - - my_ethers[i]->irq = ec[i]->irq; - my_ethers[i]->base_addr = ecard_address (ec[i], ECARD_IOC, ECARD_FAST); - my_ethers[i]->init = ether1_probe; - my_ethers[i]->name = ethernames[i]; - - ecard_claim (ec[i]); - - if (register_netdev (my_ethers[i]) != 0) { - for (i = 0; i < 4; i++) { - if (my_ethers[i]) { - kfree (my_ethers[i]); - my_ethers[i] = NULL; - } - if (ec[i]) { - ecard_release (ec[i]); - ec[i] = NULL; - } - } - return -EIO; + ret = register_netdev(ðer_devs[i].dev); + + if (ret) { + ecard_release(ec); + ether_devs[i].ec = NULL; + break; } - i++; - } while (i < MAX_ECARDS); - return i != 0 ? 0 : -ENODEV; + i += 1; + ec = ecard_find(0, ether1_cids); + } + + return i != 0 ? 0 : ret; } void @@ -1175,18 +1155,15 @@ cleanup_module (void) int i; for (i = 0; i < MAX_ECARDS; i++) { - if (my_ethers[i]) { - unregister_netdev (my_ethers[i]); - release_region (my_ethers[i]->base_addr, 16); - release_region (my_ethers[i]->base_addr + 0x800, 4096); -#ifndef CLAIM_IRQ_AT_OPEN - free_irq (my_ethers[i]->irq, my_ethers[i]); -#endif - my_ethers[i] = NULL; - } - if (ec[i]) { - ecard_release (ec[i]); - ec[i] = NULL; + if (ether_devs[i].ec) { + unregister_netdev(ðer_devs[i].dev); + + release_region(ether_devs[i].dev.base_addr, 16); + release_region(ether_devs[i].dev.base_addr + 0x800, 4096); + + ecard_release(ether_devs[i].ec); + + ether_devs[i].ec = NULL; } } } diff --git a/drivers/acorn/net/ether3.c b/drivers/acorn/net/ether3.c index ca89b7e59..ea6c13da5 100644 --- a/drivers/acorn/net/ether3.c +++ b/drivers/acorn/net/ether3.c @@ -33,11 +33,12 @@ * packet starts two bytes from the end of the * buffer, it corrupts the receiver chain, and * never updates the transmit status correctly. - * TODO: - * When we detect a fatal error on the interface, we should restart it. + * 1.14 RMK 07/01/1998 Added initial code for ETHERB addressing. + * 1.15 RMK 30/04/1999 More fixes to the transmit routine for buggy + * hardware. */ -static char *version = "ether3 ethernet driver (c) 1995-1998 R.M.King v1.13\n"; +static char *version = "ether3 ethernet driver (c) 1995-1999 R.M.King v1.15\n"; #include <linux/module.h> #include <linux/kernel.h> @@ -66,7 +67,7 @@ static char *version = "ether3 ethernet driver (c) 1995-1998 R.M.King v1.13\n"; #include "ether3.h" static unsigned int net_debug = NET_DEBUG; -static const card_ids ether3_cids[] = { +static const card_ids __init ether3_cids[] = { { MANU_ANT2, PROD_ANT_ETHER3 }, { MANU_ANT, PROD_ANT_ETHER3 }, { MANU_ANT, PROD_ANT_ETHERB }, /* trial - will etherb work? */ @@ -77,9 +78,6 @@ static void ether3_setmulticastlist(struct device *dev); static int ether3_rx(struct device *dev, struct dev_priv *priv, unsigned int maxcnt); static void ether3_tx(struct device *dev, struct dev_priv *priv); -extern int inswb(int reg, void *buffer, int len); -extern int outswb(int reg, void *buffer, int len); - #define BUS_16 2 #define BUS_8 1 #define BUS_UNKNOWN 0 @@ -88,7 +86,7 @@ extern int outswb(int reg, void *buffer, int len); * I'm not sure what address we should default to if the internal one * is corrupted... */ -unsigned char def_eth_addr[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; +unsigned char def_eth_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'}; /* --------------------------------------------------------------------------- */ @@ -99,6 +97,8 @@ typedef enum { /* * ether3 read/write. Slow things down a bit... + * The SEEQ8005 doesn't like us writing to it's registers + * too quickly. */ #define ether3_outb(v,r) { outb((v),(r)); udelay(1); } #define ether3_outw(v,r) { outw((v),(r)); udelay(1); } @@ -138,7 +138,7 @@ ether3_setbuffer(struct device *dev, buffer_rw_t read, int start) * write data to the buffer memory */ #define ether3_writebuffer(dev,data,length) \ - outswb(REG_BUFWIN, (data), (length)) + outsw(REG_BUFWIN, (data), (length) >> 1) #define ether3_writeword(dev,data) \ outw((data), REG_BUFWIN) @@ -153,7 +153,7 @@ ether3_setbuffer(struct device *dev, buffer_rw_t read, int start) * read data from the buffer memory */ #define ether3_readbuffer(dev,data,length) \ - inswb(REG_BUFWIN, (data), (length)) + insw(REG_BUFWIN, (data), (length) >> 1) #define ether3_readword(dev) \ inw(REG_BUFWIN) @@ -249,7 +249,7 @@ ether3_ramtest(struct device *dev, unsigned char byte)) } } else { if (bad != -1) { - if (bad != i - 1) + if (bad != i - 1) printk(" - 0x%04X\n", i - 1); printk("\n"); bad = -1; @@ -335,7 +335,6 @@ ether3_init_for_open(struct device *dev) for (i = 0; i < 6; i++) ether3_outb(dev->dev_addr[i], REG_BUFWIN); - priv->tx_used = 0; priv->tx_head = 0; priv->tx_tail = 0; priv->regs.config2 |= CFG2_CTRLO; @@ -471,6 +470,25 @@ failed: return error; } +__initfunc(static void +ether3_get_dev(struct device *dev, struct expansion_card *ec)) +{ + ecard_claim(ec); + + dev->base_addr = ecard_address(ec, ECARD_MEMC, 0); + dev->irq = ec->irq; + + if (ec->cid.manufacturer == MANU_ANT && + ec->cid.product == PROD_ANT_ETHERB) { + dev->base_addr += 0x200; + } + + ec->irqaddr = (volatile unsigned char *)ioaddr(dev->base_addr); + ec->irqmask = 0xf0; + + ether3_addr(dev->dev_addr, ec); +} + #ifndef MODULE __initfunc(int ether3_probe(struct device *dev)) @@ -485,12 +503,8 @@ ether3_probe(struct device *dev)) if ((ec = ecard_find(0, ether3_cids)) == NULL) return ENODEV; - dev->base_addr = ecard_address(ec, ECARD_MEMC, 0); - dev->irq = ec->irq; - - ecard_claim(ec); + ether3_get_dev(dev, ec); - ether3_addr(dev->dev_addr, ec); return ether3_probe1(dev); } #endif @@ -581,33 +595,6 @@ static void ether3_setmulticastlist(struct device *dev) } /* - * Allocate memory in transmitter ring buffer. - */ -static int -ether3_alloc_tx(struct dev_priv *priv, int length, int alloc) -{ - int start, head, tail; - - tail = priv->tx_tail; - start = priv->tx_head; - head = start + length + 4; - - if (head >= TX_END) { - if (tail > priv->tx_head) - return -1; - head -= TX_END - TX_START; - if (tail < head) - return -1; - } else if (start < tail && tail < head) - return -1; - - if (alloc) - priv->tx_head = head; - - return start; -} - -/* * Transmit a packet */ static int @@ -622,7 +609,7 @@ retry: if (!test_and_set_bit(0, (void *)&dev->tbusy)) { unsigned long flags; unsigned int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; - int ptr; + unsigned int ptr, next_ptr; length = (length + 1) & ~1; @@ -633,23 +620,31 @@ retry: return 0; } + next_ptr = (priv->tx_head + 1) & 15; + save_flags_cli(flags); - ptr = ether3_alloc_tx(priv, length, 1); - if (ptr == -1) + if (priv->tx_tail == next_ptr) { + restore_flags(flags); return 1; /* unable to queue */ + } + + dev->trans_start = jiffies; + ptr = 0x600 * priv->tx_head; + priv->tx_head = next_ptr; + next_ptr *= 0x600; #define TXHDR_FLAGS (TXHDR_TRANSMIT|TXHDR_CHAINCONTINUE|TXHDR_DATAFOLLOWS|TXHDR_ENSUCCESS) - ether3_setbuffer(dev, buffer_write, priv->tx_head); + ether3_setbuffer(dev, buffer_write, next_ptr); ether3_writelong(dev, 0); - ether3_setbuffer(dev, buffer_write, ptr); ether3_writelong(dev, 0); ether3_writebuffer(dev, skb->data, length); - + ether3_writeword(dev, htons(next_ptr)); + ether3_writeword(dev, TXHDR_CHAINCONTINUE >> 16); ether3_setbuffer(dev, buffer_write, ptr); - ether3_writeword(dev, htons(priv->tx_head)); + ether3_writeword(dev, htons((ptr + length + 4))); ether3_writeword(dev, TXHDR_FLAGS >> 16); ether3_ledon(dev, priv); @@ -658,11 +653,10 @@ retry: ether3_outw(priv->regs.command | CMD_TXON, REG_COMMAND); } - if (ether3_alloc_tx(priv, 2044, 0) != -1) + next_ptr = (priv->tx_head + 1) & 15; + if (priv->tx_tail != next_ptr) dev->tbusy = 0; - dev->trans_start = jiffies; - restore_flags(flags); dev_kfree_skb(skb); @@ -689,7 +683,7 @@ retry: ether3_inw(REG_STATUS), ether3_inw(REG_CONFIG1), ether3_inw(REG_CONFIG2)); printk(KERN_ERR "%s: { rpr=%04X rea=%04X tpr=%04X }\n", dev->name, ether3_inw(REG_RECVPTR), ether3_inw(REG_RECVEND), ether3_inw(REG_TRANSMITPTR)); - printk(KERN_ERR "%s: tx head=%04X tx tail=%04X\n", dev->name, + printk(KERN_ERR "%s: tx head=%X tx tail=%X\n", dev->name, priv->tx_head, priv->tx_tail); ether3_setbuffer(dev, buffer_read, priv->tx_tail); printk(KERN_ERR "%s: packet status = %08X\n", dev->name, ether3_readlong(dev)); @@ -698,8 +692,9 @@ retry: dev->tbusy = 0; priv->regs.config2 |= CFG2_CTRLO; priv->stats.tx_errors += 1; - ether3_outw(priv->regs.config2 , REG_CONFIG2); + ether3_outw(priv->regs.config2, REG_CONFIG2); dev->trans_start = jiffies; + priv->tx_head = priv->tx_tail = 0; goto retry; } } @@ -867,6 +862,7 @@ static void ether3_tx(struct device *dev, struct dev_priv *priv) { unsigned int tx_tail = priv->tx_tail; + int max_work = 14; do { unsigned long status; @@ -874,7 +870,7 @@ ether3_tx(struct device *dev, struct dev_priv *priv) /* * Read the packet header */ - ether3_setbuffer(dev, buffer_read, tx_tail); + ether3_setbuffer(dev, buffer_read, tx_tail * 0x600); status = ether3_readlong(dev); /* @@ -895,95 +891,72 @@ ether3_tx(struct device *dev, struct dev_priv *priv) if (status & TXSTAT_BABBLED) priv->stats.tx_fifo_errors ++; } - tx_tail = htons(status & TX_NEXT); - if (tx_tail < TX_START || tx_tail >= TX_END) { - printk("%s: transmit error: next pointer = %04X\n", dev->name, tx_tail); - tx_tail = TX_START; - priv->tx_head = TX_START; - priv->tx_tail = TX_END; - } - } while (1); + tx_tail = (tx_tail + 1) & 15; + } while (--max_work); if (priv->tx_tail != tx_tail) { priv->tx_tail = tx_tail; - if (priv->tx_used <= MAX_TX_BUFFERED) { - dev->tbusy = 0; - mark_bh(NET_BH); /* Inform upper layers. */ - } + dev->tbusy = 0; + mark_bh(NET_BH); /* Inform upper layers. */ } } #ifdef MODULE -char ethernames[MAX_ECARDS][9]; - -static struct device *my_ethers[MAX_ECARDS]; -static struct expansion_card *ec[MAX_ECARDS]; +static struct ether_dev { + struct expansion_card *ec; + char name[9]; + struct device dev; +} ether_devs[MAX_ECARDS]; int init_module(void) { - int i; + struct expansion_card *ec; + int i, ret = -ENODEV; - for(i = 0; i < MAX_ECARDS; i++) { - my_ethers[i] = NULL; - ec[i] = NULL; - strcpy(ethernames[i], " "); - } + memset(ether_devs, 0, sizeof(ether_devs)); + ecard_startfind (); + ec = ecard_find(0, ether3_cids); i = 0; - ecard_startfind(); + while (ec && i < MAX_ECARDS) { + ecard_claim(ec); - do { - if ((ec[i] = ecard_find(0, ether3_cids)) == NULL) - break; + ether_devs[i].ec = ec; + ether_devs[i].dev.init = ether3_probe1; + ether_devs[i].dev.name = ether_devs[i].name; + ether3_get_dev(ðer_devs[i].dev, ec); - my_ethers[i] = (struct device *)kmalloc(sizeof(struct device), GFP_KERNEL); - memset(my_ethers[i], 0, sizeof(struct device)); + ret = register_netdev(ðer_devs[i].dev); - my_ethers[i]->irq = ec[i]->irq; - my_ethers[i]->base_addr= ecard_address(ec[i], ECARD_MEMC, 0); - my_ethers[i]->init = ether3_probe1; - my_ethers[i]->name = ethernames[i]; - - ether3_addr(my_ethers[i]->dev_addr, ec[i]); - - ecard_claim(ec[i]); + if (ret) { + ecard_release(ec); + ether_devs[i].ec = NULL; + } else + i += 1; - if(register_netdev(my_ethers[i]) != 0) { - for (i = 0; i < 4; i++) { - if(my_ethers[i]) { - kfree(my_ethers[i]); - my_ethers[i] = NULL; - } - if(ec[i]) { - ecard_release(ec[i]); - ec[i] = NULL; - } - } - return -EIO; - } - i++; + ec = ecard_find(0, ether3_cids); } - while(i < MAX_ECARDS); - return i != 0 ? 0 : -ENODEV; + return i != 0 ? 0 : ret; } void cleanup_module(void) { int i; + for (i = 0; i < MAX_ECARDS; i++) { - if (my_ethers[i]) { - release_region(my_ethers[i]->base_addr, 128); - unregister_netdev(my_ethers[i]); - my_ethers[i] = NULL; - } - if (ec[i]) { - ecard_release(ec[i]); - ec[i] = NULL; + if (ether_devs[i].ec) { + unregister_netdev(ðer_devs[i].dev); + + release_region(ether_devs[i].dev.base_addr, 128); + + ecard_release(ether_devs[i].ec); + + ether_devs[i].ec = NULL; } } } diff --git a/drivers/acorn/net/ether3.h b/drivers/acorn/net/ether3.h index 2a92052a4..ed9f2e6f7 100644 --- a/drivers/acorn/net/ether3.h +++ b/drivers/acorn/net/ether3.h @@ -151,9 +151,8 @@ struct dev_priv { unsigned int config1; unsigned int config2; } regs; - unsigned int tx_head; /* address to insert next packet */ - unsigned int tx_tail; /* address of transmitting packet */ - unsigned int tx_used; /* number of 'slots' used */ + unsigned char tx_head; /* buffer nr to insert next packet */ + unsigned char tx_tail; /* buffer nr of transmitting packet */ unsigned int rx_head; /* address to fetch next packet from */ struct enet_statistics stats; struct timer_list timer; diff --git a/drivers/acorn/net/etherh.c b/drivers/acorn/net/etherh.c index 4f88f6bb4..319b66cfc 100644 --- a/drivers/acorn/net/etherh.c +++ b/drivers/acorn/net/etherh.c @@ -37,6 +37,7 @@ #include <linux/etherdevice.h> #include <linux/skbuff.h> #include <linux/delay.h> +#include <linux/init.h> #include <asm/system.h> #include <asm/bitops.h> @@ -50,13 +51,16 @@ #define DEBUG_INIT 2 static unsigned int net_debug = NET_DEBUG; -static const card_ids etherh_cids[] = { +static const card_ids __init etherh_cids[] = { { MANU_I3, PROD_I3_ETHERLAN500 }, { MANU_I3, PROD_I3_ETHERLAN600 }, { MANU_I3, PROD_I3_ETHERLAN600A }, { 0xffff, 0xffff } }; +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("i3 EtherH driver"); + static char *version = "etherh [500/600/600A] ethernet driver (c) 1998 R.M.King v1.05\n"; #define ETHERH500_DATAPORT 0x200 /* MEMC */ @@ -80,8 +84,8 @@ static char *version = "etherh [500/600/600A] ethernet driver (c) 1998 R.M.King * Read the ethernet address string from the on board rom. * This is an ascii string... */ -static int -etherh_addr(char *addr, struct expansion_card *ec) +__initfunc(static int +etherh_addr(char *addr, struct expansion_card *ec)) { struct in_chunk_dir cd; char *s; @@ -216,10 +220,8 @@ etherh_block_output (struct device *dev, int count, const unsigned char *buf, in if (ei_status.word16) outsw (dma_addr, buf, count >> 1); -#ifdef BIT8 else outsb (dma_addr, buf, count); -#endif dma_start = jiffies; @@ -268,11 +270,8 @@ etherh_block_input (struct device *dev, int count, struct sk_buff *skb, int ring insw (dma_addr, buf, count >> 1); if (count & 1) buf[count - 1] = inb (dma_addr); - } -#ifdef BIT8 - else + } else insb (dma_addr, buf, count); -#endif outb (ENISR_RDC, addr + EN0_ISR); ei_status.dmaing &= ~1; @@ -307,10 +306,8 @@ etherh_get_header (struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page) if (ei_status.word16) insw (dma_addr, hdr, sizeof (*hdr) >> 1); -#ifdef BIT8 else insb (dma_addr, hdr, sizeof (*hdr)); -#endif outb (ENISR_RDC, addr + EN0_ISR); ei_status.dmaing &= ~1; @@ -355,8 +352,8 @@ etherh_close(struct device *dev) /* * This is the real probe routine. */ -static int -etherh_probe1(struct device *dev) +__initfunc(static int +etherh_probe1(struct device *dev)) { static int version_printed; unsigned int addr, i, reg0, tmp; @@ -461,10 +458,13 @@ static expansioncard_ops_t etherh_ops = { etherh_irq_enable, etherh_irq_disable, NULL, + NULL, + NULL, NULL }; -static void etherh_initdev (ecard_t *ec, struct device *dev) +__initfunc(static void +etherh_initdev(ecard_t *ec, struct device *dev)) { ecard_claim (ec); @@ -492,27 +492,27 @@ static void etherh_initdev (ecard_t *ec, struct device *dev) } ec->ops = ðerh_ops; - etherh_addr (dev->dev_addr, ec); + etherh_addr(dev->dev_addr, ec); } #ifndef MODULE -int -etherh_probe(struct device *dev) +__initfunc(int +etherh_probe(struct device *dev)) { if (!dev) return ENODEV; - ecard_startfind (); - - if (!dev->base_addr) { + if (!dev->base_addr || dev->base_addr == 0xffe0) { struct expansion_card *ec; + ecard_startfind(); + if ((ec = ecard_find (0, etherh_cids)) == NULL) return ENODEV; - etherh_initdev (ec, dev); + etherh_initdev(ec, dev); } - return etherh_probe1 (dev); + return etherh_probe1(dev); } #endif @@ -529,12 +529,10 @@ static int init_all_cards(void) { struct device *dev = NULL; - struct expansion_card *boguscards[MAX_ETHERH_CARDS]; int i, found = 0; for (i = 0; i < MAX_ETHERH_CARDS; i++) { my_ethers[i] = NULL; - boguscards[i] = NULL; ec[i] = NULL; strcpy (ethernames[i], " "); } @@ -571,7 +569,7 @@ init_all_cards(void) if (register_netdev(dev) != 0) { printk (KERN_WARNING "No etherh card found at %08lX\n", dev->base_addr); if (ec[i]) { - boguscards[i] = ec[i]; + ecard_release(ec[i]); ec[i] = NULL; } continue; @@ -583,12 +581,6 @@ init_all_cards(void) if (dev) kfree (dev); - for (i = 0; i < MAX_ETHERH_CARDS; i++) - if (boguscards[i]) { - boguscards[i]->ops = NULL; - ecard_release (boguscards[i]); - } - return found ? 0 : -ENODEV; } diff --git a/drivers/acorn/scsi/Config.in b/drivers/acorn/scsi/Config.in index 5b8d14429..68d4f920b 100644 --- a/drivers/acorn/scsi/Config.in +++ b/drivers/acorn/scsi/Config.in @@ -7,11 +7,12 @@ if [ "$CONFIG_SCSI_ACORNSCSI_3" != "n" ]; then bool ' Support SCSI 2 Synchronous Transfers' CONFIG_SCSI_ACORNSCSI_SYNC fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + dep_tristate 'ARXE SCSI support (Experimental)' CONFIG_SCSI_ARXESCSI $CONFIG_SCSI dep_tristate 'CumanaSCSI II support (Experimental)' CONFIG_SCSI_CUMANA_2 $CONFIG_SCSI dep_tristate 'EESOX support (Experimental)' CONFIG_SCSI_EESOXSCSI $CONFIG_SCSI dep_tristate 'PowerTec support (Experimental)' CONFIG_SCSI_POWERTECSCSI $CONFIG_SCSI - comment 'The following drives are not fully supported' + comment 'The following drivers are not fully supported' dep_tristate 'CumanaSCSI I support' CONFIG_SCSI_CUMANA_1 $CONFIG_SCSI if [ "$CONFIG_ARCH_ARC" = "y" -o "$CONFIG_ARCH_A5K" = "y" ]; then diff --git a/drivers/acorn/scsi/Makefile b/drivers/acorn/scsi/Makefile index f69638530..6efd22681 100644 --- a/drivers/acorn/scsi/Makefile +++ b/drivers/acorn/scsi/Makefile @@ -24,6 +24,16 @@ else endif endif +ifeq ($(CONFIG_SCSI_ARXESCSI),y) + L_OBJS += arxescsi.o + CONFIG_FAS216_BUILTIN=y +else + ifeq ($(CONFIG_SCSI_ARXESCSI),m) + M_OBJS += arxescsi.o + CONFIG_FAS216_MODULE=y + endif +endif + ifeq ($(CONFIG_SCSI_CUMANA_1),y) L_OBJS += cumana_1.o else @@ -34,12 +44,10 @@ endif ifeq ($(CONFIG_SCSI_CUMANA_2),y) L_OBJS += cumana_2.o - CONFIG_QUEUE_BUILTIN=y CONFIG_FAS216_BUILTIN=y else ifeq ($(CONFIG_SCSI_CUMANA_2),m) M_OBJS += cumana_2.o - CONFIG_QUEUE_MODULE=y CONFIG_FAS216_MODULE=y endif endif @@ -62,41 +70,39 @@ endif ifeq ($(CONFIG_SCSI_POWERTECSCSI),y) L_OBJS += powertec.o - CONFIG_QUEUE_BUILTIN=y CONFIG_FAS216_BUILTIN=y else ifeq ($(CONFIG_SCSI_POWERTECSCSI),m) M_OBJS += powertec.o - CONFIG_QUEUE_MODULE=y CONFIG_FAS216_MODULE=y endif endif ifeq ($(CONFIG_SCSI_EESOXSCSI),y) L_OBJS += eesox.o - CONFIG_QUEUE_BUILTIN=y CONFIG_FAS216_BUILTIN=y else ifeq ($(CONFIG_SCSI_EESOXSCSI),m) M_OBJS += eesox.o - CONFIG_QUEUE_MODULE=y CONFIG_FAS216_MODULE=y endif endif -ifeq ($(CONFIG_QUEUE_BUILTIN),y) - LX_OBJS += queue.o msgqueue.o -else - ifeq ($(CONFIG_QUEUE_MODULE),y) - MX_OBJS += queue.o msgqueue.o - endif -endif - ifeq ($(CONFIG_FAS216_BUILTIN),y) LX_OBJS += fas216.o + CONFIG_QUEUE_BUILTIN=y else ifeq ($(CONFIG_FAS216_MODULE),y) MX_OBJS += fas216.o + CONFIG_QUEUE_MODULE=y + endif +endif + +ifeq ($(CONFIG_QUEUE_BUILTIN),y) + LX_OBJS += queue.o msgqueue.o +else + ifeq ($(CONFIG_QUEUE_MODULE),y) + MX_OBJS += queue.o msgqueue.o endif endif diff --git a/drivers/acorn/scsi/acornscsi.c b/drivers/acorn/scsi/acornscsi.c index 50ec36436..1e83bbead 100644 --- a/drivers/acorn/scsi/acornscsi.c +++ b/drivers/acorn/scsi/acornscsi.c @@ -21,6 +21,8 @@ * 12-Oct-1997 RMK Added catch for re-entering interrupt routine. * 15-Oct-1997 RMK Improved handling of commands. * 27-Jun-1998 RMK Changed asm/delay.h to linux/delay.h. + * 13-Dec-1998 RMK Better abort code and command handling. Extra state + * transitions added to allow dodgy devices to work. */ #define DEBUG_NO_WRITE 1 #define DEBUG_QUEUES 2 @@ -35,7 +37,7 @@ #define DEBUG_RESET 1024 #define DEBUG_ALL (DEBUG_RESET|DEBUG_MESSAGES|DEBUG_LINK|DEBUG_WRITE|\ DEBUG_PHASES|DEBUG_CONNECT|DEBUG_DISCON|DEBUG_ABORT|\ - DEBUG_DMA|DEBUG_QUEUES|DEBUG_NO_WRITE) + DEBUG_DMA|DEBUG_QUEUES) /* DRIVER CONFIGURATION * @@ -123,8 +125,8 @@ #ifndef STRINGIFY #define STRINGIFY(x) #x #endif -#define STR(x) STRINGIFY(x) -#define NO_WRITE_STR STR(NO_WRITE) +#define STRx(x) STRINGIFY(x) +#define NO_WRITE_STR STRx(NO_WRITE) #include <linux/config.h> #include <linux/module.h> @@ -142,6 +144,7 @@ #include <asm/bitops.h> #include <asm/system.h> #include <asm/io.h> +#include <asm/irq.h> #include <asm/ecard.h> #include "../../scsi/scsi.h" @@ -160,10 +163,6 @@ #error "Yippee! ABORT TAG is now defined! Remove this error!" #endif -#ifndef NO_IRQ -#define NO_IRQ 255 -#endif - #ifdef CONFIG_SCSI_ACORNSCSI_LINK #error SCSI2 LINKed commands not supported (yet)! #endif @@ -186,26 +185,7 @@ #define DMAC_BUFFER_SIZE 65536 #endif -/* - * This is used to dump the previous states of the SBIC - */ -static struct status_entry { - unsigned long when; - unsigned char ssr; - unsigned char ph; - unsigned char irq; - unsigned char unused; -} status[9][16]; -static unsigned char status_ptr[9]; - -#define ADD_STATUS(_q,_ssr,_ph,_irq) \ -({ \ - status[(_q)][status_ptr[(_q)]].when = jiffies; \ - status[(_q)][status_ptr[(_q)]].ssr = (_ssr); \ - status[(_q)][status_ptr[(_q)]].ph = (_ph); \ - status[(_q)][status_ptr[(_q)]].irq = (_irq); \ - status_ptr[(_q)] = (status_ptr[(_q)] + 1) & 15; \ -}) +#define STATUS_BUFFER_TO_PRINT 24 unsigned int sdtr_period = SDTR_PERIOD; unsigned int sdtr_size = SDTR_SIZE; @@ -214,31 +194,31 @@ static struct proc_dir_entry proc_scsi_acornscsi = { PROC_SCSI_EATA, 9, "acornscsi", S_IFDIR | S_IRUGO | S_IXUGO, 2 }; -static void acornscsi_done (AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result); -static int acornscsi_reconnect_finish (AS_Host *host); -static void acornscsi_dma_cleanup (AS_Host *host); -static void acornscsi_abortcmd (AS_Host *host, unsigned char tag); +static void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result); +static int acornscsi_reconnect_finish(AS_Host *host); +static void acornscsi_dma_cleanup(AS_Host *host); +static void acornscsi_abortcmd(AS_Host *host, unsigned char tag); /* ==================================================================================== * Miscellaneous */ static inline void -sbic_arm_write (unsigned int io_port, int reg, int value) +sbic_arm_write(unsigned int io_port, int reg, int value) { - outb_t (reg, io_port); - outb_t (value, io_port + 4); + outb_t(reg, io_port); + outb_t(value, io_port + 4); } #define sbic_arm_writenext(io,val) \ - outb_t ((val), (io) + 4) + outb_t((val), (io) + 4) static inline -int sbic_arm_read (unsigned int io_port, int reg) +int sbic_arm_read(unsigned int io_port, int reg) { if(reg == ASR) return inl_t(io_port) & 255; - outb_t (reg, io_port); + outb_t(reg, io_port); return inl_t(io_port + 4) & 255; } @@ -247,129 +227,165 @@ int sbic_arm_read (unsigned int io_port, int reg) #ifdef USE_DMAC #define dmac_read(io_port,reg) \ - inb ((io_port) + (reg)) + inb((io_port) + (reg)) #define dmac_write(io_port,reg,value) \ - ({ outb ((value), (io_port) + (reg)); }) + ({ outb((value), (io_port) + (reg)); }) #define dmac_clearintr(io_port) \ - ({ outb (0, (io_port)); }) + ({ outb(0, (io_port)); }) static inline -unsigned int dmac_address (unsigned int io_port) +unsigned int dmac_address(unsigned int io_port) { - return dmac_read (io_port, TXADRHI) << 16 | - dmac_read (io_port, TXADRMD) << 8 | - dmac_read (io_port, TXADRLO); + return dmac_read(io_port, TXADRHI) << 16 | + dmac_read(io_port, TXADRMD) << 8 | + dmac_read(io_port, TXADRLO); } static -void acornscsi_dumpdma (AS_Host *host, char *where) +void acornscsi_dumpdma(AS_Host *host, char *where) { unsigned int mode, addr, len; - mode = dmac_read (host->dma.io_port, MODECON); - addr = dmac_address (host->dma.io_port); - len = dmac_read (host->dma.io_port, TXCNTHI) << 8 | - dmac_read (host->dma.io_port, TXCNTLO); + mode = dmac_read(host->dma.io_port, MODECON); + addr = dmac_address(host->dma.io_port); + len = dmac_read(host->dma.io_port, TXCNTHI) << 8 | + dmac_read(host->dma.io_port, TXCNTLO); - printk ("scsi%d: %s: DMAC %02x @%06x+%04x msk %02x, ", + printk("scsi%d: %s: DMAC %02x @%06x+%04x msk %02x, ", host->host->host_no, where, mode, addr, (len + 1) & 0xffff, - dmac_read (host->dma.io_port, MASKREG)); + dmac_read(host->dma.io_port, MASKREG)); - printk ("DMA @%06x, ", host->dma.start_addr); - printk ("BH @%p +%04x, ", host->scsi.SCp.ptr, + printk("DMA @%06x, ", host->dma.start_addr); + printk("BH @%p +%04x, ", host->scsi.SCp.ptr, host->scsi.SCp.this_residual); - printk ("DT @+%04x ST @+%04x", host->dma.transferred, + printk("DT @+%04x ST @+%04x", host->dma.transferred, host->scsi.SCp.scsi_xferred); - printk ("\n"); + printk("\n"); } #endif static -unsigned long acornscsi_sbic_xfcount (AS_Host *host) +unsigned long acornscsi_sbic_xfcount(AS_Host *host) { unsigned long length; - length = sbic_arm_read (host->scsi.io_port, TRANSCNTH) << 16; - length |= sbic_arm_readnext (host->scsi.io_port) << 8; - length |= sbic_arm_readnext (host->scsi.io_port); + length = sbic_arm_read(host->scsi.io_port, TRANSCNTH) << 16; + length |= sbic_arm_readnext(host->scsi.io_port) << 8; + length |= sbic_arm_readnext(host->scsi.io_port); return length; } -static -int acornscsi_sbic_issuecmd (AS_Host *host, int command) +static int +acornscsi_sbic_wait(AS_Host *host, int stat_mask, int stat, int timeout, char *msg) { - int asr; + int asr; - do { - asr = sbic_arm_read (host->scsi.io_port, ASR); - } while (asr & ASR_CIP); + do { + asr = sbic_arm_read(host->scsi.io_port, ASR); + + if ((asr & stat_mask) == stat) + return 0; + + udelay(1); + } while (--timeout); + + printk("scsi%d: timeout while %s\n", host->host->host_no, msg); + + return -1; +} - sbic_arm_write (host->scsi.io_port, CMND, command); +static +int acornscsi_sbic_issuecmd(AS_Host *host, int command) +{ + if (acornscsi_sbic_wait(host, ASR_CIP, 0, 1000, "issuing command")) + return -1; + + sbic_arm_write(host->scsi.io_port, CMND, command); return 0; } static void -acornscsi_csdelay (unsigned int cs) +acornscsi_csdelay(unsigned int cs) { unsigned long target_jiffies, flags; target_jiffies = jiffies + 1 + cs * HZ / 100; - save_flags (flags); - sti (); + save_flags(flags); + sti(); while (time_before(jiffies, target_jiffies)) barrier(); - restore_flags (flags); + restore_flags(flags); } static -void acornscsi_resetcard (AS_Host *host) +void acornscsi_resetcard(AS_Host *host) { - unsigned int i; + unsigned int i, timeout; /* assert reset line */ host->card.page_reg = 0x80; - outb (host->card.page_reg, host->card.io_page); + outb(host->card.page_reg, host->card.io_page); /* wait 3 cs. SCSI standard says 25ms. */ - acornscsi_csdelay (3); + acornscsi_csdelay(3); host->card.page_reg = 0; - outb (host->card.page_reg, host->card.io_page); + outb(host->card.page_reg, host->card.io_page); /* * Should get a reset from the card */ - while (!(inb (host->card.io_intr) & 8)); - sbic_arm_read (host->scsi.io_port, ASR); - sbic_arm_read (host->scsi.io_port, SSR); + timeout = 1000; + do { + if (inb(host->card.io_intr) & 8) + break; + udelay(1); + } while (--timeout); + + if (timeout == 0) + printk("scsi%d: timeout while resetting card\n", + host->host->host_no); + + sbic_arm_read(host->scsi.io_port, ASR); + sbic_arm_read(host->scsi.io_port, SSR); /* setup sbic - WD33C93A */ - sbic_arm_write (host->scsi.io_port, OWNID, OWNID_EAF | host->host->this_id); - sbic_arm_write (host->scsi.io_port, CMND, CMND_RESET); + sbic_arm_write(host->scsi.io_port, OWNID, OWNID_EAF | host->host->this_id); + sbic_arm_write(host->scsi.io_port, CMND, CMND_RESET); /* * Command should cause a reset interrupt */ - while (!(inb (host->card.io_intr) & 8)); - sbic_arm_read (host->scsi.io_port, ASR); - if (sbic_arm_read (host->scsi.io_port, SSR) != 0x01) - printk (KERN_CRIT "scsi%d: WD33C93A didn't give enhanced reset interrupt\n", + timeout = 1000; + do { + if (inb(host->card.io_intr) & 8) + break; + udelay(1); + } while (--timeout); + + if (timeout == 0) + printk("scsi%d: timeout while resetting card\n", host->host->host_no); - sbic_arm_write (host->scsi.io_port, CTRL, INIT_SBICDMA | CTRL_IDI); - sbic_arm_write (host->scsi.io_port, TIMEOUT, TIMEOUT_TIME); - sbic_arm_write (host->scsi.io_port, SYNCHTRANSFER, SYNCHTRANSFER_2DBA); - sbic_arm_write (host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP); + sbic_arm_read(host->scsi.io_port, ASR); + if (sbic_arm_read(host->scsi.io_port, SSR) != 0x01) + printk(KERN_CRIT "scsi%d: WD33C93A didn't give enhanced reset interrupt\n", + host->host->host_no); + + sbic_arm_write(host->scsi.io_port, CTRL, INIT_SBICDMA | CTRL_IDI); + sbic_arm_write(host->scsi.io_port, TIMEOUT, TIMEOUT_TIME); + sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, SYNCHTRANSFER_2DBA); + sbic_arm_write(host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP); host->card.page_reg = 0x40; - outb (host->card.page_reg, host->card.io_page); + outb(host->card.page_reg, host->card.io_page); /* setup dmac - uPC71071 */ dmac_write(host->dma.io_port, INIT, 0); @@ -391,7 +407,7 @@ void acornscsi_resetcard (AS_Host *host) } /* wait 25 cs. SCSI standard says 250ms. */ - acornscsi_csdelay (25); + acornscsi_csdelay(25); } /*============================================================================================= @@ -461,80 +477,101 @@ static char *acornscsi_interruptcode[] = { }; static -void print_scsi_status (unsigned int ssr) +void print_scsi_status(unsigned int ssr) { if (acornscsi_map[ssr] != -1) - printk ("%s:%s", + printk("%s:%s", acornscsi_interrupttype[(ssr >> 4)], acornscsi_interruptcode[acornscsi_map[ssr]]); else - printk ("%X:%X", ssr >> 4, ssr & 0x0f); + printk("%X:%X", ssr >> 4, ssr & 0x0f); } #endif static -void print_sbic_status (int asr, int ssr, int cmdphase) +void print_sbic_status(int asr, int ssr, int cmdphase) { #ifdef CONFIG_ACORNSCSI_CONSTANTS - printk ("sbic: %c%c%c%c%c%c ", + printk("sbic: %c%c%c%c%c%c ", asr & ASR_INT ? 'I' : 'i', asr & ASR_LCI ? 'L' : 'l', asr & ASR_BSY ? 'B' : 'b', asr & ASR_CIP ? 'C' : 'c', asr & ASR_PE ? 'P' : 'p', asr & ASR_DBR ? 'D' : 'd'); - printk ("scsi: "); - print_scsi_status (ssr); - printk (" ph %02X\n", cmdphase); + printk("scsi: "); + print_scsi_status(ssr); + printk(" ph %02X\n", cmdphase); #else - printk ("sbic: %02X scsi: %X:%X ph: %02X\n", + printk("sbic: %02X scsi: %X:%X ph: %02X\n", asr, (ssr & 0xf0)>>4, ssr & 0x0f, cmdphase); #endif } -static -void acornscsi_dumplog (AS_Host *host, int target) +static void +acornscsi_dumplogline(AS_Host *host, int target, int line) { - unsigned int prev; - do { - signed int statptr; - - printk ("%c:", target == 8 ? 'H' : ('0' + target)); - statptr = status_ptr[target] - 10; + unsigned long prev; + signed int ptr; + + ptr = host->status_ptr[target] - STATUS_BUFFER_TO_PRINT; + if (ptr < 0) + ptr += STATUS_BUFFER_SIZE; + + printk("%c: %3s:", target == 8 ? 'H' : '0' + target, + line == 0 ? "ph" : line == 1 ? "ssr" : "int"); + + prev = host->status[target][ptr].when; + + for (; ptr != host->status_ptr[target]; ptr = (ptr + 1) & (STATUS_BUFFER_SIZE - 1)) { + unsigned long time_diff; + + if (!host->status[target][ptr].when) + continue; + + switch (line) { + case 0: + printk("%c%02X", host->status[target][ptr].irq ? '-' : ' ', + host->status[target][ptr].ph); + break; + + case 1: + printk(" %02X", host->status[target][ptr].ssr); + break; + + case 2: + time_diff = host->status[target][ptr].when - prev; + prev = host->status[target][ptr].when; + if (time_diff == 0) + printk("==^"); + else if (time_diff >= 100) + printk(" "); + else + printk(" %02ld", time_diff); + break; + } + } - if (statptr < 0) - statptr += 16; + printk("\n"); +} - prev = status[target][statptr].when; +static +void acornscsi_dumplog(AS_Host *host, int target) +{ + do { + acornscsi_dumplogline(host, target, 0); + acornscsi_dumplogline(host, target, 1); + acornscsi_dumplogline(host, target, 2); - for (; statptr != status_ptr[target]; statptr = (statptr + 1) & 15) { - if (status[target][statptr].when) { -#ifdef CONFIG_ACORNSCSI_CONSTANTS - printk ("%c%02X:S=", - status[target][statptr].irq ? '-' : ' ', - status[target][statptr].ph); - print_scsi_status (status[target][statptr].ssr); -#else - printk ("%c%02X:%02X", - status[target][statptr].irq ? '-' : ' ', - status[target][statptr].ph, - status[target][statptr].ssr); -#endif - printk ("+%02ld", - (status[target][statptr].when - prev) < 100 ? - (status[target][statptr].when - prev) : 99); - prev = status[target][statptr].when; - } - } - printk ("\n"); if (target == 8) break; + target = 8; } while (1); } static -char acornscsi_target (AS_Host *host) +char acornscsi_target(AS_Host *host) { if (host->SCpnt) return '0' + host->SCpnt->target; @@ -542,7 +579,7 @@ char acornscsi_target (AS_Host *host) } /* - * Prototype: cmdtype_t acornscsi_cmdtype (int command) + * Prototype: cmdtype_t acornscsi_cmdtype(int command) * Purpose : differentiate READ from WRITE from other commands * Params : command - command to interpret * Returns : CMD_READ - command reads data, @@ -550,7 +587,7 @@ char acornscsi_target (AS_Host *host) * CMD_MISC - everything else */ static inline -cmdtype_t acornscsi_cmdtype (int command) +cmdtype_t acornscsi_cmdtype(int command) { switch (command) { case WRITE_6: case WRITE_10: case WRITE_12: @@ -563,7 +600,7 @@ cmdtype_t acornscsi_cmdtype (int command) } /* - * Prototype: int acornscsi_datadirection (int command) + * Prototype: int acornscsi_datadirection(int command) * Purpose : differentiate between commands that have a DATA IN phase * and a DATA OUT phase * Params : command - command to interpret @@ -571,7 +608,7 @@ cmdtype_t acornscsi_cmdtype (int command) * DATADIR_IN - data in phase expected */ static -datadir_t acornscsi_datadirection (int command) +datadir_t acornscsi_datadirection(int command) { switch (command) { case CHANGE_DEFINITION: case COMPARE: case COPY: @@ -605,13 +642,13 @@ static struct sync_xfer_tbl { }; /* - * Prototype: int acornscsi_getperiod (unsigned char syncxfer) + * Prototype: int acornscsi_getperiod(unsigned char syncxfer) * Purpose : period for the synchronous transfer setting * Params : syncxfer SYNCXFER register value * Returns : period in ns. */ static -int acornscsi_getperiod (unsigned char syncxfer) +int acornscsi_getperiod(unsigned char syncxfer) { int i; @@ -626,14 +663,14 @@ int acornscsi_getperiod (unsigned char syncxfer) } /* - * Prototype: int round_period (unsigned int period) + * Prototype: int round_period(unsigned int period) * Purpose : return index into above table for a required REQ period * Params : period - time (ns) for REQ * Returns : table index * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting */ static inline -int round_period (unsigned int period) +int round_period(unsigned int period) { int i; @@ -646,7 +683,7 @@ int round_period (unsigned int period) } /* - * Prototype: unsigned char calc_sync_xfer (unsigned int period, unsigned int offset) + * Prototype: unsigned char calc_sync_xfer(unsigned int period, unsigned int offset) * Purpose : calculate value for 33c93s SYNC register * Params : period - time (ns) for REQ * offset - offset in bytes between REQ/ACK @@ -654,7 +691,7 @@ int round_period (unsigned int period) * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting */ static -unsigned char calc_sync_xfer (unsigned int period, unsigned int offset) +unsigned char calc_sync_xfer(unsigned int period, unsigned int offset) { return sync_xfer_table[round_period(period)].reg_value | ((offset < SDTR_SIZE) ? offset : SDTR_SIZE); @@ -664,14 +701,14 @@ unsigned char calc_sync_xfer (unsigned int period, unsigned int offset) * Command functions */ /* - * Function: acornscsi_kick (AS_Host *host) + * Function: acornscsi_kick(AS_Host *host) * Purpose : kick next command to interface * Params : host - host to send command to * Returns : INTR_IDLE if idle, otherwise INTR_PROCESSING * Notes : interrupts are always disabled! */ static -intr_ret_t acornscsi_kick (AS_Host *host) +intr_ret_t acornscsi_kick(AS_Host *host) { int from_queue = 0; Scsi_Cmnd *SCpnt; @@ -682,7 +719,7 @@ intr_ret_t acornscsi_kick (AS_Host *host) /* retrieve next command */ if (!SCpnt) { - SCpnt = queue_remove_exclude (&host->queues.issue, host->busyluns); + SCpnt = queue_remove_exclude(&host->queues.issue, host->busyluns); if (!SCpnt) return INTR_IDLE; @@ -690,11 +727,11 @@ intr_ret_t acornscsi_kick (AS_Host *host) } if (host->scsi.disconnectable && host->SCpnt) { - queue_add_cmd_tail (&host->queues.disconnected, host->SCpnt); + queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt); host->scsi.disconnectable = 0; #if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) - DBG(host->SCpnt, printk ("scsi%d.%c: moved command to disconnected queue\n", - host->host->host_no, acornscsi_target (host))); + DBG(host->SCpnt, printk("scsi%d.%c: moved command to disconnected queue\n", + host->host->host_no, acornscsi_target(host))); #endif host->SCpnt = NULL; } @@ -703,9 +740,9 @@ intr_ret_t acornscsi_kick (AS_Host *host) * If we have an interrupt pending, then we may have been reselected. * In this case, we don't want to write to the registers */ - if (!(sbic_arm_read (host->scsi.io_port, ASR) & (ASR_INT|ASR_BSY|ASR_CIP))) { - sbic_arm_write (host->scsi.io_port, DESTID, SCpnt->target); - sbic_arm_write (host->scsi.io_port, CMND, CMND_SELWITHATN); + if (!(sbic_arm_read(host->scsi.io_port, ASR) & (ASR_INT|ASR_BSY|ASR_CIP))) { + sbic_arm_write(host->scsi.io_port, DESTID, SCpnt->target); + sbic_arm_write(host->scsi.io_port, CMND, CMND_SELWITHATN); } /* @@ -717,9 +754,10 @@ intr_ret_t acornscsi_kick (AS_Host *host) host->scsi.SCp = SCpnt->SCp; host->dma.xfer_setup = 0; host->dma.xfer_required = 0; + host->dma.xfer_done = 0; #if (DEBUG & (DEBUG_ABORT|DEBUG_CONNECT)) - DBG(SCpnt,printk ("scsi%d.%c: starting cmd %02X\n", + DBG(SCpnt,printk("scsi%d.%c: starting cmd %02X\n", host->host->host_no, '0' + SCpnt->target, SCpnt->cmnd[0])); #endif @@ -736,11 +774,11 @@ intr_ret_t acornscsi_kick (AS_Host *host) SCpnt->tag = SCpnt->device->current_tag; } else #endif - set_bit (SCpnt->target * 8 + SCpnt->lun, host->busyluns); + set_bit(SCpnt->target * 8 + SCpnt->lun, host->busyluns); host->stats.removes += 1; - switch (acornscsi_cmdtype (SCpnt->cmnd[0])) { + switch (acornscsi_cmdtype(SCpnt->cmnd[0])) { case CMD_WRITE: host->stats.writes += 1; break; @@ -757,25 +795,25 @@ intr_ret_t acornscsi_kick (AS_Host *host) } /* - * Function: void acornscsi_done (AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result) + * Function: void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result) * Purpose : complete processing for command * Params : host - interface that completed * result - driver byte of result */ static -void acornscsi_done (AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result) +void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result) { Scsi_Cmnd *SCpnt = *SCpntp; /* clean up */ - sbic_arm_write (host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP); + sbic_arm_write(host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP); host->stats.fins += 1; if (SCpnt) { *SCpntp = NULL; - acornscsi_dma_cleanup (host); + acornscsi_dma_cleanup(host); SCpnt->result = result << 16 | host->scsi.SCp.Message << 8 | host->scsi.SCp.Status; @@ -787,35 +825,63 @@ void acornscsi_done (AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result) * It doesn't appear to be set to something meaningful by the higher * levels all the time. */ - if (host->scsi.SCp.ptr && result == DID_OK && - acornscsi_cmdtype (SCpnt->cmnd[0]) != CMD_MISC) { - switch (status_byte (SCpnt->result)) { - case CHECK_CONDITION: - case COMMAND_TERMINATED: - case BUSY: - case QUEUE_FULL: - case RESERVATION_CONFLICT: - break; - - default: - printk (KERN_ERR "scsi%d.H: incomplete data transfer detected: result=%08X command=", - host->host->host_no, SCpnt->result); - print_command (SCpnt->cmnd); - acornscsi_dumpdma (host, "done"); - acornscsi_dumplog (host, SCpnt->target); - SCpnt->result &= 0xffff; - SCpnt->result |= DID_ERROR << 16; - } + if (result == DID_OK) { + int xfer_warn = 0; + + if (SCpnt->underflow == 0) { + if (host->scsi.SCp.ptr && + acornscsi_cmdtype(SCpnt->cmnd[0]) != CMD_MISC) + xfer_warn = 1; + } else { + if (host->scsi.SCp.scsi_xferred < SCpnt->underflow || + host->scsi.SCp.scsi_xferred != host->dma.transferred) + xfer_warn = 1; + } + + /* ANSI standard says: (SCSI-2 Rev 10c Sect 5.6.6) + * Targets which break data transfers into multiple + * connections shall end each successful connection + * (except possibly the last) with a SAVE DATA + * POINTER - DISCONNECT message sequence. + * + * This makes it difficult to ensure that a transfer has + * completed. If we reach the end of a transfer during + * the command, then we can only have finished the transfer. + * therefore, if we seem to have some data remaining, this + * is not a problem. + */ + if (host->dma.xfer_done) + xfer_warn = 0; + + if (xfer_warn) { + switch (status_byte(SCpnt->result)) { + case CHECK_CONDITION: + case COMMAND_TERMINATED: + case BUSY: + case QUEUE_FULL: + case RESERVATION_CONFLICT: + break; + + default: + printk(KERN_ERR "scsi%d.H: incomplete data transfer detected: result=%08X command=", + host->host->host_no, SCpnt->result); + print_command(SCpnt->cmnd); + acornscsi_dumpdma(host, "done"); + acornscsi_dumplog(host, SCpnt->target); + SCpnt->result &= 0xffff; + SCpnt->result |= DID_ERROR << 16; + } + } } if (!SCpnt->scsi_done) - panic ("scsi%d.H: null scsi_done function in acornscsi_done", host->host->host_no); + panic("scsi%d.H: null scsi_done function in acornscsi_done", host->host->host_no); - clear_bit (SCpnt->target * 8 + SCpnt->lun, host->busyluns); + clear_bit(SCpnt->target * 8 + SCpnt->lun, host->busyluns); - SCpnt->scsi_done (SCpnt); + SCpnt->scsi_done(SCpnt); } else - printk ("scsi%d: null command in acornscsi_done", host->host->host_no); + printk("scsi%d: null command in acornscsi_done", host->host->host_no); host->scsi.phase = PHASE_IDLE; } @@ -828,7 +894,7 @@ void acornscsi_done (AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result) * Notes : this will only be one SG entry or less */ static -void acornscsi_data_updateptr (AS_Host *host, Scsi_Pointer *SCp, unsigned int length) +void acornscsi_data_updateptr(AS_Host *host, Scsi_Pointer *SCp, unsigned int length) { SCp->ptr += length; SCp->this_residual -= length; @@ -839,13 +905,15 @@ void acornscsi_data_updateptr (AS_Host *host, Scsi_Pointer *SCp, unsigned int le SCp->buffers_residual--; SCp->ptr = (char *)SCp->buffer->address; SCp->this_residual = SCp->buffer->length; - } else + } else { SCp->ptr = NULL; + host->dma.xfer_done = 1; + } } } /* - * Prototype: void acornscsi_data_read (AS_Host *host, char *ptr, + * Prototype: void acornscsi_data_read(AS_Host *host, char *ptr, * unsigned int start_addr, unsigned int length) * Purpose : read data from DMA RAM * Params : host - host to transfer from @@ -855,16 +923,16 @@ void acornscsi_data_updateptr (AS_Host *host, Scsi_Pointer *SCp, unsigned int le * Notes : this will only be one SG entry or less */ static -void acornscsi_data_read (AS_Host *host, char *ptr, +void acornscsi_data_read(AS_Host *host, char *ptr, unsigned int start_addr, unsigned int length) { - extern void __acornscsi_in (int port, char *buf, int len); + extern void __acornscsi_in(int port, char *buf, int len); unsigned int page, offset, len = length; page = (start_addr >> 12); offset = start_addr & ((1 << 12) - 1); - outb ((page & 0x3f) | host->card.page_reg, host->card.io_page); + outb((page & 0x3f) | host->card.page_reg, host->card.io_page); while (len > 0) { unsigned int this_len; @@ -874,7 +942,7 @@ void acornscsi_data_read (AS_Host *host, char *ptr, else this_len = len; - __acornscsi_in (host->card.io_ram + (offset << 1), ptr, this_len); + __acornscsi_in(host->card.io_ram + (offset << 1), ptr, this_len); offset += this_len; ptr += this_len; @@ -883,14 +951,14 @@ void acornscsi_data_read (AS_Host *host, char *ptr, if (offset == (1 << 12)) { offset = 0; page ++; - outb ((page & 0x3f) | host->card.page_reg, host->card.io_page); + outb((page & 0x3f) | host->card.page_reg, host->card.io_page); } } - outb (host->card.page_reg, host->card.io_page); + outb(host->card.page_reg, host->card.io_page); } /* - * Prototype: void acornscsi_data_write (AS_Host *host, char *ptr, + * Prototype: void acornscsi_data_write(AS_Host *host, char *ptr, * unsigned int start_addr, unsigned int length) * Purpose : write data to DMA RAM * Params : host - host to transfer from @@ -900,16 +968,16 @@ void acornscsi_data_read (AS_Host *host, char *ptr, * Notes : this will only be one SG entry or less */ static -void acornscsi_data_write (AS_Host *host, char *ptr, +void acornscsi_data_write(AS_Host *host, char *ptr, unsigned int start_addr, unsigned int length) { - extern void __acornscsi_out (int port, char *buf, int len); + extern void __acornscsi_out(int port, char *buf, int len); unsigned int page, offset, len = length; page = (start_addr >> 12); offset = start_addr & ((1 << 12) - 1); - outb ((page & 0x3f) | host->card.page_reg, host->card.io_page); + outb((page & 0x3f) | host->card.page_reg, host->card.io_page); while (len > 0) { unsigned int this_len; @@ -919,7 +987,7 @@ void acornscsi_data_write (AS_Host *host, char *ptr, else this_len = len; - __acornscsi_out (host->card.io_ram + (offset << 1), ptr, this_len); + __acornscsi_out(host->card.io_ram + (offset << 1), ptr, this_len); offset += this_len; ptr += this_len; @@ -928,10 +996,10 @@ void acornscsi_data_write (AS_Host *host, char *ptr, if (offset == (1 << 12)) { offset = 0; page ++; - outb ((page & 0x3f) | host->card.page_reg, host->card.io_page); + outb((page & 0x3f) | host->card.page_reg, host->card.io_page); } } - outb (host->card.page_reg, host->card.io_page); + outb(host->card.page_reg, host->card.io_page); } /* ========================================================================================= @@ -939,25 +1007,25 @@ void acornscsi_data_write (AS_Host *host, char *ptr, */ #ifdef USE_DMAC /* - * Prototype: void acornscsi_dmastop (AS_Host *host) + * Prototype: void acornscsi_dmastop(AS_Host *host) * Purpose : stop all DMA * Params : host - host on which to stop DMA * Notes : This is called when leaving DATA IN/OUT phase, * or when interface is RESET */ static inline -void acornscsi_dma_stop (AS_Host *host) +void acornscsi_dma_stop(AS_Host *host) { - dmac_write (host->dma.io_port, MASKREG, MASK_ON); - dmac_clearintr (host->dma.io_intr_clear); + dmac_write(host->dma.io_port, MASKREG, MASK_ON); + dmac_clearintr(host->dma.io_intr_clear); #if (DEBUG & DEBUG_DMA) - DBG(host->SCpnt, acornscsi_dumpdma (host, "stop")); + DBG(host->SCpnt, acornscsi_dumpdma(host, "stop")); #endif } /* - * Function: void acornscsi_dma_setup (AS_Host *host, dmadir_t direction) + * Function: void acornscsi_dma_setup(AS_Host *host, dmadir_t direction) * Purpose : setup DMA controller for data transfer * Params : host - host to setup * direction - data transfer direction @@ -965,19 +1033,19 @@ void acornscsi_dma_stop (AS_Host *host) * while we're in a DATA I/O phase */ static -void acornscsi_dma_setup (AS_Host *host, dmadir_t direction) +void acornscsi_dma_setup(AS_Host *host, dmadir_t direction) { unsigned int address, length, mode; host->dma.direction = direction; - dmac_write (host->dma.io_port, MASKREG, MASK_ON); + dmac_write(host->dma.io_port, MASKREG, MASK_ON); if (direction == DMA_OUT) { #if (DEBUG & DEBUG_NO_WRITE) if (NO_WRITE & (1 << host->SCpnt->target)) { - printk (KERN_CRIT "scsi%d.%c: I can't handle DMA_OUT!\n", - host->host->host_no, acornscsi_target (host)); + printk(KERN_CRIT "scsi%d.%c: I can't handle DMA_OUT!\n", + host->host->host_no, acornscsi_target(host)); return; } #endif @@ -988,7 +1056,7 @@ void acornscsi_dma_setup (AS_Host *host, dmadir_t direction) /* * Allocate some buffer space, limited to half the buffer size */ - length = min (host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2); + length = min(host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2); if (length) { host->dma.start_addr = address = host->dma.free_addr; host->dma.free_addr = (host->dma.free_addr + length) & @@ -998,27 +1066,27 @@ void acornscsi_dma_setup (AS_Host *host, dmadir_t direction) * Transfer data to DMA memory */ if (direction == DMA_OUT) - acornscsi_data_write (host, host->scsi.SCp.ptr, host->dma.start_addr, + acornscsi_data_write(host, host->scsi.SCp.ptr, host->dma.start_addr, length); length -= 1; - dmac_write (host->dma.io_port, TXCNTLO, length); - dmac_write (host->dma.io_port, TXCNTHI, length >> 8); - dmac_write (host->dma.io_port, TXADRLO, address); - dmac_write (host->dma.io_port, TXADRMD, address >> 8); - dmac_write (host->dma.io_port, TXADRHI, 0); - dmac_write (host->dma.io_port, MODECON, mode); - dmac_write (host->dma.io_port, MASKREG, MASK_OFF); + dmac_write(host->dma.io_port, TXCNTLO, length); + dmac_write(host->dma.io_port, TXCNTHI, length >> 8); + dmac_write(host->dma.io_port, TXADRLO, address); + dmac_write(host->dma.io_port, TXADRMD, address >> 8); + dmac_write(host->dma.io_port, TXADRHI, 0); + dmac_write(host->dma.io_port, MODECON, mode); + dmac_write(host->dma.io_port, MASKREG, MASK_OFF); #if (DEBUG & DEBUG_DMA) - DBG(host->SCpnt, acornscsi_dumpdma (host, "strt")); + DBG(host->SCpnt, acornscsi_dumpdma(host, "strt")); #endif host->dma.xfer_setup = 1; } } /* - * Function: void acornscsi_dma_cleanup (AS_Host *host) + * Function: void acornscsi_dma_cleanup(AS_Host *host) * Purpose : ensure that all DMA transfers are up-to-date & host->scsi.SCp is correct * Params : host - host to finish * Notes : This is called when a command is: @@ -1026,10 +1094,10 @@ void acornscsi_dma_setup (AS_Host *host, dmadir_t direction) * : This must not return until all transfers are completed. */ static -void acornscsi_dma_cleanup (AS_Host *host) +void acornscsi_dma_cleanup(AS_Host *host) { - dmac_write (host->dma.io_port, MASKREG, MASK_ON); - dmac_clearintr (host->dma.io_intr_clear); + dmac_write(host->dma.io_port, MASKREG, MASK_ON); + dmac_clearintr(host->dma.io_intr_clear); /* * Check for a pending transfer @@ -1037,7 +1105,7 @@ void acornscsi_dma_cleanup (AS_Host *host) if (host->dma.xfer_required) { host->dma.xfer_required = 0; if (host->dma.direction == DMA_IN) - acornscsi_data_read (host, host->dma.xfer_ptr, + acornscsi_data_read(host, host->dma.xfer_ptr, host->dma.xfer_start, host->dma.xfer_length); } @@ -1056,17 +1124,17 @@ void acornscsi_dma_cleanup (AS_Host *host) /* * Calculate number of bytes transferred from DMA. */ - transferred = dmac_address (host->dma.io_port) - host->dma.start_addr; + transferred = dmac_address(host->dma.io_port) - host->dma.start_addr; host->dma.transferred += transferred; if (host->dma.direction == DMA_IN) - acornscsi_data_read (host, host->scsi.SCp.ptr, + acornscsi_data_read(host, host->scsi.SCp.ptr, host->dma.start_addr, transferred); /* * Update SCSI pointers */ - acornscsi_data_updateptr (host, &host->scsi.SCp, transferred); + acornscsi_data_updateptr(host, &host->scsi.SCp, transferred); #if (DEBUG & DEBUG_DMA) DBG(host->SCpnt, acornscsi_dumpdma(host, "cupo")); #endif @@ -1074,7 +1142,7 @@ void acornscsi_dma_cleanup (AS_Host *host) } /* - * Function: void acornscsi_dmacintr (AS_Host *host) + * Function: void acornscsi_dmacintr(AS_Host *host) * Purpose : handle interrupts from DMAC device * Params : host - host to process * Notes : If reading, we schedule the read to main memory & @@ -1084,21 +1152,21 @@ void acornscsi_dma_cleanup (AS_Host *host) * : Called whenever DMAC finished it's current transfer. */ static -void acornscsi_dma_intr (AS_Host *host) +void acornscsi_dma_intr(AS_Host *host) { unsigned int address, length, transferred; #if (DEBUG & DEBUG_DMA) - DBG(host->SCpnt, acornscsi_dumpdma (host, "inti")); + DBG(host->SCpnt, acornscsi_dumpdma(host, "inti")); #endif - dmac_write (host->dma.io_port, MASKREG, MASK_ON); - dmac_clearintr (host->dma.io_intr_clear); + dmac_write(host->dma.io_port, MASKREG, MASK_ON); + dmac_clearintr(host->dma.io_intr_clear); /* * Calculate amount transferred via DMA */ - transferred = dmac_address (host->dma.io_port) - host->dma.start_addr; + transferred = dmac_address(host->dma.io_port) - host->dma.start_addr; host->dma.transferred += transferred; /* @@ -1111,12 +1179,12 @@ void acornscsi_dma_intr (AS_Host *host) host->dma.xfer_required = 1; } - acornscsi_data_updateptr (host, &host->scsi.SCp, transferred); + acornscsi_data_updateptr(host, &host->scsi.SCp, transferred); /* * Allocate some buffer space, limited to half the on-board RAM size */ - length = min (host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2); + length = min(host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2); if (length) { host->dma.start_addr = address = host->dma.free_addr; host->dma.free_addr = (host->dma.free_addr + length) & @@ -1126,19 +1194,19 @@ void acornscsi_dma_intr (AS_Host *host) * Transfer data to DMA memory */ if (host->dma.direction == DMA_OUT) - acornscsi_data_write (host, host->scsi.SCp.ptr, host->dma.start_addr, + acornscsi_data_write(host, host->scsi.SCp.ptr, host->dma.start_addr, length); length -= 1; - dmac_write (host->dma.io_port, TXCNTLO, length); - dmac_write (host->dma.io_port, TXCNTHI, length >> 8); - dmac_write (host->dma.io_port, TXADRLO, address); - dmac_write (host->dma.io_port, TXADRMD, address >> 8); - dmac_write (host->dma.io_port, TXADRHI, 0); - dmac_write (host->dma.io_port, MASKREG, MASK_OFF); + dmac_write(host->dma.io_port, TXCNTLO, length); + dmac_write(host->dma.io_port, TXCNTHI, length >> 8); + dmac_write(host->dma.io_port, TXADRLO, address); + dmac_write(host->dma.io_port, TXADRMD, address >> 8); + dmac_write(host->dma.io_port, TXADRHI, 0); + dmac_write(host->dma.io_port, MASKREG, MASK_OFF); #if (DEBUG & DEBUG_DMA) - DBG(host->SCpnt, acornscsi_dumpdma (host, "into")); + DBG(host->SCpnt, acornscsi_dumpdma(host, "into")); #endif } else { host->dma.xfer_setup = 0; @@ -1149,48 +1217,48 @@ void acornscsi_dma_intr (AS_Host *host) * attention condition. We continue giving one byte until * the device recognises the attention. */ - if (dmac_read (host->dma.io_port, STATUS) & STATUS_RQ0) { - acornscsi_abortcmd (host, host->SCpnt->tag); - - dmac_write (host->dma.io_port, TXCNTLO, 0); - dmac_write (host->dma.io_port, TXCNTHI, 0); - dmac_write (host->dma.io_port, TXADRLO, 0); - dmac_write (host->dma.io_port, TXADRMD, 0); - dmac_write (host->dma.io_port, TXADRHI, 0); - dmac_write (host->dma.io_port, MASKREG, MASK_OFF); + if (dmac_read(host->dma.io_port, STATUS) & STATUS_RQ0) { + acornscsi_abortcmd(host, host->SCpnt->tag); + + dmac_write(host->dma.io_port, TXCNTLO, 0); + dmac_write(host->dma.io_port, TXCNTHI, 0); + dmac_write(host->dma.io_port, TXADRLO, 0); + dmac_write(host->dma.io_port, TXADRMD, 0); + dmac_write(host->dma.io_port, TXADRHI, 0); + dmac_write(host->dma.io_port, MASKREG, MASK_OFF); } #endif } } /* - * Function: void acornscsi_dma_xfer (AS_Host *host) + * Function: void acornscsi_dma_xfer(AS_Host *host) * Purpose : transfer data between AcornSCSI and memory * Params : host - host to process */ static -void acornscsi_dma_xfer (AS_Host *host) +void acornscsi_dma_xfer(AS_Host *host) { host->dma.xfer_required = 0; if (host->dma.direction == DMA_IN) - acornscsi_data_read (host, host->dma.xfer_ptr, + acornscsi_data_read(host, host->dma.xfer_ptr, host->dma.xfer_start, host->dma.xfer_length); } /* - * Function: void acornscsi_dma_adjust (AS_Host *host) + * Function: void acornscsi_dma_adjust(AS_Host *host) * Purpose : adjust DMA pointers & count for bytes transfered to * SBIC but not SCSI bus. * Params : host - host to adjust DMA count for */ static -void acornscsi_dma_adjust (AS_Host *host) +void acornscsi_dma_adjust(AS_Host *host) { if (host->dma.xfer_setup) { signed long transferred; #if (DEBUG & (DEBUG_DMA|DEBUG_WRITE)) - DBG(host->SCpnt, acornscsi_dumpdma (host, "adji")); + DBG(host->SCpnt, acornscsi_dumpdma(host, "adji")); #endif /* * Calculate correct DMA address - DMA is ahead of SCSI bus while @@ -1205,17 +1273,17 @@ void acornscsi_dma_adjust (AS_Host *host) */ transferred = host->scsi.SCp.scsi_xferred - host->dma.transferred; if (transferred < 0) - printk ("scsi%d.%c: Ack! DMA write correction %ld < 0!\n", - host->host->host_no, acornscsi_target (host), transferred); + printk("scsi%d.%c: Ack! DMA write correction %ld < 0!\n", + host->host->host_no, acornscsi_target(host), transferred); else if (transferred == 0) host->dma.xfer_setup = 0; else { transferred += host->dma.start_addr; - dmac_write (host->dma.io_port, TXADRLO, transferred); - dmac_write (host->dma.io_port, TXADRMD, transferred >> 8); - dmac_write (host->dma.io_port, TXADRHI, transferred >> 16); + dmac_write(host->dma.io_port, TXADRLO, transferred); + dmac_write(host->dma.io_port, TXADRMD, transferred >> 8); + dmac_write(host->dma.io_port, TXADRHI, transferred >> 16); #if (DEBUG & (DEBUG_DMA|DEBUG_WRITE)) - DBG(host->SCpnt, acornscsi_dumpdma (host, "adjo")); + DBG(host->SCpnt, acornscsi_dumpdma(host, "adjo")); #endif } } @@ -1225,66 +1293,88 @@ void acornscsi_dma_adjust (AS_Host *host) /* ========================================================================================= * Data I/O */ +static int +acornscsi_write_pio(AS_Host *host, char *bytes, int *ptr, int len, unsigned int max_timeout) +{ + unsigned int asr, timeout = max_timeout; + int my_ptr = *ptr; + + while (my_ptr < len) { + asr = sbic_arm_read(host->scsi.io_port, ASR); + + if (asr & ASR_DBR) { + timeout = max_timeout; + + sbic_arm_write(host->scsi.io_port, DATA, bytes[my_ptr++]); + } else if (asr & ASR_INT) + break; + else if (--timeout == 0) + break; + udelay(1); + } + + *ptr = my_ptr; + + return (timeout == 0) ? -1 : 0; +} + /* - * Function: void acornscsi_sendcommand (AS_Host *host) + * Function: void acornscsi_sendcommand(AS_Host *host) * Purpose : send a command to a target * Params : host - host which is connected to target */ -static -void acornscsi_sendcommand (AS_Host *host) +static void +acornscsi_sendcommand(AS_Host *host) { Scsi_Cmnd *SCpnt = host->SCpnt; - unsigned int asr; - unsigned char *cmdptr, *cmdend; - - sbic_arm_write (host->scsi.io_port, TRANSCNTH, 0); - sbic_arm_writenext (host->scsi.io_port, 0); - sbic_arm_writenext (host->scsi.io_port, SCpnt->cmd_len - host->scsi.SCp.sent_command); - acornscsi_sbic_issuecmd (host, CMND_XFERINFO); - - cmdptr = SCpnt->cmnd + host->scsi.SCp.sent_command; - cmdend = SCpnt->cmnd + SCpnt->cmd_len; - - while (cmdptr < cmdend) { - asr = sbic_arm_read (host->scsi.io_port, ASR); - if (asr & ASR_DBR) - sbic_arm_write (host->scsi.io_port, DATA, *cmdptr++); - else if (asr & ASR_INT) - break; - } - if (cmdptr >= cmdend) - host->scsi.SCp.sent_command = cmdptr - SCpnt->cmnd; + + sbic_arm_write(host->scsi.io_port, TRANSCNTH, 0); + sbic_arm_writenext(host->scsi.io_port, 0); + sbic_arm_writenext(host->scsi.io_port, SCpnt->cmd_len - host->scsi.SCp.sent_command); + + acornscsi_sbic_issuecmd(host, CMND_XFERINFO); + + if (acornscsi_write_pio(host, SCpnt->cmnd, + (int *)&host->scsi.SCp.sent_command, SCpnt->cmd_len, 1000000)) + printk("scsi%d: timeout while sending command\n", host->host->host_no); + host->scsi.phase = PHASE_COMMAND; } static -void acornscsi_sendmessage (AS_Host *host) +void acornscsi_sendmessage(AS_Host *host) { - unsigned int message_length = msgqueue_msglength (&host->scsi.msgs); - int msgnr; + unsigned int message_length = msgqueue_msglength(&host->scsi.msgs); + unsigned int msgnr; struct message *msg; #if (DEBUG & DEBUG_MESSAGES) - printk ("scsi%d.%c: sending message ", - host->host->host_no, acornscsi_target (host)); + printk("scsi%d.%c: sending message ", + host->host->host_no, acornscsi_target(host)); #endif switch (message_length) { case 0: - acornscsi_sbic_issuecmd (host, CMND_XFERINFO | CMND_SBT); - while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_DBR) == 0); - sbic_arm_write (host->scsi.io_port, DATA, NOP); + acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT); + + acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "sending message 1"); + + sbic_arm_write(host->scsi.io_port, DATA, NOP); + host->scsi.last_message = NOP; #if (DEBUG & DEBUG_MESSAGES) - printk ("NOP"); + printk("NOP"); #endif break; case 1: - acornscsi_sbic_issuecmd (host, CMND_XFERINFO | CMND_SBT); + acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT); msg = msgqueue_getmsg(&host->scsi.msgs, 0); - while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_DBR) == 0); - sbic_arm_write (host->scsi.io_port, DATA, msg->msg[0]); + + acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "sending message 2"); + + sbic_arm_write(host->scsi.io_port, DATA, msg->msg[0]); + host->scsi.last_message = msg->msg[0]; #if (DEBUG & DEBUG_MESSAGES) print_msg(msg->msg); @@ -1300,86 +1390,85 @@ void acornscsi_sendmessage (AS_Host *host) * initiator. This provides an interlock so that the * initiator can determine which message byte is rejected. */ - sbic_arm_write (host->scsi.io_port, TRANSCNTH, 0); - sbic_arm_writenext (host->scsi.io_port, 0); - sbic_arm_writenext (host->scsi.io_port, message_length); - acornscsi_sbic_issuecmd (host, CMND_XFERINFO); + sbic_arm_write(host->scsi.io_port, TRANSCNTH, 0); + sbic_arm_writenext(host->scsi.io_port, 0); + sbic_arm_writenext(host->scsi.io_port, message_length); + acornscsi_sbic_issuecmd(host, CMND_XFERINFO); msgnr = 0; while ((msg = msgqueue_getmsg(&host->scsi.msgs, msgnr++)) != NULL) { - unsigned int asr, i; + unsigned int i; #if (DEBUG & DEBUG_MESSAGES) - print_msg (msg); + print_msg(msg); #endif - for (i = 0; i < msg->length;) { - asr = sbic_arm_read (host->scsi.io_port, ASR); - if (asr & ASR_DBR) - sbic_arm_write (host->scsi.io_port, DATA, msg->msg[i++]); - if (asr & ASR_INT) - break; - } + i = 0; + if (acornscsi_write_pio(host, msg->msg, &i, msg->length, 1000000)) + printk("scsi%d: timeout while sending message\n", host->host->host_no); + host->scsi.last_message = msg->msg[0]; if (msg->msg[0] == EXTENDED_MESSAGE) host->scsi.last_message |= msg->msg[2] << 8; - if (asr & ASR_INT) + + if (i != msg->length) break; } break; } #if (DEBUG & DEBUG_MESSAGES) - printk ("\n"); + printk("\n"); #endif } /* - * Function: void acornscsi_readstatusbyte (AS_Host *host) + * Function: void acornscsi_readstatusbyte(AS_Host *host) * Purpose : Read status byte from connected target * Params : host - host connected to target */ static -void acornscsi_readstatusbyte (AS_Host *host) +void acornscsi_readstatusbyte(AS_Host *host) { - acornscsi_sbic_issuecmd (host, CMND_XFERINFO|CMND_SBT); - while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_DBR) == 0); - - host->scsi.SCp.Status = sbic_arm_read (host->scsi.io_port, DATA); + acornscsi_sbic_issuecmd(host, CMND_XFERINFO|CMND_SBT); + acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "reading status byte"); + host->scsi.SCp.Status = sbic_arm_read(host->scsi.io_port, DATA); } /* - * Function: unsigned char acornscsi_readmessagebyte (AS_Host *host) + * Function: unsigned char acornscsi_readmessagebyte(AS_Host *host) * Purpose : Read one message byte from connected target * Params : host - host connected to target */ static -unsigned char acornscsi_readmessagebyte (AS_Host *host) +unsigned char acornscsi_readmessagebyte(AS_Host *host) { unsigned char message; - acornscsi_sbic_issuecmd (host, CMND_XFERINFO | CMND_SBT); - while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_DBR) == 0); + acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT); + + acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "for message byte"); - message = sbic_arm_read (host->scsi.io_port, DATA); + message = sbic_arm_read(host->scsi.io_port, DATA); /* wait for MSGIN-XFER-PAUSED */ - while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_INT) == 0); - sbic_arm_read (host->scsi.io_port, SSR); + acornscsi_sbic_wait(host, ASR_INT, ASR_INT, 1000, "for interrupt after message byte"); + + sbic_arm_read(host->scsi.io_port, SSR); return message; } /* - * Function: void acornscsi_message (AS_Host *host) + * Function: void acornscsi_message(AS_Host *host) * Purpose : Read complete message from connected target & action message * Params : host - host connected to target */ static -void acornscsi_message (AS_Host *host) +void acornscsi_message(AS_Host *host) { unsigned char message[16]; unsigned int msgidx = 0, msglen = 1; do { - message[msgidx] = acornscsi_readmessagebyte (host); + message[msgidx] = acornscsi_readmessagebyte(host); switch (msgidx) { case 0: @@ -1395,17 +1484,17 @@ void acornscsi_message (AS_Host *host) } msgidx += 1; if (msgidx < msglen) { - acornscsi_sbic_issuecmd (host, CMND_NEGATEACK); + acornscsi_sbic_issuecmd(host, CMND_NEGATEACK); /* wait for next msg-in */ - while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_INT) == 0); - sbic_arm_read (host->scsi.io_port, SSR); + acornscsi_sbic_wait(host, ASR_INT, ASR_INT, 1000, "for interrupt after negate ack"); + sbic_arm_read(host->scsi.io_port, SSR); } } while (msgidx < msglen); #if (DEBUG & DEBUG_MESSAGES) printk("scsi%d.%c: message in: ", - host->host->host_no, acornscsi_target (host)); + host->host->host_no, acornscsi_target(host)); print_msg(message); printk("\n"); #endif @@ -1419,7 +1508,7 @@ void acornscsi_message (AS_Host *host) */ if (message[0] == SIMPLE_QUEUE_TAG) host->scsi.reconnected.tag = message[1]; - if (acornscsi_reconnect_finish (host)) + if (acornscsi_reconnect_finish(host)) host->scsi.phase = PHASE_MSGIN; } @@ -1429,7 +1518,7 @@ void acornscsi_message (AS_Host *host) case COMMAND_COMPLETE: if (host->scsi.phase != PHASE_STATUSIN) { printk(KERN_ERR "scsi%d.%c: command complete following non-status in phase?\n", - host->host->host_no, acornscsi_target (host)); + host->host->host_no, acornscsi_target(host)); acornscsi_dumplog(host, host->SCpnt->target); } host->scsi.phase = PHASE_DONE; @@ -1443,7 +1532,7 @@ void acornscsi_message (AS_Host *host) * direct the initiator to copy the active data pointer to * the saved data pointer for the current I/O process. */ - acornscsi_dma_cleanup (host); + acornscsi_dma_cleanup(host); host->SCpnt->SCp = host->scsi.SCp; host->SCpnt->SCp.sent_command = 0; host->scsi.phase = PHASE_MSGIN; @@ -1459,7 +1548,7 @@ void acornscsi_message (AS_Host *host) * status pointers shall be restored to the beginning of * the present command and status areas.' */ - acornscsi_dma_cleanup (host); + acornscsi_dma_cleanup(host); host->scsi.SCp = host->SCpnt->SCp; host->scsi.phase = PHASE_MSGIN; break; @@ -1474,7 +1563,7 @@ void acornscsi_message (AS_Host *host) * message. When reconnection is completed, the most recent * saved pointer values are restored.' */ - acornscsi_dma_cleanup (host); + acornscsi_dma_cleanup(host); host->scsi.phase = PHASE_DISCONNECT; break; @@ -1493,8 +1582,8 @@ void acornscsi_message (AS_Host *host) /* * If we have any messages waiting to go out, then assert ATN now */ - if (msgqueue_msglength (&host->scsi.msgs)) - acornscsi_sbic_issuecmd (host, CMND_ASSERTATN); + if (msgqueue_msglength(&host->scsi.msgs)) + acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); switch (host->scsi.last_message) { #ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE @@ -1507,21 +1596,21 @@ void acornscsi_message (AS_Host *host) * message is received, it shall respond with a MESSAGE REJECT * message and accept the I/O process as if it were untagged. */ - printk (KERN_NOTICE "scsi%d.%c: disabling tagged queueing\n", - host->host->host_no, acornscsi_target (host)); + printk(KERN_NOTICE "scsi%d.%c: disabling tagged queueing\n", + host->host->host_no, acornscsi_target(host)); host->SCpnt->device->tagged_queue = 0; - set_bit (host->SCpnt->target * 8 + host->SCpnt->lun, &host->busyluns); + set_bit(host->SCpnt->target * 8 + host->SCpnt->lun, &host->busyluns); break; #endif case EXTENDED_MESSAGE | (EXTENDED_SDTR << 8): /* * Target can't handle synchronous transfers */ - printk (KERN_NOTICE "scsi%d.%c: Using asynchronous transfer\n", - host->host->host_no, acornscsi_target (host)); + printk(KERN_NOTICE "scsi%d.%c: Using asynchronous transfer\n", + host->host->host_no, acornscsi_target(host)); host->device[host->SCpnt->target].sync_xfer = SYNCHTRANSFER_2DBA; host->device[host->SCpnt->target].sync_state = SYNC_ASYNCHRONOUS; - sbic_arm_write (host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->target].sync_xfer); + sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->target].sync_xfer); break; default: @@ -1535,8 +1624,8 @@ void acornscsi_message (AS_Host *host) case SIMPLE_QUEUE_TAG: /* tag queue reconnect... message[1] = queue tag. Print something to indicate something happened! */ - printk ("scsi%d.%c: reconnect queue tag %02X\n", - host->host->host_no, acornscsi_target (host), + printk("scsi%d.%c: reconnect queue tag %02X\n", + host->host->host_no, acornscsi_target(host), message[1]); break; @@ -1552,26 +1641,26 @@ void acornscsi_message (AS_Host *host) * and the target retries fail, then we fallback to asynchronous mode */ host->device[host->SCpnt->target].sync_state = SYNC_COMPLETED; - printk (KERN_NOTICE "scsi%d.%c: Using synchronous transfer, offset %d, %d ns\n", + printk(KERN_NOTICE "scsi%d.%c: Using synchronous transfer, offset %d, %d ns\n", host->host->host_no, acornscsi_target(host), message[4], message[3] * 4); host->device[host->SCpnt->target].sync_xfer = - calc_sync_xfer (message[3] * 4, message[4]); + calc_sync_xfer(message[3] * 4, message[4]); } else { unsigned char period, length; /* * Target requested synchronous transfers. The agreement is only * to be in operation AFTER the target leaves message out phase. */ - acornscsi_sbic_issuecmd (host, CMND_ASSERTATN); - period = max (message[3], sdtr_period / 4); - length = min (message[4], sdtr_size); - msgqueue_addmsg (&host->scsi.msgs, 5, EXTENDED_MESSAGE, 3, + acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); + period = max(message[3], sdtr_period / 4); + length = min(message[4], sdtr_size); + msgqueue_addmsg(&host->scsi.msgs, 5, EXTENDED_MESSAGE, 3, EXTENDED_SDTR, period, length); host->device[host->SCpnt->target].sync_xfer = - calc_sync_xfer (period * 4, length); + calc_sync_xfer(period * 4, length); } - sbic_arm_write (host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->target].sync_xfer); + sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->target].sync_xfer); break; #else /* We do not accept synchronous transfers. Respond with a @@ -1584,9 +1673,9 @@ void acornscsi_message (AS_Host *host) * to a wide data transfer request. */ default: - acornscsi_sbic_issuecmd (host, CMND_ASSERTATN); - msgqueue_flush (&host->scsi.msgs); - msgqueue_addmsg (&host->scsi.msgs, 1, MESSAGE_REJECT); + acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); + msgqueue_flush(&host->scsi.msgs); + msgqueue_addmsg(&host->scsi.msgs, 1, MESSAGE_REJECT); break; } break; @@ -1607,19 +1696,19 @@ void acornscsi_message (AS_Host *host) * if there are more linked commands available. */ if (!host->SCpnt->next_link) { - printk (KERN_WARNING "scsi%d.%c: lun %d tag %d linked command complete, but no next_link\n", - instance->host_no, acornscsi_target (host), host->SCpnt->tag); - acornscsi_sbic_issuecmd (host, CMND_ASSERTATN); - msgqueue_addmsg (&host->scsi.msgs, 1, ABORT); + printk(KERN_WARNING "scsi%d.%c: lun %d tag %d linked command complete, but no next_link\n", + instance->host_no, acornscsi_target(host), host->SCpnt->tag); + acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); + msgqueue_addmsg(&host->scsi.msgs, 1, ABORT); } else { Scsi_Cmnd *SCpnt = host->SCpnt; - acornscsi_dma_cleanup (host); + acornscsi_dma_cleanup(host); host->SCpnt = host->SCpnt->next_link; host->SCpnt->tag = SCpnt->tag; SCpnt->result = DID_OK | host->scsi.SCp.Message << 8 | host->Scsi.SCp.Status; - SCpnt->done (SCpnt); + SCpnt->done(SCpnt); /* initialise host->SCpnt->SCp */ } @@ -1628,42 +1717,42 @@ void acornscsi_message (AS_Host *host) #endif default: /* reject message */ - printk (KERN_ERR "scsi%d.%c: unrecognised message %02X, rejecting\n", - host->host->host_no, acornscsi_target (host), + printk(KERN_ERR "scsi%d.%c: unrecognised message %02X, rejecting\n", + host->host->host_no, acornscsi_target(host), message[0]); - acornscsi_sbic_issuecmd (host, CMND_ASSERTATN); - msgqueue_flush (&host->scsi.msgs); - msgqueue_addmsg (&host->scsi.msgs, 1, MESSAGE_REJECT); + acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); + msgqueue_flush(&host->scsi.msgs); + msgqueue_addmsg(&host->scsi.msgs, 1, MESSAGE_REJECT); host->scsi.phase = PHASE_MSGIN; break; } - acornscsi_sbic_issuecmd (host, CMND_NEGATEACK); + acornscsi_sbic_issuecmd(host, CMND_NEGATEACK); } /* - * Function: int acornscsi_buildmessages (AS_Host *host) + * Function: int acornscsi_buildmessages(AS_Host *host) * Purpose : build the connection messages for a host * Params : host - host to add messages to */ static -void acornscsi_buildmessages (AS_Host *host) +void acornscsi_buildmessages(AS_Host *host) { #if 0 /* does the device need resetting? */ if (cmd_reset) { - msgqueue_addmsg (&host->scsi.msgs, 1, BUS_DEVICE_RESET); + msgqueue_addmsg(&host->scsi.msgs, 1, BUS_DEVICE_RESET); return; } #endif - msgqueue_addmsg (&host->scsi.msgs, 1, + msgqueue_addmsg(&host->scsi.msgs, 1, IDENTIFY(host->device[host->SCpnt->target].disconnect_ok, host->SCpnt->lun)); #if 0 /* does the device need the current command aborted */ if (cmd_aborted) { - acornscsi_abortcmd (host->SCpnt->tag); + acornscsi_abortcmd(host->SCpnt->tag); return; } #endif @@ -1678,14 +1767,14 @@ void acornscsi_buildmessages (AS_Host *host) tag_type = HEAD_OF_QUEUE_TAG; else tag_type = SIMPLE_QUEUE_TAG; - msgqueue_addmsg (&host->scsi.msgs, 2, tag_type, host->SCpnt->tag); + msgqueue_addmsg(&host->scsi.msgs, 2, tag_type, host->SCpnt->tag); } #endif #ifdef CONFIG_SCSI_ACORNSCSI_SYNC if (host->device[host->SCpnt->target].sync_state == SYNC_NEGOCIATE) { host->device[host->SCpnt->target].sync_state = SYNC_SENT_REQUEST; - msgqueue_addmsg (&host->scsi.msgs, 5, + msgqueue_addmsg(&host->scsi.msgs, 5, EXTENDED_MESSAGE, 3, EXTENDED_SDTR, sdtr_period / 4, sdtr_size); } @@ -1693,29 +1782,29 @@ void acornscsi_buildmessages (AS_Host *host) } /* - * Function: int acornscsi_starttransfer (AS_Host *host) + * Function: int acornscsi_starttransfer(AS_Host *host) * Purpose : transfer data to/from connected target * Params : host - host to which target is connected * Returns : 0 if failure */ static -int acornscsi_starttransfer (AS_Host *host) +int acornscsi_starttransfer(AS_Host *host) { int residual; if (!host->scsi.SCp.ptr /*&& host->scsi.SCp.this_residual*/) { - printk (KERN_ERR "scsi%d.%c: null buffer passed to acornscsi_starttransfer\n", - host->host->host_no, acornscsi_target (host)); + printk(KERN_ERR "scsi%d.%c: null buffer passed to acornscsi_starttransfer\n", + host->host->host_no, acornscsi_target(host)); return 0; } residual = host->SCpnt->request_bufflen - host->scsi.SCp.scsi_xferred; - sbic_arm_write (host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->target].sync_xfer); - sbic_arm_writenext (host->scsi.io_port, residual >> 16); - sbic_arm_writenext (host->scsi.io_port, residual >> 8); - sbic_arm_writenext (host->scsi.io_port, residual); - acornscsi_sbic_issuecmd (host, CMND_XFERINFO); + sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->target].sync_xfer); + sbic_arm_writenext(host->scsi.io_port, residual >> 16); + sbic_arm_writenext(host->scsi.io_port, residual >> 8); + sbic_arm_writenext(host->scsi.io_port, residual); + acornscsi_sbic_issuecmd(host, CMND_XFERINFO); return 1; } @@ -1723,7 +1812,7 @@ int acornscsi_starttransfer (AS_Host *host) * Connection & Disconnection */ /* - * Function : acornscsi_reconnect (AS_Host *host) + * Function : acornscsi_reconnect(AS_Host *host) * Purpose : reconnect a previously disconnected command * Params : host - host specific data * Remarks : SCSI spec says: @@ -1731,27 +1820,27 @@ int acornscsi_starttransfer (AS_Host *host) * of saved pointers upon reconnection of the I/O process' */ static -int acornscsi_reconnect (AS_Host *host) +int acornscsi_reconnect(AS_Host *host) { unsigned int target, lun, ok = 0; - target = sbic_arm_read (host->scsi.io_port, SOURCEID); + target = sbic_arm_read(host->scsi.io_port, SOURCEID); if (!(target & 8)) - printk (KERN_ERR "scsi%d: invalid source id after reselection " + printk(KERN_ERR "scsi%d: invalid source id after reselection " "- device fault?\n", host->host->host_no); target &= 7; if (host->SCpnt && !host->scsi.disconnectable) { - printk (KERN_ERR "scsi%d.%d: reconnected while command in " + printk(KERN_ERR "scsi%d.%d: reconnected while command in " "progress to target %d?\n", host->host->host_no, target, host->SCpnt->target); host->SCpnt = NULL; } - lun = sbic_arm_read (host->scsi.io_port, DATA) & 7; + lun = sbic_arm_read(host->scsi.io_port, DATA) & 7; host->scsi.reconnected.target = target; host->scsi.reconnected.lun = lun; @@ -1761,7 +1850,7 @@ int acornscsi_reconnect (AS_Host *host) host->SCpnt->target == target && host->SCpnt->lun == lun) ok = 1; - if (!ok && queue_probetgtlun (&host->queues.disconnected, target, lun)) + if (!ok && queue_probetgtlun(&host->queues.disconnected, target, lun)) ok = 1; ADD_STATUS(target, 0x81, host->scsi.phase, 0); @@ -1770,26 +1859,28 @@ int acornscsi_reconnect (AS_Host *host) host->scsi.phase = PHASE_RECONNECTED; } else { /* this doesn't seem to work */ - printk (KERN_ERR "scsi%d.%c: reselected with no command " + printk(KERN_ERR "scsi%d.%c: reselected with no command " "to reconnect with\n", host->host->host_no, '0' + target); - acornscsi_dumplog (host, target); - acornscsi_sbic_issuecmd (host, CMND_ASSERTATN); - msgqueue_addmsg (&host->scsi.msgs, 1, ABORT); - host->scsi.phase = PHASE_ABORTED; + acornscsi_dumplog(host, target); + acornscsi_abortcmd(host, 0); + if (host->SCpnt) { + queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt); + host->SCpnt = NULL; + } } - acornscsi_sbic_issuecmd (host, CMND_NEGATEACK); + acornscsi_sbic_issuecmd(host, CMND_NEGATEACK); return !ok; } /* - * Function: int acornscsi_reconect_finish (AS_Host *host) + * Function: int acornscsi_reconect_finish(AS_Host *host) * Purpose : finish reconnecting a command * Params : host - host to complete * Returns : 0 if failed */ static -int acornscsi_reconnect_finish (AS_Host *host) +int acornscsi_reconnect_finish(AS_Host *host) { if (host->scsi.disconnectable && host->SCpnt) { host->scsi.disconnectable = 0; @@ -1797,45 +1888,44 @@ int acornscsi_reconnect_finish (AS_Host *host) host->SCpnt->lun == host->scsi.reconnected.lun && host->SCpnt->tag == host->scsi.reconnected.tag) { #if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) - DBG(host->SCpnt, printk ("scsi%d.%c: reconnected", - host->host->host_no, acornscsi_target (host))); + DBG(host->SCpnt, printk("scsi%d.%c: reconnected", + host->host->host_no, acornscsi_target(host))); #endif } else { - queue_add_cmd_tail (&host->queues.disconnected, host->SCpnt); + queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt); #if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) - DBG(host->SCpnt, printk ("scsi%d.%c: had to move command " + DBG(host->SCpnt, printk("scsi%d.%c: had to move command " "to disconnected queue\n", - host->host->host_no, acornscsi_target (host))); + host->host->host_no, acornscsi_target(host))); #endif host->SCpnt = NULL; } } if (!host->SCpnt) { - host->SCpnt = queue_remove_tgtluntag (&host->queues.disconnected, + host->SCpnt = queue_remove_tgtluntag(&host->queues.disconnected, host->scsi.reconnected.target, host->scsi.reconnected.lun, host->scsi.reconnected.tag); #if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) - DBG(host->SCpnt, printk ("scsi%d.%c: had to get command", - host->host->host_no, acornscsi_target (host))); + DBG(host->SCpnt, printk("scsi%d.%c: had to get command", + host->host->host_no, acornscsi_target(host))); #endif } - if (!host->SCpnt) { - acornscsi_abortcmd (host, host->scsi.reconnected.tag); - host->scsi.phase = PHASE_ABORTED; - } else { + if (!host->SCpnt) + acornscsi_abortcmd(host, host->scsi.reconnected.tag); + else { /* * Restore data pointer from SAVED pointers. */ host->scsi.SCp = host->SCpnt->SCp; #if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) - printk (", data pointers: [%p, %X]", + printk(", data pointers: [%p, %X]", host->scsi.SCp.ptr, host->scsi.SCp.this_residual); #endif } #if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) - printk ("\n"); + printk("\n"); #endif host->dma.transferred = host->scsi.SCp.scsi_xferred; @@ -1844,47 +1934,48 @@ int acornscsi_reconnect_finish (AS_Host *host) } /* - * Function: void acornscsi_disconnect_unexpected (AS_Host *host) + * Function: void acornscsi_disconnect_unexpected(AS_Host *host) * Purpose : handle an unexpected disconnect * Params : host - host on which disconnect occurred */ static -void acornscsi_disconnect_unexpected (AS_Host *host) +void acornscsi_disconnect_unexpected(AS_Host *host) { - printk (KERN_ERR "scsi%d.%c: unexpected disconnect\n", - host->host->host_no, acornscsi_target (host)); + printk(KERN_ERR "scsi%d.%c: unexpected disconnect\n", + host->host->host_no, acornscsi_target(host)); #if (DEBUG & DEBUG_ABORT) - acornscsi_dumplog (host, 8); + acornscsi_dumplog(host, 8); #endif - acornscsi_done (host, &host->SCpnt, DID_ABORT); + acornscsi_done(host, &host->SCpnt, DID_ERROR); } /* - * Function: void acornscsi_abortcmd (AS_host *host, unsigned char tag) + * Function: void acornscsi_abortcmd(AS_host *host, unsigned char tag) * Purpose : abort a currently executing command * Params : host - host with connected command to abort * tag - tag to abort */ static -void acornscsi_abortcmd (AS_Host *host, unsigned char tag) +void acornscsi_abortcmd(AS_Host *host, unsigned char tag) { - sbic_arm_write (host->scsi.io_port, CMND, CMND_ASSERTATN); + host->scsi.phase = PHASE_ABORTED; + sbic_arm_write(host->scsi.io_port, CMND, CMND_ASSERTATN); - msgqueue_flush (&host->scsi.msgs); + msgqueue_flush(&host->scsi.msgs); #ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE if (tag) - msgqueue_addmsg (&host->scsi.msgs, 2, ABORT_TAG, tag); + msgqueue_addmsg(&host->scsi.msgs, 2, ABORT_TAG, tag); else #endif - msgqueue_addmsg (&host->scsi.msgs, 1, ABORT); + msgqueue_addmsg(&host->scsi.msgs, 1, ABORT); } /* ========================================================================================== * Interrupt routines. */ /* - * Function: int acornscsi_sbicintr (AS_Host *host) + * Function: int acornscsi_sbicintr(AS_Host *host) * Purpose : handle interrupts from SCSI device * Params : host - host to process * Returns : INTR_PROCESS if expecting another SBIC interrupt @@ -1892,15 +1983,15 @@ void acornscsi_abortcmd (AS_Host *host, unsigned char tag) * INTR_NEXT_COMMAND if we have finished processing the command */ static -intr_ret_t acornscsi_sbicintr (AS_Host *host, int in_irq) +intr_ret_t acornscsi_sbicintr(AS_Host *host, int in_irq) { unsigned int asr, ssr; - asr = sbic_arm_read (host->scsi.io_port, ASR); + asr = sbic_arm_read(host->scsi.io_port, ASR); if (!(asr & ASR_INT)) return INTR_IDLE; - ssr = sbic_arm_read (host->scsi.io_port, SSR); + ssr = sbic_arm_read(host->scsi.io_port, SSR); #if (DEBUG & DEBUG_PHASES) print_sbic_status(asr, ssr, host->scsi.phase); @@ -1913,23 +2004,23 @@ intr_ret_t acornscsi_sbicintr (AS_Host *host, int in_irq) switch (ssr) { case 0x00: /* reset state - not advanced */ - printk (KERN_ERR "scsi%d: reset in standard mode but wanted advanced mode.\n", + printk(KERN_ERR "scsi%d: reset in standard mode but wanted advanced mode.\n", host->host->host_no); /* setup sbic - WD33C93A */ - sbic_arm_write (host->scsi.io_port, OWNID, OWNID_EAF | host->host->this_id); - sbic_arm_write (host->scsi.io_port, CMND, CMND_RESET); + sbic_arm_write(host->scsi.io_port, OWNID, OWNID_EAF | host->host->this_id); + sbic_arm_write(host->scsi.io_port, CMND, CMND_RESET); return INTR_IDLE; case 0x01: /* reset state - advanced */ - sbic_arm_write (host->scsi.io_port, CTRL, INIT_SBICDMA | CTRL_IDI); - sbic_arm_write (host->scsi.io_port, TIMEOUT, TIMEOUT_TIME); - sbic_arm_write (host->scsi.io_port, SYNCHTRANSFER, SYNCHTRANSFER_2DBA); - sbic_arm_write (host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP); - msgqueue_flush (&host->scsi.msgs); + sbic_arm_write(host->scsi.io_port, CTRL, INIT_SBICDMA | CTRL_IDI); + sbic_arm_write(host->scsi.io_port, TIMEOUT, TIMEOUT_TIME); + sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, SYNCHTRANSFER_2DBA); + sbic_arm_write(host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP); + msgqueue_flush(&host->scsi.msgs); return INTR_IDLE; case 0x41: /* unexpected disconnect aborted command */ - acornscsi_disconnect_unexpected (host); + acornscsi_disconnect_unexpected(host); return INTR_NEXT_COMMAND; } @@ -1939,35 +2030,35 @@ intr_ret_t acornscsi_sbicintr (AS_Host *host, int in_irq) case 0x11: /* -> PHASE_CONNECTED */ /* BUS FREE -> SELECTION */ host->scsi.phase = PHASE_CONNECTED; - msgqueue_flush (&host->scsi.msgs); + msgqueue_flush(&host->scsi.msgs); host->dma.transferred = host->scsi.SCp.scsi_xferred; /* 33C93 gives next interrupt indicating bus phase */ - asr = sbic_arm_read (host->scsi.io_port, ASR); + asr = sbic_arm_read(host->scsi.io_port, ASR); if (!(asr & ASR_INT)) break; - ssr = sbic_arm_read (host->scsi.io_port, SSR); + ssr = sbic_arm_read(host->scsi.io_port, SSR); ADD_STATUS(8, ssr, host->scsi.phase, 1); ADD_STATUS(host->SCpnt->target, ssr, host->scsi.phase, 1); goto connected; case 0x42: /* select timed out */ /* -> PHASE_IDLE */ - acornscsi_done (host, &host->SCpnt, DID_NO_CONNECT); + acornscsi_done(host, &host->SCpnt, DID_NO_CONNECT); return INTR_NEXT_COMMAND; case 0x81: /* -> PHASE_RECONNECTED or PHASE_ABORTED */ /* BUS FREE -> RESELECTION */ host->origSCpnt = host->SCpnt; host->SCpnt = NULL; - msgqueue_flush (&host->scsi.msgs); - acornscsi_reconnect (host); + msgqueue_flush(&host->scsi.msgs); + acornscsi_reconnect(host); break; default: - printk (KERN_ERR "scsi%d.%c: PHASE_CONNECTING, SSR %02X?\n", - host->host->host_no, acornscsi_target (host), ssr); - acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8); - acornscsi_abortcmd (host, host->SCpnt->tag); + printk(KERN_ERR "scsi%d.%c: PHASE_CONNECTING, SSR %02X?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8); + acornscsi_abortcmd(host, host->SCpnt->tag); } return INTR_PROCESSING; @@ -1977,12 +2068,12 @@ intr_ret_t acornscsi_sbicintr (AS_Host *host, int in_irq) #ifdef NONSTANDARD case 0x8a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */ /* SELECTION -> COMMAND */ - acornscsi_sendcommand (host); + acornscsi_sendcommand(host); break; case 0x8b: /* -> PHASE_STATUS */ /* SELECTION -> STATUS */ - acornscsi_readstatusbyte (host); + acornscsi_readstatusbyte(host); host->scsi.phase = PHASE_STATUSIN; break; #endif @@ -1990,55 +2081,57 @@ intr_ret_t acornscsi_sbicintr (AS_Host *host, int in_irq) case 0x8e: /* -> PHASE_MSGOUT */ /* SELECTION ->MESSAGE OUT */ host->scsi.phase = PHASE_MSGOUT; - acornscsi_buildmessages (host); - acornscsi_sendmessage (host); + acornscsi_buildmessages(host); + acornscsi_sendmessage(host); break; /* these should not happen */ case 0x85: /* target disconnected */ - acornscsi_done (host, &host->SCpnt, DID_ERROR); + acornscsi_done(host, &host->SCpnt, DID_ERROR); break; default: - printk (KERN_ERR "scsi%d.%c: PHASE_CONNECTED, SSR %02X?\n", - host->host->host_no, acornscsi_target (host), ssr); - acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8); - acornscsi_abortcmd (host, host->SCpnt->tag); + printk(KERN_ERR "scsi%d.%c: PHASE_CONNECTED, SSR %02X?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8); + acornscsi_abortcmd(host, host->SCpnt->tag); } return INTR_PROCESSING; case PHASE_MSGOUT: /* STATE: connected & sent IDENTIFY message */ /* - * SCSI standard says th at a MESSAGE OUT phases can be followed by a DATA phase + * SCSI standard says that MESSAGE OUT phases can be followed by a + * DATA phase, STATUS phase, MESSAGE IN phase or COMMAND phase */ switch (ssr) { - case 0x8a: + case 0x8a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */ case 0x1a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */ /* MESSAGE OUT -> COMMAND */ - acornscsi_sendcommand (host); + acornscsi_sendcommand(host); break; + case 0x8b: /* -> PHASE_STATUS */ case 0x1b: /* -> PHASE_STATUS */ /* MESSAGE OUT -> STATUS */ - acornscsi_readstatusbyte (host); + acornscsi_readstatusbyte(host); host->scsi.phase = PHASE_STATUSIN; break; case 0x8e: /* -> PHASE_MSGOUT */ /* MESSAGE_OUT(MESSAGE_IN) ->MESSAGE OUT */ - acornscsi_sendmessage (host); + acornscsi_sendmessage(host); break; - case 0x4f: + case 0x4f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */ case 0x1f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */ /* MESSAGE OUT -> MESSAGE IN */ - acornscsi_message (host); + acornscsi_message(host); break; default: - printk (KERN_ERR "scsi%d.%c: PHASE_MSGOUT, SSR %02X?\n", - host->host->host_no, acornscsi_target (host), ssr); - acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8); + printk(KERN_ERR "scsi%d.%c: PHASE_MSGOUT, SSR %02X?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8); } return INTR_PROCESSING; @@ -2047,43 +2140,43 @@ intr_ret_t acornscsi_sbicintr (AS_Host *host, int in_irq) case 0x18: /* -> PHASE_DATAOUT */ /* COMMAND -> DATA OUT */ if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len) - acornscsi_abortcmd (host, host->SCpnt->tag); - acornscsi_dma_setup (host, DMA_OUT); - if (!acornscsi_starttransfer (host)) - acornscsi_abortcmd (host, host->SCpnt->tag); + acornscsi_abortcmd(host, host->SCpnt->tag); + acornscsi_dma_setup(host, DMA_OUT); + if (!acornscsi_starttransfer(host)) + acornscsi_abortcmd(host, host->SCpnt->tag); host->scsi.phase = PHASE_DATAOUT; return INTR_IDLE; case 0x19: /* -> PHASE_DATAIN */ /* COMMAND -> DATA IN */ if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len) - acornscsi_abortcmd (host, host->SCpnt->tag); - acornscsi_dma_setup (host, DMA_IN); - if (!acornscsi_starttransfer (host)) - acornscsi_abortcmd (host, host->SCpnt->tag); + acornscsi_abortcmd(host, host->SCpnt->tag); + acornscsi_dma_setup(host, DMA_IN); + if (!acornscsi_starttransfer(host)) + acornscsi_abortcmd(host, host->SCpnt->tag); host->scsi.phase = PHASE_DATAIN; return INTR_IDLE; case 0x1b: /* -> PHASE_STATUS */ /* COMMAND -> STATUS */ - acornscsi_readstatusbyte (host); + acornscsi_readstatusbyte(host); host->scsi.phase = PHASE_STATUSIN; break; case 0x1e: /* -> PHASE_MSGOUT */ /* COMMAND -> MESSAGE OUT */ - acornscsi_sendmessage (host); + acornscsi_sendmessage(host); break; case 0x1f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */ /* COMMAND -> MESSAGE IN */ - acornscsi_message (host); + acornscsi_message(host); break; default: - printk (KERN_ERR "scsi%d.%c: PHASE_COMMAND, SSR %02X?\n", - host->host->host_no, acornscsi_target (host), ssr); - acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8); + printk(KERN_ERR "scsi%d.%c: PHASE_COMMAND, SSR %02X?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8); } return INTR_PROCESSING; @@ -2094,19 +2187,19 @@ intr_ret_t acornscsi_sbicintr (AS_Host *host, int in_irq) host->scsi.phase = PHASE_IDLE; host->stats.disconnects += 1; } else { - printk (KERN_ERR "scsi%d.%c: PHASE_DISCONNECT, SSR %02X instead of disconnect?\n", - host->host->host_no, acornscsi_target (host), ssr); - acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8); + printk(KERN_ERR "scsi%d.%c: PHASE_DISCONNECT, SSR %02X instead of disconnect?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8); } return INTR_NEXT_COMMAND; case PHASE_IDLE: /* STATE: disconnected */ if (ssr == 0x81) /* -> PHASE_RECONNECTED or PHASE_ABORTED */ - acornscsi_reconnect (host); + acornscsi_reconnect(host); else { - printk (KERN_ERR "scsi%d.%c: PHASE_IDLE, SSR %02X while idle?\n", - host->host->host_no, acornscsi_target (host), ssr); - acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8); + printk(KERN_ERR "scsi%d.%c: PHASE_IDLE, SSR %02X while idle?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8); } return INTR_PROCESSING; @@ -2119,54 +2212,54 @@ intr_ret_t acornscsi_sbicintr (AS_Host *host, int in_irq) * If we reconnected and we're not in MESSAGE IN phase after IDENTIFY, * reconnect I_T_L command */ - if (ssr != 0x8f && !acornscsi_reconnect_finish (host)) + if (ssr != 0x8f && !acornscsi_reconnect_finish(host)) return INTR_IDLE; ADD_STATUS(host->SCpnt->target, ssr, host->scsi.phase, in_irq); switch (ssr) { case 0x88: /* data out phase */ /* -> PHASE_DATAOUT */ /* MESSAGE IN -> DATA OUT */ - acornscsi_dma_setup (host, DMA_OUT); - if (!acornscsi_starttransfer (host)) - acornscsi_abortcmd (host, host->SCpnt->tag); + acornscsi_dma_setup(host, DMA_OUT); + if (!acornscsi_starttransfer(host)) + acornscsi_abortcmd(host, host->SCpnt->tag); host->scsi.phase = PHASE_DATAOUT; return INTR_IDLE; case 0x89: /* data in phase */ /* -> PHASE_DATAIN */ /* MESSAGE IN -> DATA IN */ - acornscsi_dma_setup (host, DMA_IN); - if (!acornscsi_starttransfer (host)) - acornscsi_abortcmd (host, host->SCpnt->tag); + acornscsi_dma_setup(host, DMA_IN); + if (!acornscsi_starttransfer(host)) + acornscsi_abortcmd(host, host->SCpnt->tag); host->scsi.phase = PHASE_DATAIN; return INTR_IDLE; case 0x8a: /* command out */ /* MESSAGE IN -> COMMAND */ - acornscsi_sendcommand (host);/* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */ + acornscsi_sendcommand(host);/* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */ break; case 0x8b: /* status in */ /* -> PHASE_STATUSIN */ /* MESSAGE IN -> STATUS */ - acornscsi_readstatusbyte (host); + acornscsi_readstatusbyte(host); host->scsi.phase = PHASE_STATUSIN; break; case 0x8e: /* message out */ /* -> PHASE_MSGOUT */ /* MESSAGE IN -> MESSAGE OUT */ - acornscsi_sendmessage (host); + acornscsi_sendmessage(host); break; case 0x8f: /* message in */ - acornscsi_message (host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */ + acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */ break; default: - printk (KERN_ERR "scsi%d.%c: PHASE_RECONNECTED, SSR %02X after reconnect?\n", - host->host->host_no, acornscsi_target (host), ssr); - acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8); + printk(KERN_ERR "scsi%d.%c: PHASE_RECONNECTED, SSR %02X after reconnect?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8); } return INTR_PROCESSING; @@ -2177,41 +2270,45 @@ intr_ret_t acornscsi_sbicintr (AS_Host *host, int in_irq) */ switch (ssr) { case 0x19: /* -> PHASE_DATAIN */ - acornscsi_abortcmd (host, host->SCpnt->tag); + case 0x89: /* -> PHASE_DATAIN */ + acornscsi_abortcmd(host, host->SCpnt->tag); return INTR_IDLE; - case 0x4b: /* -> PHASE_STATUSIN */ case 0x1b: /* -> PHASE_STATUSIN */ + case 0x4b: /* -> PHASE_STATUSIN */ + case 0x8b: /* -> PHASE_STATUSIN */ /* DATA IN -> STATUS */ host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - - acornscsi_sbic_xfcount (host); - acornscsi_dma_stop (host); - acornscsi_readstatusbyte (host); + acornscsi_sbic_xfcount(host); + acornscsi_dma_stop(host); + acornscsi_readstatusbyte(host); host->scsi.phase = PHASE_STATUSIN; break; case 0x1e: /* -> PHASE_MSGOUT */ case 0x4e: /* -> PHASE_MSGOUT */ + case 0x8e: /* -> PHASE_MSGOUT */ /* DATA IN -> MESSAGE OUT */ host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - - acornscsi_sbic_xfcount (host); - acornscsi_dma_stop (host); - acornscsi_sendmessage (host); + acornscsi_sbic_xfcount(host); + acornscsi_dma_stop(host); + acornscsi_sendmessage(host); break; case 0x1f: /* message in */ case 0x4f: /* message in */ + case 0x8f: /* message in */ /* DATA IN -> MESSAGE IN */ host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - - acornscsi_sbic_xfcount (host); - acornscsi_dma_stop (host); - acornscsi_message (host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */ + acornscsi_sbic_xfcount(host); + acornscsi_dma_stop(host); + acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */ break; default: - printk (KERN_ERR "scsi%d.%c: PHASE_DATAIN, SSR %02X?\n", - host->host->host_no, acornscsi_target (host), ssr); - acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8); + printk(KERN_ERR "scsi%d.%c: PHASE_DATAIN, SSR %02X?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8); } return INTR_PROCESSING; @@ -2222,58 +2319,69 @@ intr_ret_t acornscsi_sbicintr (AS_Host *host, int in_irq) */ switch (ssr) { case 0x18: /* -> PHASE_DATAOUT */ - acornscsi_abortcmd (host, host->SCpnt->tag); + case 0x88: /* -> PHASE_DATAOUT */ + acornscsi_abortcmd(host, host->SCpnt->tag); return INTR_IDLE; - case 0x4b: /* -> PHASE_STATUSIN */ case 0x1b: /* -> PHASE_STATUSIN */ + case 0x4b: /* -> PHASE_STATUSIN */ + case 0x8b: /* -> PHASE_STATUSIN */ /* DATA OUT -> STATUS */ host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - - acornscsi_sbic_xfcount (host); - acornscsi_dma_stop (host); - acornscsi_dma_adjust (host); - acornscsi_readstatusbyte (host); + acornscsi_sbic_xfcount(host); + acornscsi_dma_stop(host); + acornscsi_dma_adjust(host); + acornscsi_readstatusbyte(host); host->scsi.phase = PHASE_STATUSIN; break; case 0x1e: /* -> PHASE_MSGOUT */ case 0x4e: /* -> PHASE_MSGOUT */ + case 0x8e: /* -> PHASE_MSGOUT */ /* DATA OUT -> MESSAGE OUT */ host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - - acornscsi_sbic_xfcount (host); - acornscsi_dma_stop (host); - acornscsi_dma_adjust (host); - acornscsi_sendmessage (host); + acornscsi_sbic_xfcount(host); + acornscsi_dma_stop(host); + acornscsi_dma_adjust(host); + acornscsi_sendmessage(host); break; case 0x1f: /* message in */ case 0x4f: /* message in */ + case 0x8f: /* message in */ /* DATA OUT -> MESSAGE IN */ host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - - acornscsi_sbic_xfcount (host); - acornscsi_dma_stop (host); - acornscsi_dma_adjust (host); - acornscsi_message (host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */ + acornscsi_sbic_xfcount(host); + acornscsi_dma_stop(host); + acornscsi_dma_adjust(host); + acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */ break; default: - printk (KERN_ERR "scsi%d.%c: PHASE_DATAOUT, SSR %02X?\n", - host->host->host_no, acornscsi_target (host), ssr); - acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8); + printk(KERN_ERR "scsi%d.%c: PHASE_DATAOUT, SSR %02X?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8); } return INTR_PROCESSING; case PHASE_STATUSIN: /* STATE: status in complete */ - if (ssr == 0x1f) /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */ + switch (ssr) { + case 0x1f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */ + case 0x8f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */ /* STATUS -> MESSAGE IN */ - acornscsi_message (host); - else if (ssr == 0x1e) /* -> PHASE_MSGOUT */ + acornscsi_message(host); + break; + + case 0x1e: /* -> PHASE_MSGOUT */ + case 0x8e: /* -> PHASE_MSGOUT */ /* STATUS -> MESSAGE OUT */ - acornscsi_sendmessage (host); - else { - printk (KERN_ERR "scsi%d.%c: PHASE_STATUSIN, SSR %02X instead of MESSAGE_IN?\n", - host->host->host_no, acornscsi_target (host), ssr); - acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8); + acornscsi_sendmessage(host); + break; + + default: + printk(KERN_ERR "scsi%d.%c: PHASE_STATUSIN, SSR %02X instead of MESSAGE_IN?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8); } return INTR_PROCESSING; @@ -2281,78 +2389,93 @@ intr_ret_t acornscsi_sbicintr (AS_Host *host, int in_irq) switch (ssr) { case 0x1e: /* -> PHASE_MSGOUT */ case 0x4e: /* -> PHASE_MSGOUT */ + case 0x8e: /* -> PHASE_MSGOUT */ /* MESSAGE IN -> MESSAGE OUT */ - acornscsi_sendmessage (host); + acornscsi_sendmessage(host); break; case 0x1f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */ case 0x2f: case 0x4f: case 0x8f: - acornscsi_message (host); + acornscsi_message(host); + break; + + case 0x85: + printk("scsi%d.%c: strange message in disconnection\n", + host->host->host_no, acornscsi_target(host)); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8); + acornscsi_done(host, &host->SCpnt, DID_ERROR); break; default: - printk (KERN_ERR "scsi%d.%c: PHASE_MSGIN, SSR %02X after message in?\n", - host->host->host_no, acornscsi_target (host), ssr); - acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8); + printk(KERN_ERR "scsi%d.%c: PHASE_MSGIN, SSR %02X after message in?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8); } return INTR_PROCESSING; case PHASE_DONE: /* STATE: received status & message */ switch (ssr) { case 0x85: /* -> PHASE_IDLE */ - acornscsi_done (host, &host->SCpnt, DID_OK); + acornscsi_done(host, &host->SCpnt, DID_OK); return INTR_NEXT_COMMAND; + case 0x1e: case 0x8e: - acornscsi_sendmessage (host); + acornscsi_sendmessage(host); break; default: - printk (KERN_ERR "scsi%d.%c: PHASE_DONE, SSR %02X instead of disconnect?\n", - host->host->host_no, acornscsi_target (host), ssr); - acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8); + printk(KERN_ERR "scsi%d.%c: PHASE_DONE, SSR %02X instead of disconnect?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8); } return INTR_PROCESSING; case PHASE_ABORTED: switch (ssr) { case 0x85: - acornscsi_done (host, &host->SCpnt, DID_ABORT); + if (host->SCpnt) + acornscsi_done(host, &host->SCpnt, DID_ABORT); + else { + clear_bit(host->scsi.reconnected.target * 8 + host->scsi.reconnected.lun, + host->busyluns); + host->scsi.phase = PHASE_IDLE; + } return INTR_NEXT_COMMAND; case 0x1e: case 0x2e: case 0x4e: case 0x8e: - acornscsi_sendmessage (host); + acornscsi_sendmessage(host); break; default: - printk (KERN_ERR "scsi%d.%c: PHASE_ABORTED, SSR %02X?\n", - host->host->host_no, acornscsi_target (host), ssr); - acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8); + printk(KERN_ERR "scsi%d.%c: PHASE_ABORTED, SSR %02X?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8); } return INTR_PROCESSING; default: - printk (KERN_ERR "scsi%d.%c: unknown driver phase %d\n", - host->host->host_no, acornscsi_target (host), ssr); - acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8); + printk(KERN_ERR "scsi%d.%c: unknown driver phase %d\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8); } return INTR_PROCESSING; } /* - * Prototype: void acornscsi_intr (int irq, void *dev_id, struct pt_regs *regs) + * Prototype: void acornscsi_intr(int irq, void *dev_id, struct pt_regs *regs) * Purpose : handle interrupts from Acorn SCSI card * Params : irq - interrupt number * dev_id - device specific data (AS_Host structure) * regs - processor registers when interrupt occurred */ static -void acornscsi_intr (int irq, void *dev_id, struct pt_regs *regs) +void acornscsi_intr(int irq, void *dev_id, struct pt_regs *regs) { AS_Host *host = (AS_Host *)dev_id; intr_ret_t ret; @@ -2360,21 +2483,21 @@ void acornscsi_intr (int irq, void *dev_id, struct pt_regs *regs) int in_irq = 0; if (host->scsi.interrupt) - printk ("scsi%d: interrupt re-entered\n", host->host->host_no); + printk("scsi%d: interrupt re-entered\n", host->host->host_no); host->scsi.interrupt = 1; do { ret = INTR_IDLE; - iostatus = inb (host->card.io_intr); + iostatus = inb(host->card.io_intr); if (iostatus & 2) { - acornscsi_dma_intr (host); - iostatus = inb (host->card.io_intr); + acornscsi_dma_intr(host); + iostatus = inb(host->card.io_intr); } if (iostatus & 8) - ret = acornscsi_sbicintr (host, in_irq); + ret = acornscsi_sbicintr(host, in_irq); /* * If we have a transfer pending, start it. @@ -2382,10 +2505,10 @@ void acornscsi_intr (int irq, void *dev_id, struct pt_regs *regs) * it's data */ if (host->dma.xfer_required) - acornscsi_dma_xfer (host); + acornscsi_dma_xfer(host); if (ret == INTR_NEXT_COMMAND) - ret = acornscsi_kick (host); + ret = acornscsi_kick(host); in_irq = 1; } while (ret != INTR_IDLE); @@ -2398,29 +2521,29 @@ void acornscsi_intr (int irq, void *dev_id, struct pt_regs *regs) */ /* - * Function : acornscsi_queuecmd (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) + * Function : acornscsi_queuecmd(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) * Purpose : queues a SCSI command * Params : cmd - SCSI command * done - function called on completion, with pointer to command descriptor * Returns : 0, or < 0 on error. */ -int acornscsi_queuecmd (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) +int acornscsi_queuecmd(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) { AS_Host *host = (AS_Host *)SCpnt->host->hostdata; if (!done) { /* there should be some way of rejecting errors like this without panicing... */ - panic ("scsi%d: queuecommand called with NULL done function [cmd=%p]", + panic("scsi%d: queuecommand called with NULL done function [cmd=%p]", SCpnt->host->host_no, SCpnt); return -EINVAL; } #if (DEBUG & DEBUG_NO_WRITE) - if (acornscsi_cmdtype (SCpnt->cmnd[0]) == CMD_WRITE && (NO_WRITE & (1 << SCpnt->target))) { - printk (KERN_CRIT "scsi%d.%c: WRITE attempted with NO_WRITE flag set\n", + if (acornscsi_cmdtype(SCpnt->cmnd[0]) == CMD_WRITE && (NO_WRITE & (1 << SCpnt->target))) { + printk(KERN_CRIT "scsi%d.%c: WRITE attempted with NO_WRITE flag set\n", SCpnt->host->host_no, '0' + SCpnt->target); SCpnt->result = DID_NO_CONNECT << 16; - done (SCpnt); + done(SCpnt); return 0; } #endif @@ -2429,7 +2552,7 @@ int acornscsi_queuecmd (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) SCpnt->host_scribble = NULL; SCpnt->result = 0; SCpnt->tag = 0; - SCpnt->SCp.phase = (int)acornscsi_datadirection (SCpnt->cmnd[0]); + SCpnt->SCp.phase = (int)acornscsi_datadirection(SCpnt->cmnd[0]); SCpnt->SCp.sent_command = 0; SCpnt->SCp.scsi_xferred = 0; SCpnt->SCp.Status = 0; @@ -2452,21 +2575,21 @@ int acornscsi_queuecmd (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) { unsigned long flags; - if (!queue_add_cmd_ordered (&host->queues.issue, SCpnt)) { + if (!queue_add_cmd_ordered(&host->queues.issue, SCpnt)) { SCpnt->result = DID_ERROR << 16; - done (SCpnt); + done(SCpnt); return 0; } - save_flags_cli (flags); + save_flags_cli(flags); if (host->scsi.phase == PHASE_IDLE) - acornscsi_kick (host); - restore_flags (flags); + acornscsi_kick(host); + restore_flags(flags); } return 0; } /* - * Prototype: void acornscsi_reportstatus (Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result) + * Prototype: void acornscsi_reportstatus(Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result) * Purpose : pass a result to *SCpntp1, and check if *SCpntp1 = *SCpntp2 * Params : SCpntp1 - pointer to command to return * SCpntp2 - pointer to command to check @@ -2474,7 +2597,7 @@ int acornscsi_queuecmd (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) * Returns : *SCpntp2 = NULL if *SCpntp1 is the same command structure as *SCpntp2. */ static inline -void acornscsi_reportstatus (Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result) +void acornscsi_reportstatus(Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result) { Scsi_Cmnd *SCpnt = *SCpntp1; @@ -2482,80 +2605,203 @@ void acornscsi_reportstatus (Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int resul *SCpntp1 = NULL; SCpnt->result = result; - SCpnt->scsi_done (SCpnt); + SCpnt->scsi_done(SCpnt); } if (SCpnt == *SCpntp2) *SCpntp2 = NULL; } +enum res_abort { res_not_running, res_success, res_success_clear, res_snooze }; + /* - * Prototype: int acornscsi_abort (Scsi_Cmnd *SCpnt) + * Prototype: enum res acornscsi_do_abort(Scsi_Cmnd *SCpnt) * Purpose : abort a command on this host * Params : SCpnt - command to abort - * Returns : one of SCSI_ABORT_ macros + * Returns : our abort status */ -int acornscsi_abort (Scsi_Cmnd *SCpnt) +static enum res_abort +acornscsi_do_abort(AS_Host *host, Scsi_Cmnd *SCpnt) { - AS_Host *host = (AS_Host *) SCpnt->host->hostdata; - int result = SCSI_ABORT_NOT_RUNNING; + enum res_abort res = res_not_running; - host->stats.aborts += 1; + if (queue_removecmd(&host->queues.issue, SCpnt)) { + /* + * The command was on the issue queue, and has not been + * issued yet. We can remove the command from the queue, + * and acknowledge the abort. Neither the devices nor the + * interface know about the command. + */ +//#if (DEBUG & DEBUG_ABORT) + printk("on issue queue "); +//#endif + res = res_success; + } else if (queue_removecmd(&host->queues.disconnected, SCpnt)) { + /* + * The command was on the disconnected queue. Simply + * acknowledge the abort condition, and when the target + * reconnects, we will give it an ABORT message. The + * target should then disconnect, and we will clear + * the busylun bit. + */ +//#if (DEBUG & DEBUG_ABORT) + printk("on disconnected queue "); +//#endif + res = res_success; + } else if (host->SCpnt == SCpnt) { + unsigned long flags; + +//#if (DEBUG & DEBUG_ABORT) + printk("executing "); +//#endif + + save_flags(flags); + cli(); + switch (host->scsi.phase) { + /* + * If the interface is idle, and the command is 'disconnectable', + * then it is the same as on the disconnected queue. We simply + * remove all traces of the command. When the target reconnects, + * we will give it an ABORT message since the command could not + * be found. When the target finally disconnects, we will clear + * the busylun bit. + */ + case PHASE_IDLE: + if (host->scsi.disconnectable) { + host->scsi.disconnectable = 0; + host->SCpnt = NULL; + res = res_success; + } + break; -#if (DEBUG & DEBUG_ABORT) - { - int asr, ssr; - asr = sbic_arm_read (host->scsi.io_port, ASR); - ssr = sbic_arm_read (host->scsi.io_port, SSR); + /* + * If the command has connected and done nothing further, + * simply force a disconnect. We also need to clear the + * busylun bit. + */ + case PHASE_CONNECTED: + sbic_arm_write(host->scsi.io_port, CMND, CMND_DISCONNECT); + host->SCpnt = NULL; + res = res_success_clear; + break; + + default: + acornscsi_abortcmd(host, host->SCpnt->tag); + res = res_snooze; + } + restore_flags(flags); + } else if (host->origSCpnt == SCpnt) { + /* + * The command will be executed next, but a command + * is currently using the interface. This is similar to + * being on the issue queue, except the busylun bit has + * been set. + */ + host->origSCpnt = NULL; +//#if (DEBUG & DEBUG_ABORT) + printk("waiting for execution "); +//#endif + res = res_success_clear; + } else + printk("unknown "); - printk (KERN_WARNING "acornscsi_abort: "); - print_sbic_status(asr, ssr, host->scsi.phase); - acornscsi_dumplog (host, SCpnt->target); - } -#endif + return res; +} + +/* + * Prototype: int acornscsi_abort(Scsi_Cmnd *SCpnt) + * Purpose : abort a command on this host + * Params : SCpnt - command to abort + * Returns : one of SCSI_ABORT_ macros + */ +int acornscsi_abort(Scsi_Cmnd *SCpnt) +{ + AS_Host *host = (AS_Host *) SCpnt->host->hostdata; + int result; + + host->stats.aborts += 1; - if (queue_removecmd (&host->queues.issue, SCpnt)) { - SCpnt->result = DID_ABORT << 16; - SCpnt->scsi_done (SCpnt); -#if (DEBUG & DEBUG_ABORT) - printk ("scsi%d: command on issue queue\n", host->host->host_no); -#endif - result = SCSI_ABORT_SUCCESS; - } else if (queue_cmdonqueue (&host->queues.disconnected, SCpnt)) { - printk ("scsi%d: command on disconnected queue\n", host->host->host_no); - result = SCSI_ABORT_SNOOZE; - } else if (host->SCpnt == SCpnt) { - acornscsi_abortcmd (host, host->SCpnt->tag); - printk ("scsi%d: command executing\n", host->host->host_no); - result = SCSI_ABORT_SNOOZE; - } else if (host->origSCpnt == SCpnt) { - host->origSCpnt = NULL; - SCpnt->result = DID_ABORT << 16; - SCpnt->scsi_done (SCpnt); #if (DEBUG & DEBUG_ABORT) - printk ("scsi%d: command waiting for execution\n", host->host->host_no); + { + int asr, ssr; + asr = sbic_arm_read(host->scsi.io_port, ASR); + ssr = sbic_arm_read(host->scsi.io_port, SSR); + + printk(KERN_WARNING "acornscsi_abort: "); + print_sbic_status(asr, ssr, host->scsi.phase); + acornscsi_dumplog(host, SCpnt->target); + } #endif - result = SCSI_ABORT_SUCCESS; - } - if (result == SCSI_ABORT_NOT_RUNNING) { - printk ("scsi%d: abort(): command not running\n", host->host->host_no); - acornscsi_dumplog (host, SCpnt->target); + printk("scsi%d: ", host->host->host_no); + + switch (acornscsi_do_abort(host, SCpnt)) { + /* + * We managed to find the command and cleared it out. + * We do not expect the command to be executing on the + * target, but we have set the busylun bit. + */ + case res_success_clear: +//#if (DEBUG & DEBUG_ABORT) + printk("clear "); +//#endif + clear_bit(SCpnt->target * 8 + SCpnt->lun, host->busyluns); + + /* + * We found the command, and cleared it out. Either + * the command is still known to be executing on the + * target, or the busylun bit is not set. + */ + case res_success: +//#if (DEBUG & DEBUG_ABORT) + printk("success\n"); +//#endif + SCpnt->result = DID_ABORT << 16; + SCpnt->scsi_done(SCpnt); + result = SCSI_ABORT_SUCCESS; + break; + + /* + * We did find the command, but unfortunately we couldn't + * unhook it from ourselves. Wait some more, and if it + * still doesn't complete, reset the interface. + */ + case res_snooze: +//#if (DEBUG & DEBUG_ABORT) + printk("snooze\n"); +//#endif + result = SCSI_ABORT_SNOOZE; + break; + + /* + * The command could not be found (either because it completed, + * or it got dropped. + */ + default: + case res_not_running: + acornscsi_dumplog(host, SCpnt->target); #if (DEBUG & DEBUG_ABORT) - result = SCSI_ABORT_SNOOZE; + result = SCSI_ABORT_SNOOZE; +#else + result = SCSI_ABORT_NOT_RUNNING; #endif - } - return result; +//#if (DEBUG & DEBUG_ABORT) + printk("not running\n"); +//#endif + break; + } + + return result; } /* - * Prototype: int acornscsi_reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags) + * Prototype: int acornscsi_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags) * Purpose : reset a command on this host/reset this host * Params : SCpnt - command causing reset * result - what type of reset to perform * Returns : one of SCSI_RESET_ macros */ -int acornscsi_reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags) +int acornscsi_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags) { AS_Host *host = (AS_Host *)SCpnt->host->hostdata; Scsi_Cmnd *SCptr; @@ -2566,16 +2812,16 @@ int acornscsi_reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags) { int asr, ssr; - asr = sbic_arm_read (host->scsi.io_port, ASR); - ssr = sbic_arm_read (host->scsi.io_port, SSR); + asr = sbic_arm_read(host->scsi.io_port, ASR); + ssr = sbic_arm_read(host->scsi.io_port, SSR); - printk (KERN_WARNING "acornscsi_reset: "); + printk(KERN_WARNING "acornscsi_reset: "); print_sbic_status(asr, ssr, host->scsi.phase); - acornscsi_dumplog (host, SCpnt->target); + acornscsi_dumplog(host, SCpnt->target); } #endif - acornscsi_dma_stop (host); + acornscsi_dma_stop(host); SCptr = host->SCpnt; @@ -2583,19 +2829,19 @@ int acornscsi_reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags) * do hard reset. This resets all devices on this host, and so we * must set the reset status on all commands. */ - acornscsi_resetcard (host); + acornscsi_resetcard(host); /* * report reset on commands current connected/disconnected */ - acornscsi_reportstatus (&host->SCpnt, &SCptr, DID_RESET); + acornscsi_reportstatus(&host->SCpnt, &SCptr, DID_RESET); - while ((SCptr = queue_remove (&host->queues.disconnected)) != NULL) - acornscsi_reportstatus (&SCptr, &SCpnt, DID_RESET); + while ((SCptr = queue_remove(&host->queues.disconnected)) != NULL) + acornscsi_reportstatus(&SCptr, &SCpnt, DID_RESET); if (SCpnt) { SCpnt->result = DID_RESET << 16; - SCpnt->scsi_done (SCpnt); + SCpnt->scsi_done(SCpnt); } return SCSI_RESET_BUS_RESET | SCSI_RESET_HOST_RESET | SCSI_RESET_SUCCESS; @@ -2607,19 +2853,19 @@ int acornscsi_reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags) static struct expansion_card *ecs[MAX_ECARDS]; /* - * Prototype: void acornscsi_init (AS_Host *host) + * Prototype: void acornscsi_init(AS_Host *host) * Purpose : initialise the AS_Host structure for one interface & setup hardware * Params : host - host to setup */ static -void acornscsi_init (AS_Host *host) +void acornscsi_init(AS_Host *host) { - memset (&host->stats, 0, sizeof (host->stats)); - queue_initialise (&host->queues.issue); - queue_initialise (&host->queues.disconnected); - msgqueue_initialise (&host->scsi.msgs); + memset(&host->stats, 0, sizeof (host->stats)); + queue_initialise(&host->queues.issue); + queue_initialise(&host->queues.disconnected); + msgqueue_initialise(&host->scsi.msgs); - acornscsi_resetcard (host); + acornscsi_resetcard(host); } int acornscsi_detect(Scsi_Host_Template * tpnt) @@ -2634,7 +2880,7 @@ int acornscsi_detect(Scsi_Host_Template * tpnt) for (i = 0; i < MAX_ECARDS; i++) ecs[i] = NULL; - ecard_startfind (); + ecard_startfind(); while(1) { ecs[count] = ecard_find(0, acornscsi_cids); @@ -2642,37 +2888,37 @@ int acornscsi_detect(Scsi_Host_Template * tpnt) break; if (ecs[count]->irq == 0xff) { - printk ("scsi: WD33C93 does not have IRQ enabled - ignoring\n"); + printk("scsi: WD33C93 does not have IRQ enabled - ignoring\n"); continue; } ecard_claim(ecs[count]); /* Must claim here - card produces irq on reset */ - instance = scsi_register (tpnt, sizeof(AS_Host)); + instance = scsi_register(tpnt, sizeof(AS_Host)); host = (AS_Host *)instance->hostdata; - instance->io_port = ecard_address (ecs[count], ECARD_MEMC, 0); + instance->io_port = ecard_address(ecs[count], ECARD_MEMC, 0); instance->irq = ecs[count]->irq; host->host = instance; - host->scsi.io_port = ioaddr (instance->io_port + 0x800); + host->scsi.io_port = ioaddr(instance->io_port + 0x800); host->scsi.irq = instance->irq; host->card.io_intr = POD_SPACE(instance->io_port) + 0x800; host->card.io_page = POD_SPACE(instance->io_port) + 0xc00; - host->card.io_ram = ioaddr (instance->io_port); + host->card.io_ram = ioaddr(instance->io_port); host->dma.io_port = instance->io_port + 0xc00; host->dma.io_intr_clear = POD_SPACE(instance->io_port) + 0x800; ecs[count]->irqaddr = (char *)ioaddr(host->card.io_intr); ecs[count]->irqmask = 0x0a; - request_region (instance->io_port + 0x800, 2, "acornscsi(sbic)"); - request_region (host->card.io_intr, 1, "acornscsi(intr)"); - request_region (host->card.io_page, 1, "acornscsi(page)"); + request_region(instance->io_port + 0x800, 2, "acornscsi(sbic)"); + request_region(host->card.io_intr, 1, "acornscsi(intr)"); + request_region(host->card.io_page, 1, "acornscsi(page)"); #ifdef USE_DMAC - request_region (host->dma.io_port, 256, "acornscsi(dmac)"); + request_region(host->dma.io_port, 256, "acornscsi(dmac)"); #endif - request_region (instance->io_port, 2048, "acornscsi(ram)"); + request_region(instance->io_port, 2048, "acornscsi(ram)"); if (request_irq(host->scsi.irq, acornscsi_intr, SA_INTERRUPT, "acornscsi", host)) { printk(KERN_CRIT "scsi%d: IRQ%d not free, interrupts disabled\n", @@ -2680,7 +2926,7 @@ int acornscsi_detect(Scsi_Host_Template * tpnt) host->scsi.irq = NO_IRQ; } - acornscsi_init (host); + acornscsi_init(host); ++count; } @@ -2688,12 +2934,12 @@ int acornscsi_detect(Scsi_Host_Template * tpnt) } /* - * Function: int acornscsi_release (struct Scsi_Host *host) + * Function: int acornscsi_release(struct Scsi_Host *host) * Purpose : release all resources used by this adapter * Params : host - driver structure to release * Returns : nothing of any consequence */ -int acornscsi_release (struct Scsi_Host *instance) +int acornscsi_release(struct Scsi_Host *instance) { AS_Host *host = (AS_Host *)instance->hostdata; int i; @@ -2701,30 +2947,30 @@ int acornscsi_release (struct Scsi_Host *instance) /* * Put card into RESET state */ - outb (0x80, host->card.io_page); + outb(0x80, host->card.io_page); if (host->scsi.irq != NO_IRQ) - free_irq (host->scsi.irq, host); + free_irq(host->scsi.irq, host); - release_region (instance->io_port + 0x800, 2); - release_region (host->card.io_intr, 1); - release_region (host->card.io_page, 1); - release_region (host->dma.io_port, 256); - release_region (instance->io_port, 2048); + release_region(instance->io_port + 0x800, 2); + release_region(host->card.io_intr, 1); + release_region(host->card.io_page, 1); + release_region(host->dma.io_port, 256); + release_region(instance->io_port, 2048); for (i = 0; i < MAX_ECARDS; i++) - if (ecs[i] && instance->io_port == ecard_address (ecs[i], ECARD_MEMC, 0)) - ecard_release (ecs[i]); + if (ecs[i] && instance->io_port == ecard_address(ecs[i], ECARD_MEMC, 0)) + ecard_release(ecs[i]); - msgqueue_free (&host->scsi.msgs); - queue_free (&host->queues.disconnected); - queue_free (&host->queues.issue); + msgqueue_free(&host->scsi.msgs); + queue_free(&host->queues.disconnected); + queue_free(&host->queues.issue); return 0; } /* - * Function: char *acornscsi_info (struct Scsi_Host *host) + * Function: char *acornscsi_info(struct Scsi_Host *host) * Purpose : return a string describing this interface * Params : host - host to give information on * Returns : a constant string @@ -2736,7 +2982,7 @@ char *acornscsi_info(struct Scsi_Host *host) p = string; - p += sprintf (string, "%s at port %lX irq %d v%d.%d.%d" + p += sprintf(string, "%s at port %X irq %d v%d.%d.%d" #ifdef CONFIG_SCSI_ACORNSCSI_SYNC " SYNC" #endif @@ -2772,7 +3018,7 @@ int acornscsi_proc_info(char *buffer, char **start, off_t offset, host = (AS_Host *)instance->hostdata; - p += sprintf (p, "AcornSCSI driver v%d.%d.%d" + p += sprintf(p, "AcornSCSI driver v%d.%d.%d" #ifdef CONFIG_SCSI_ACORNSCSI_SYNC " SYNC" #endif @@ -2787,14 +3033,14 @@ int acornscsi_proc_info(char *buffer, char **start, off_t offset, #endif "\n\n", VER_MAJOR, VER_MINOR, VER_PATCH); - p += sprintf (p, "SBIC: WD33C93A Address: %08X IRQ : %d\n", + p += sprintf(p, "SBIC: WD33C93A Address: %08X IRQ : %d\n", host->scsi.io_port, host->scsi.irq); #ifdef USE_DMAC - p += sprintf (p, "DMAC: uPC71071 Address: %08X IRQ : %d\n\n", + p += sprintf(p, "DMAC: uPC71071 Address: %08X IRQ : %d\n\n", host->dma.io_port, host->scsi.irq); #endif - p += sprintf (p, "Statistics:\n" + p += sprintf(p, "Statistics:\n" "Queued commands: %-10u Issued commands: %-10u\n" "Done commands : %-10u Reads : %-10u\n" "Writes : %-10u Others : %-10u\n" @@ -2809,47 +3055,47 @@ int acornscsi_proc_info(char *buffer, char **start, off_t offset, for (devidx = 0; devidx < 9; devidx ++) { unsigned int statptr, prev; - p += sprintf (p, "\n%c:", devidx == 8 ? 'H' : ('0' + devidx)); - statptr = status_ptr[devidx] - 10; + p += sprintf(p, "\n%c:", devidx == 8 ? 'H' : ('0' + devidx)); + statptr = host->status_ptr[devidx] - 10; if ((signed int)statptr < 0) - statptr += 16; - - prev = status[devidx][statptr].when; - - for (; statptr != status_ptr[devidx]; statptr = (statptr + 1) & 15) { - if (status[devidx][statptr].when) { - p += sprintf (p, "%c%02X:%02X+%2ld", - status[devidx][statptr].irq ? '-' : ' ', - status[devidx][statptr].ph, - status[devidx][statptr].ssr, - (status[devidx][statptr].when - prev) < 100 ? - (status[devidx][statptr].when - prev) : 99); - prev = status[devidx][statptr].when; + statptr += STATUS_BUFFER_SIZE; + + prev = host->status[devidx][statptr].when; + + for (; statptr != host->status_ptr[devidx]; statptr = (statptr + 1) & (STATUS_BUFFER_SIZE - 1)) { + if (host->status[devidx][statptr].when) { + p += sprintf(p, "%c%02X:%02X+%2ld", + host->status[devidx][statptr].irq ? '-' : ' ', + host->status[devidx][statptr].ph, + host->status[devidx][statptr].ssr, + (host->status[devidx][statptr].when - prev) < 100 ? + (host->status[devidx][statptr].when - prev) : 99); + prev = host->status[devidx][statptr].when; } } } - p += sprintf (p, "\nAttached devices:%s\n", instance->host_queue ? "" : " none"); + p += sprintf(p, "\nAttached devices:%s\n", instance->host_queue ? "" : " none"); for (scd = instance->host_queue; scd; scd = scd->next) { int len; - proc_print_scsidevice (scd, p, &len, 0); + proc_print_scsidevice(scd, p, &len, 0); p += len; - p += sprintf (p, "Extensions: "); + p += sprintf(p, "Extensions: "); if (scd->tagged_supported) - p += sprintf (p, "TAG %sabled [%d] ", + p += sprintf(p, "TAG %sabled [%d] ", scd->tagged_queue ? "en" : "dis", scd->current_tag); - p += sprintf (p, "\nTransfers: "); + p += sprintf(p, "\nTransfers: "); if (host->device[scd->id].sync_xfer & 15) - p += sprintf (p, "sync, offset %d, %d ns\n", + p += sprintf(p, "sync, offset %d, %d ns\n", host->device[scd->id].sync_xfer & 15, - acornscsi_getperiod (host->device[scd->id].sync_xfer)); + acornscsi_getperiod(host->device[scd->id].sync_xfer)); else - p += sprintf (p, "async\n"); + p += sprintf(p, "async\n"); pos = p - buffer; if (pos + begin < offset) { diff --git a/drivers/acorn/scsi/acornscsi.h b/drivers/acorn/scsi/acornscsi.h index ffaba7c2c..5470c6b6c 100644 --- a/drivers/acorn/scsi/acornscsi.h +++ b/drivers/acorn/scsi/acornscsi.h @@ -291,6 +291,27 @@ typedef enum { /* Data direction */ #include "queue.h" #include "msgqueue.h" +#define STATUS_BUFFER_SIZE 32 +/* + * This is used to dump the previous states of the SBIC + */ +struct status_entry { + unsigned long when; + unsigned char ssr; + unsigned char ph; + unsigned char irq; + unsigned char unused; +}; + +#define ADD_STATUS(_q,_ssr,_ph,_irq) \ +({ \ + host->status[(_q)][host->status_ptr[(_q)]].when = jiffies; \ + host->status[(_q)][host->status_ptr[(_q)]].ssr = (_ssr); \ + host->status[(_q)][host->status_ptr[(_q)]].ph = (_ph); \ + host->status[(_q)][host->status_ptr[(_q)]].irq = (_irq); \ + host->status_ptr[(_q)] = (host->status_ptr[(_q)] + 1) & (STATUS_BUFFER_SIZE - 1); \ +}) + /* * AcornSCSI host specific data */ @@ -303,7 +324,7 @@ typedef struct acornscsi_hostdata { /* driver information */ struct { unsigned int io_port; /* base address of WD33C93 */ - unsigned char irq; /* interrupt */ + unsigned int irq; /* interrupt */ phase_t phase; /* current phase */ struct { @@ -361,6 +382,7 @@ typedef struct acornscsi_hostdata { char *xfer_ptr; /* pointer to area */ unsigned char xfer_required:1; /* set if we need to transfer something */ unsigned char xfer_setup:1; /* set if DMA is setup */ + unsigned char xfer_done:1; /* set if DMA reached end of BH list */ } dma; /* card info */ @@ -370,6 +392,9 @@ typedef struct acornscsi_hostdata { unsigned int io_ram; /* base address of RAM access */ unsigned char page_reg; /* current setting of page reg */ } card; + + unsigned char status_ptr[9]; + struct status_entry status[9][STATUS_BUFFER_SIZE]; } AS_Host; #endif /* ndef HOSTS_C */ diff --git a/drivers/acorn/scsi/arxescsi.c b/drivers/acorn/scsi/arxescsi.c new file mode 100644 index 000000000..4afebe359 --- /dev/null +++ b/drivers/acorn/scsi/arxescsi.c @@ -0,0 +1,395 @@ +/* + * linux/arch/arm/drivers/scsi/cumana_2.c + * + * Copyright (C) 1997,1998 Russell King + * + * This driver is based on experimentation. Hence, it may have made + * assumptions about the particular card that I have available, and + * may not be reliable! + * + * Changelog: + * 30-08-1997 RMK 0.0.0 Created, READONLY version as cumana_2.c + * 22-01-1998 RMK 0.0.1 Updated to 2.1.80 + * 15-04-1998 RMK 0.0.1 Only do PIO if FAS216 will allow it. + * 11-06-1998 0.0.2 Changed to support ARXE 16-bit SCSI card, enabled writing + * by Stefan Hanske + */ + +#include <linux/module.h> +#include <linux/blk.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/ioport.h> +#include <linux/sched.h> +#include <linux/proc_fs.h> +#include <linux/unistd.h> +#include <linux/stat.h> + +#include <asm/delay.h> +#include <asm/dma.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/ecard.h> + +#include "../../scsi/sd.h" +#include "../../scsi/hosts.h" +#include "arxescsi.h" +#include "fas216.h" + +/* Hmm - this should go somewhere else */ +#define BUS_ADDR(x) ((((unsigned long)(x)) << 2) + IO_BASE) + +/* Configuration */ +#define ARXESCSI_XTALFREQ 24 +#define ARXESCSI_ASYNC_PERIOD 200 +#define ARXESCSI_SYNC_DEPTH 0 + +/* + * List of devices that the driver will recognise + */ +#define ARXESCSI_LIST { MANU_ARXE, PROD_ARXE_SCSI } + +/* + * Version + */ +#define VER_MAJOR 0 +#define VER_MINOR 0 +#define VER_PATCH 2 + +static struct expansion_card *ecs[MAX_ECARDS]; + +static struct proc_dir_entry proc_scsi_arxescsi = { + PROC_SCSI_QLOGICFAS, 6, "arxescsi", + S_IFDIR | S_IRUGO | S_IXUGO, 2 +}; + +/* + * Function: int arxescsi_dma_setup(host, SCpnt, direction, min_type) + * Purpose : initialises DMA/PIO + * Params : host - host + * SCpnt - command + * direction - DMA on to/off of card + * min_type - minimum DMA support that we must have for this transfer + * Returns : 0 if we should not set CMD_WITHDMA for transfer info command + */ +static fasdmatype_t +arxescsi_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp, + fasdmadir_t direction, fasdmatype_t min_type) +{ + /* + * We don't do real DMA + */ + return fasdma_pseudo; +} + + + +/* Faster transfer routines, written by SH to speed up the loops */ + +static __inline__ unsigned char getb(unsigned int address, unsigned int reg) +{ + unsigned char value; + + __asm__ __volatile__( + "ldrb %0, [%1, %2, lsl #5]" + : "=r" (value) + : "r" (address), "r" (reg) ); + return value; +} + +static __inline__ unsigned int getw(unsigned int address, unsigned int reg) +{ + unsigned int value; + + __asm__ __volatile__( + "ldr %0, [%1, %2, lsl #5]\n\t" + "mov %0, %0, lsl #16\n\t" + "mov %0, %0, lsr #16" + : "=r" (value) + : "r" (address), "r" (reg) ); + return value; +} + +static __inline__ void putw(unsigned int address, unsigned int reg, unsigned long value) +{ + __asm__ __volatile__( + "mov %0, %0, lsl #16\n\t" + "str %0, [%1, %2, lsl #5]" + : + : "r" (value), "r" (address), "r" (reg) ); +} + + +/* + * Function: int arxescsi_dma_pseudo(host, SCpnt, direction, transfer) + * Purpose : handles pseudo DMA + * Params : host - host + * SCpnt - command + * direction - DMA on to/off of card + * transfer - minimum number of bytes we expect to transfer + */ +void arxescsi_dma_pseudo(struct Scsi_Host *host, Scsi_Pointer *SCp, + fasdmadir_t direction, int transfer) +{ + ARXEScsi_Info *info = (ARXEScsi_Info *)host->hostdata; + unsigned int length, io, error=0; + unsigned char *addr; + + length = SCp->this_residual; + addr = SCp->ptr; + io = __ioaddr(host->io_port); + + if (direction == DMA_OUT) { + while (length > 0) { + unsigned long word; + + + word = *addr | *(addr + 1) << 8; + if (getb(io, 4) & STAT_INT) + break; + + if (!(getb(io, 48) & CSTATUS_IRQ)) + continue; + + putw(io, 16, word); + if (length > 1) { + addr += 2; + length -= 2; + } else { + addr += 1; + length -= 1; + } + } + } + else { + if (transfer && (transfer & 255)) { + while (length >= 256) { + if (getb(io, 4) & STAT_INT) { + error=1; + break; + } + + if (!(getb(io, 48) & CSTATUS_IRQ)) + continue; + + insw(info->dmaarea, addr, 256 >> 1); + addr += 256; + length -= 256; + } + } + + if (!(error)) + while (length > 0) { + unsigned long word; + + if (getb(io, 4) & STAT_INT) + break; + + if (!(getb(io, 48) & CSTATUS_IRQ)) + continue; + + word = getw(io, 16); + *addr++ = word; + if (--length > 0) { + *addr++ = word >> 8; + length --; + } + } + } +} + +/* + * Function: int arxescsi_dma_stop(host, SCpnt) + * Purpose : stops DMA/PIO + * Params : host - host + * SCpnt - command + */ +static void arxescsi_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp) +{ + /* + * no DMA to stop + */ +} + +/* + * Function: int arxescsi_detect(Scsi_Host_Template * tpnt) + * Purpose : initialises ARXE SCSI driver + * Params : tpnt - template for this SCSI adapter + * Returns : >0 if host found, 0 otherwise. + */ +int arxescsi_detect(Scsi_Host_Template *tpnt) +{ + static const card_ids arxescsi_cids[] = { ARXESCSI_LIST, { 0xffff, 0xffff} }; + int count = 0; + struct Scsi_Host *host; + + tpnt->proc_dir = &proc_scsi_arxescsi; + memset(ecs, 0, sizeof (ecs)); + + ecard_startfind(); + + while (1) { + ARXEScsi_Info *info; + + ecs[count] = ecard_find(0, arxescsi_cids); + if (!ecs[count]) + break; + + ecard_claim(ecs[count]); + + host = scsi_register(tpnt, sizeof (ARXEScsi_Info)); + if (!host) { + ecard_release(ecs[count]); + break; + } + + host->io_port = ecard_address(ecs[count], ECARD_MEMC, 0) + 0x0800; + host->irq = NO_IRQ; + host->dma_channel = NO_DMA; + host->can_queue = 0; /* no command queueing */ + info = (ARXEScsi_Info *)host->hostdata; + + info->info.scsi.io_port = host->io_port; + info->info.scsi.irq = host->irq; + info->info.scsi.io_shift = 3; + info->info.ifcfg.clockrate = ARXESCSI_XTALFREQ; + info->info.ifcfg.select_timeout = 255; + info->info.ifcfg.asyncperiod = ARXESCSI_ASYNC_PERIOD; + info->info.ifcfg.sync_max_depth = ARXESCSI_SYNC_DEPTH; + info->info.ifcfg.cntl3 = CNTL3_FASTSCSI | CNTL3_FASTCLK; + info->info.ifcfg.disconnect_ok = 0; + info->info.ifcfg.wide_max_size = 0; + info->info.dma.setup = arxescsi_dma_setup; + info->info.dma.pseudo = arxescsi_dma_pseudo; + info->info.dma.stop = arxescsi_dma_stop; + info->dmaarea = host->io_port + 128; + info->cstatus = host->io_port + 384; + + ecs[count]->irqaddr = (unsigned char *)BUS_ADDR(host->io_port); + ecs[count]->irqmask = CSTATUS_IRQ; + + request_region(host->io_port , 120, "arxescsi-fas"); + request_region(host->io_port + 128, 384, "arxescsi-dma"); + + printk("scsi%d: Has no interrupts - using polling mode\n", + host->host_no); + + fas216_init(host); + ++count; + } + return count; +} + +/* + * Function: int arxescsi_release(struct Scsi_Host * host) + * Purpose : releases all resources used by this adapter + * Params : host - driver host structure to return info for. + * Returns : nothing + */ +int arxescsi_release(struct Scsi_Host *host) +{ + int i; + + fas216_release(host); + + release_region(host->io_port, 120); + release_region(host->io_port + 128, 384); + + for (i = 0; i < MAX_ECARDS; i++) + if (ecs[i] && host->io_port == (ecard_address(ecs[i], ECARD_MEMC, 0) + 0x0800)) + ecard_release(ecs[i]); + return 0; +} + +/* + * Function: const char *arxescsi_info(struct Scsi_Host * host) + * Purpose : returns a descriptive string about this interface, + * Params : host - driver host structure to return info for. + * Returns : pointer to a static buffer containing null terminated string. + */ +const char *arxescsi_info(struct Scsi_Host *host) +{ + ARXEScsi_Info *info = (ARXEScsi_Info *)host->hostdata; + static char string[100], *p; + + p = string; + p += sprintf(string, "%s at port %lX irq %d v%d.%d.%d scsi %s", + host->hostt->name, host->io_port, host->irq, + VER_MAJOR, VER_MINOR, VER_PATCH, + info->info.scsi.type); + + return string; +} + +/* + * Function: int arxescsi_proc_info(char *buffer, char **start, off_t offset, + * int length, int host_no, int inout) + * Purpose : Return information about the driver to a user process accessing + * the /proc filesystem. + * Params : buffer - a buffer to write information to + * start - a pointer into this buffer set by this routine to the start + * of the required information. + * offset - offset into information that we have read upto. + * length - length of buffer + * host_no - host number to return information for + * inout - 0 for reading, 1 for writing. + * Returns : length of data written to buffer. + */ +int arxescsi_proc_info(char *buffer, char **start, off_t offset, + int length, int host_no, int inout) +{ + int pos, begin; + struct Scsi_Host *host = scsi_hostlist; + ARXEScsi_Info *info; + Scsi_Device *scd; + + while (host) { + if (host->host_no == host_no) + break; + host = host->next; + } + if (!host) + return 0; + + info = (ARXEScsi_Info *)host->hostdata; + if (inout == 1) + return -EINVAL; + + begin = 0; + pos = sprintf(buffer, + "ARXE 16-bit SCSI driver version %d.%d.%d\n", + VER_MAJOR, VER_MINOR, VER_PATCH); + pos += sprintf(buffer + pos, + "Address: %08lX IRQ : %d\n" + "FAS : %s\n\n" + "Statistics:\n", + host->io_port, host->irq, info->info.scsi.type); + + pos += fas216_print_stats(&info->info, buffer + pos); + + pos += sprintf (buffer+pos, "\nAttached devices:\n"); + + for (scd = host->host_queue; scd; scd = scd->next) { + pos += fas216_print_device(&info->info, scd, buffer + pos); + + if (pos + begin < offset) { + begin += pos; + pos = 0; + } + if (pos + begin > offset + length) + break; + } + + *start = buffer + (offset - begin); + pos -= offset - begin; + if (pos > length) + pos = length; + + return pos; +} + +#ifdef MODULE +Scsi_Host_Template driver_template = ARXEScsi; + +#include "../../scsi/scsi_module.c" +#endif diff --git a/drivers/acorn/scsi/arxescsi.h b/drivers/acorn/scsi/arxescsi.h new file mode 100644 index 000000000..11dc6b6e3 --- /dev/null +++ b/drivers/acorn/scsi/arxescsi.h @@ -0,0 +1,80 @@ +/* + * ARXE SCSI card driver + * + * Copyright (C) 1997 Russell King + * Changes to support ARXE 16-bit SCSI card by Stefan Hanske + */ +#ifndef ARXE_SCSI_H +#define ARXE_SCSI_H + +#define MANU_ARXE 0x0041 +#define PROD_ARXE_SCSI 0x00be + +extern int arxescsi_detect (Scsi_Host_Template *); +extern int arxescsi_release (struct Scsi_Host *); +extern const char *arxescsi_info (struct Scsi_Host *); +extern int arxescsi_proc_info (char *buffer, char **start, off_t offset, + int length, int hostno, int inout); + +#ifndef NULL +#define NULL ((void *)0) +#endif + +#ifndef CAN_QUEUE +/* + * Default queue size + */ +#define CAN_QUEUE 1 +#endif + +#ifndef CMD_PER_LUN +#define CMD_PER_LUN 1 +#endif + +#ifndef SCSI_ID +/* + * Default SCSI host ID + */ +#define SCSI_ID 7 +#endif + +#include <scsi/scsicam.h> + +#ifndef HOSTS_C +#include "fas216.h" +#endif + +#define ARXEScsi { \ +proc_info: arxescsi_proc_info, \ +name: "ARXE SCSI card", \ +detect: arxescsi_detect, /* detect */ \ +release: arxescsi_release, /* release */ \ +info: arxescsi_info, /* info */ \ +command: fas216_command, /* command */ \ +queuecommand: fas216_queue_command, /* queuecommand */ \ +abort: fas216_abort, /* abort */ \ +reset: fas216_reset, /* reset */ \ +bios_param: scsicam_bios_param, /* biosparam */ \ +can_queue: CAN_QUEUE, /* can queue */ \ +this_id: SCSI_ID, /* scsi host id */ \ +sg_tablesize: SG_ALL, /* sg_tablesize */ \ +cmd_per_lun: CMD_PER_LUN, /* cmd per lun */ \ +use_clustering: DISABLE_CLUSTERING \ + } + +#ifndef HOSTS_C + +typedef struct { + FAS216_Info info; + + /* other info... */ + unsigned int cstatus; /* card status register */ + unsigned int dmaarea; /* Pseudo DMA area */ +} ARXEScsi_Info; + +#define CSTATUS_IRQ (1 << 0) +#define CSTATUS_DRQ (1 << 0) + +#endif /* HOSTS_C */ + +#endif /* ARXE_SCSI_H */ diff --git a/drivers/acorn/scsi/cumana_2.c b/drivers/acorn/scsi/cumana_2.c index 797eaae06..c0edf3b64 100644 --- a/drivers/acorn/scsi/cumana_2.c +++ b/drivers/acorn/scsi/cumana_2.c @@ -4,12 +4,12 @@ * Copyright (C) 1997-1998 Russell King * * Changelog: - * 30-08-1997 RMK 0.0.0 Created, READONLY version - * 22-01-1998 RMK 0.0.1 Updated to 2.1.80 + * 30-08-1997 RMK 0.0.0 Created, READONLY version. + * 22-01-1998 RMK 0.0.1 Updated to 2.1.80. * 15-04-1998 RMK 0.0.1 Only do PIO if FAS216 will allow it. - * 02-05-1998 RMK 0.0.2 Updated & added DMA support + * 02-05-1998 RMK 0.0.2 Updated & added DMA support. * 27-06-1998 RMK Changed asm/delay.h to linux/delay.h - * 18-08-1998 RMK 0.0.3 Fixed synchronous transfer depth + * 18-08-1998 RMK 0.0.3 Fixed synchronous transfer depth. */ #include <linux/module.h> @@ -117,6 +117,8 @@ static const expansioncard_ops_t cumanascsi_2_ops = { cumanascsi_2_irqenable, cumanascsi_2_irqdisable, NULL, + NULL, + NULL, NULL }; @@ -364,6 +366,7 @@ cumanascsi_2_detect(Scsi_Host_Template *tpnt) info->info.ifcfg.sync_max_depth = CUMANASCSI2_SYNC_DEPTH; info->info.ifcfg.cntl3 = CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK; info->info.ifcfg.disconnect_ok = 1; + info->info.ifcfg.wide_max_size = 0; info->info.dma.setup = cumanascsi_2_dma_setup; info->info.dma.pseudo = cumanascsi_2_dma_pseudo; info->info.dma.stop = cumanascsi_2_dma_stop; diff --git a/drivers/acorn/scsi/eesox.c b/drivers/acorn/scsi/eesox.c index 301b83d05..8ed77e157 100644 --- a/drivers/acorn/scsi/eesox.c +++ b/drivers/acorn/scsi/eesox.c @@ -38,9 +38,6 @@ #include "../../scsi/hosts.h" #include "eesox.h" -#define NO_IRQ 255 -#define NO_DMA 255 - /* Configuration */ #define EESOX_XTALFREQ 40 #define EESOX_ASYNC_PERIOD 200 @@ -123,6 +120,8 @@ static const expansioncard_ops_t eesoxscsi_ops = { eesoxscsi_irqenable, eesoxscsi_irqdisable, NULL, + NULL, + NULL, NULL }; @@ -379,6 +378,7 @@ eesoxscsi_detect(Scsi_Host_Template *tpnt) info->info.ifcfg.sync_max_depth = EESOX_SYNC_DEPTH; info->info.ifcfg.cntl3 = CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK; info->info.ifcfg.disconnect_ok = 1; + info->info.ifcfg.wide_max_size = 0; info->info.dma.setup = eesoxscsi_dma_setup; info->info.dma.pseudo = eesoxscsi_dma_pseudo; info->info.dma.stop = eesoxscsi_dma_stop; diff --git a/drivers/acorn/scsi/fas216.c b/drivers/acorn/scsi/fas216.c index 6cd638242..c89b74f1e 100644 --- a/drivers/acorn/scsi/fas216.c +++ b/drivers/acorn/scsi/fas216.c @@ -24,9 +24,9 @@ * 02-05-1998 RMK Added extra checks in fas216_reset * 24-05-1998 RMK Fixed synchronous transfers with period >= 200ns * 27-06-1998 RMK Changed asm/delay.h to linux/delay.h + * 26-08-1998 RMK Improved message support wrt MESSAGE_REJECT * * Todo: - * - tighten up the MESSAGE_REJECT support. * - allow individual devices to enable sync xfers. */ @@ -57,7 +57,7 @@ MODULE_DESCRIPTION("Generic FAS216/NCR53C9x driver"); #define VER_MAJOR 0 #define VER_MINOR 0 -#define VER_PATCH 4 +#define VER_PATCH 5 #define SCSI2_TAG @@ -86,6 +86,8 @@ MODULE_DESCRIPTION("Generic FAS216/NCR53C9x driver"); */ #define SCSI2_SYNC +#define SCSI2_WIDE + #undef DEBUG_CONNECT #undef DEBUG_BUSSERVICE #undef DEBUG_FUNCTIONDONE @@ -132,8 +134,8 @@ static void fas216_dumpinfo(FAS216_Info *info) printk(" SCp={ ptr=%p this_residual=%X buffer=%p buffers_residual=%X }\n", info->scsi.SCp.ptr, info->scsi.SCp.this_residual, info->scsi.SCp.buffer, info->scsi.SCp.buffers_residual); - printk(" msgs async_stp=%X last_message=%X disconnectable=%d aborting=%d }\n", - info->scsi.async_stp, info->scsi.last_message, + printk(" msgs async_stp=%X disconnectable=%d aborting=%d }\n", + info->scsi.async_stp, info->scsi.disconnectable, info->scsi.aborting); printk(" stats={ queues=%X removes=%X fins=%X reads=%X writes=%X miscs=%X\n" " disconnects=%X aborts=%X resets=%X }\n", @@ -144,10 +146,10 @@ static void fas216_dumpinfo(FAS216_Info *info) info->ifcfg.clockrate, info->ifcfg.select_timeout, info->ifcfg.asyncperiod, info->ifcfg.sync_max_depth); for (i = 0; i < 8; i++) { - printk(" busyluns[%d]=%X dev[%d]={ disconnect_ok=%d stp=%X sof=%X negstate=%X }\n", + printk(" busyluns[%d]=%X dev[%d]={ disconnect_ok=%d stp=%X sof=%X sync_state=%X }\n", i, info->busyluns[i], i, info->device[i].disconnect_ok, info->device[i].stp, - info->device[i].sof, info->device[i].negstate); + info->device[i].sof, info->device[i].sync_state); } printk(" dma={ transfer_type=%X setup=%p pseudo=%p stop=%p }\n", info->dma.transfer_type, info->dma.setup, @@ -192,19 +194,19 @@ static const char *fas216_bus_phase(int stat) static const char *fas216_drv_phase(FAS216_Info *info) { switch (info->scsi.phase) { - case PHASE_IDLE: return "idle"; - case PHASE_SELECTION: return "selection"; - case PHASE_MESSAGESENT: return "message sent"; - case PHASE_RECONNECTED: return "reconnected"; - case PHASE_DATAOUT: return "data out"; - case PHASE_DATAIN: return "data in"; - case PHASE_MSGOUT: return "message out"; - case PHASE_MSGIN: return "message in"; - case PHASE_AFTERMSGOUT: return "after message out"; - case PHASE_STATUS: return "status"; - case PHASE_DISCONNECT: return "disconnect"; - case PHASE_DONE: return "done"; - default: return "???"; + case PHASE_IDLE: return "idle"; + case PHASE_SELECTION: return "selection"; + case PHASE_COMMAND: return "command"; + case PHASE_RECONNECTED: return "reconnected"; + case PHASE_DATAOUT: return "data out"; + case PHASE_DATAIN: return "data in"; + case PHASE_MSGIN: return "message in"; + case PHASE_MSGIN_DISCONNECT: return "disconnect"; + case PHASE_MSGOUT_EXPECT: return "expect message out"; + case PHASE_MSGOUT: return "message out"; + case PHASE_STATUS: return "status"; + case PHASE_DONE: return "done"; + default: return "???"; } } @@ -262,6 +264,37 @@ static int fas216_clockrate(int clock) return clock; } +/* Function: unsigned short fas216_get_last_msg(FAS216_Info *info, int pos) + * Purpose : retrieve a last message from the list, using position in fifo + * Params : info - interface to search + * : pos - current fifo position + */ +static inline unsigned short +fas216_get_last_msg(FAS216_Info *info, int pos) +{ + unsigned short packed_msg = NOP; + struct message *msg; + int msgnr = 0; + + while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) { + if (pos >= msg->fifo) + break; + } + + if (msg) { + if (msg->msg[0] == EXTENDED_MESSAGE) + packed_msg = EXTENDED_MESSAGE | msg->msg[2] << 8; + else + packed_msg = msg->msg[0]; + } + +#ifdef DEBUG_MESSAGES + printk("Message: %04X found at position %02X\n", + packed_msg, pos); +#endif + return packed_msg; +} + /* Function: int fas216_syncperiod(FAS216_Info *info, int ns) * Purpose : Calculate value to be loaded into the STP register * for a given period in ns @@ -303,6 +336,240 @@ fas216_set_sync(FAS216_Info *info, int target) outb(info->scsi.cfg[2], REG_CNTL3(info)); } +/* Synchronous transfer support + * + * Note: The SCSI II r10 spec says (5.6.12): + * + * (2) Due to historical problems with early host adapters that could + * not accept an SDTR message, some targets may not initiate synchronous + * negotiation after a power cycle as required by this standard. Host + * adapters that support synchronous mode may avoid the ensuing failure + * modes when the target is independently power cycled by initiating a + * synchronous negotiation on each REQUEST SENSE and INQUIRY command. + * This approach increases the SCSI bus overhead and is not recommended + * for new implementations. The correct method is to respond to an + * SDTR message with a MESSAGE REJECT message if the either the + * initiator or target devices does not support synchronous transfers + * or does not want to negotiate for synchronous transfers at the time. + * Using the correct method assures compatibility with wide data + * transfers and future enhancements. + * + * We will always initiate a synchronous transfer negociation request on + * every INQUIRY or REQUEST SENSE message, unless the target itself has + * at some point performed a synchronous transfer negociation request, or + * we have synchronous transfers disabled for this device. + */ + +/* Function: void fas216_handlesync(FAS216_Info *info, char *msg) + * Purpose : Handle a synchronous transfer message from the target + * Params : info - state structure for interface + * : msg - message from target + */ +static void +fas216_handlesync(FAS216_Info *info, char *msg) +{ + struct fas216_device *dev = &info->device[info->SCpnt->target]; + enum { sync, async, none, reject } res = none; + +#ifdef SCSI2_SYNC + switch (msg[0]) { + case MESSAGE_REJECT: + /* Synchronous transfer request failed. + * Note: SCSI II r10: + * + * SCSI devices that are capable of synchronous + * data transfers shall not respond to an SDTR + * message with a MESSAGE REJECT message. + * + * Hence, if we get this condition, we disable + * negociation for this device. + */ + if (dev->sync_state == neg_inprogress) { + dev->sync_state = neg_invalid; + res = async; + } + break; + + case EXTENDED_MESSAGE: + switch (dev->sync_state) { + /* We don't accept synchronous transfer requests. + * Respond with a MESSAGE_REJECT to prevent a + * synchronous transfer agreement from being reached. + */ + case neg_invalid: + res = reject; + break; + + /* We were not negociating a synchronous transfer, + * but the device sent us a negociation request. + * Honour the request by sending back a SDTR + * message containing our capability, limited by + * the targets capability. + */ + default: + outb(CMD_SETATN, REG_CMD(info)); + if (msg[4] > info->ifcfg.sync_max_depth) + msg[4] = info->ifcfg.sync_max_depth; + if (msg[3] < 1000 / info->ifcfg.clockrate) + msg[3] = 1000 / info->ifcfg.clockrate; + + msgqueue_flush(&info->scsi.msgs); + msgqueue_addmsg(&info->scsi.msgs, 5, + EXTENDED_MESSAGE, 3, EXTENDED_SDTR, + msg[3], msg[4]); + info->scsi.phase = PHASE_MSGOUT_EXPECT; + + /* This is wrong. The agreement is not in effect + * until this message is accepted by the device + */ + dev->sync_state = neg_targcomplete; + res = sync; + break; + + /* We initiated the synchronous transfer negociation, + * and have successfully received a response from the + * target. The synchronous transfer agreement has been + * reached. Note: if the values returned are out of our + * bounds, we must reject the message. + */ + case neg_inprogress: + res = reject; + if (msg[4] <= info->ifcfg.sync_max_depth && + msg[3] >= 1000 / info->ifcfg.clockrate) { + dev->sync_state = neg_complete; + res = sync; + } + break; + } + } +#else + res = reject; +#endif + + switch (res) { + case sync: + dev->period = msg[3]; + dev->sof = msg[4]; + dev->stp = fas216_syncperiod(info, msg[3] * 4); + fas216_set_sync(info, info->SCpnt->target); + break; + + case reject: + outb(CMD_SETATN, REG_CMD(info)); + msgqueue_flush(&info->scsi.msgs); + msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT); + info->scsi.phase = PHASE_MSGOUT_EXPECT; + + case async: + dev->period = info->ifcfg.asyncperiod / 4; + dev->sof = 0; + dev->stp = info->scsi.async_stp; + fas216_set_sync(info, info->SCpnt->target); + break; + + case none: + break; + } +} + +/* Function: void fas216_handlewide(FAS216_Info *info, char *msg) + * Purpose : Handle a wide transfer message from the target + * Params : info - state structure for interface + * : msg - message from target + */ +static void +fas216_handlewide(FAS216_Info *info, char *msg) +{ + struct fas216_device *dev = &info->device[info->SCpnt->target]; + enum { wide, bit8, none, reject } res = none; + +#ifdef SCSI2_WIDE + switch (msg[0]) { + case MESSAGE_REJECT: + /* Wide transfer request failed. + * Note: SCSI II r10: + * + * SCSI devices that are capable of wide + * data transfers shall not respond to a + * WDTR message with a MESSAGE REJECT message. + * + * Hence, if we get this condition, we never + * reattempt negociation for this device. + */ + if (dev->wide_state == neg_inprogress) { + dev->wide_state = neg_invalid; + res = bit8; + } + break; + + case EXTENDED_MESSAGE: + switch (dev->wide_state) { + /* We don't accept wide data transfer requests. + * Respond with a MESSAGE REJECT to prevent a + * wide data transfer agreement from being reached. + */ + case neg_invalid: + res = reject; + break; + + /* We were not negociating a wide data transfer, + * but the device sent is a negociation request. + * Honour the request by sending back a WDTR + * message containing our capability, limited by + * the targets capability. + */ + default: + outb(CMD_SETATN, REG_CMD(info)); + if (msg[3] > info->ifcfg.wide_max_size) + msg[3] = info->ifcfg.wide_max_size; + + msgqueue_flush(&info->scsi.msgs); + msgqueue_addmsg(&info->scsi.msgs, 4, + EXTENDED_MESSAGE, 2, EXTENDED_WDTR, + msg[3]); + info->scsi.phase = PHASE_MSGOUT_EXPECT; + res = wide; + break; + + /* We initiated the wide data transfer negociation, + * and have successfully received a response from the + * target. The synchronous transfer agreement has been + * reached. Note: if the values returned are out of our + * bounds, we must reject the message. + */ + case neg_inprogress: + res = reject; + if (msg[3] <= info->ifcfg.wide_max_size) { + dev->wide_state = neg_complete; + res = wide; + } + break; + } + } +#else + res = reject; +#endif + + switch (res) { + case wide: + dev->wide_xfer = msg[3]; + break; + + case reject: + outb(CMD_SETATN, REG_CMD(info)); + msgqueue_flush(&info->scsi.msgs); + msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT); + info->scsi.phase = PHASE_MSGOUT_EXPECT; + + case bit8: + dev->wide_xfer = 0; + break; + + case none: + break; + } +} + /* Function: void fas216_updateptrs(FAS216_Info *info, int bytes_transferred) * Purpose : update data pointers after transfer suspended/paused * Params : info - interface's local pointer to update @@ -338,6 +605,9 @@ fas216_updateptrs(FAS216_Info *info, int bytes_transferred) residual -= bytes_transferred; ptr += bytes_transferred; + if (residual == 0) + ptr = NULL; + info->scsi.SCp.ptr = ptr; info->scsi.SCp.this_residual = residual; } @@ -353,7 +623,7 @@ fas216_pio(FAS216_Info *info, fasdmadir_t direction) { unsigned int residual; char *ptr; - int correction; + int correction = 0; fas216_checkmagic(info, "fas216_pio"); @@ -361,23 +631,24 @@ fas216_pio(FAS216_Info *info, fasdmadir_t direction) ptr = info->scsi.SCp.ptr; if (direction == DMA_OUT) { - while (residual > 0) { - if ((inb(REG_CFIS(info)) & CFIS_CF) < 8) { +// while (residual > 0) { +// if ((inb(REG_CFIS(info)) & CFIS_CF) < 8) { outb(*ptr++, REG_FF(info)); residual -= 1; - } else if (inb(REG_STAT(info)) & STAT_INT) - break; - } - correction = inb(REG_CFIS(info)) & CFIS_CF; +// } +// if (inb(REG_STAT(info)) & STAT_INT) +// break; +// } +// correction = inb(REG_CFIS(info)) & CFIS_CF; } else { - while (residual > 0) { - if ((inb(REG_CFIS(info)) & CFIS_CF) != 0) { +// while (residual > 0) { +// if ((inb(REG_CFIS(info)) & CFIS_CF) != 0) { *ptr++ = inb(REG_FF(info)); residual -= 1; - } else if (inb(REG_STAT(info)) & STAT_INT) - break; - } - correction = 0; +// } +// if (inb(REG_STAT(info)) & STAT_INT) +// break; +// } } ptr -= correction; @@ -549,10 +820,11 @@ fas216_disconnect_intr(FAS216_Info *info) switch (info->scsi.phase) { case PHASE_SELECTION: /* while selecting - no target */ + case PHASE_SELSTEPS: fas216_done(info, DID_NO_CONNECT); break; - case PHASE_DISCONNECT: /* message in - disconnecting */ + case PHASE_MSGIN_DISCONNECT: /* message in - disconnecting */ outb(CMD_ENABLESEL, REG_CMD(info)); info->scsi.disconnectable = 1; info->scsi.reconnected.tag = 0; @@ -564,8 +836,8 @@ fas216_disconnect_intr(FAS216_Info *info) fas216_done(info, DID_OK); break; - case PHASE_AFTERMSGOUT: /* message out - possible ABORT message */ - if (info->scsi.last_message == ABORT) { + case PHASE_MSGOUT: /* message out - possible ABORT message */ + if (fas216_get_last_msg(info, info->scsi.msgin_fifo) == ABORT) { info->scsi.aborting = 0; fas216_done(info, DID_ABORT); break; @@ -592,14 +864,17 @@ fas216_reselected_intr(FAS216_Info *info) fas216_checkmagic(info, "fas216_reselected_intr"); - if (info->scsi.phase == PHASE_SELECTION && info->SCpnt) { + if ((info->scsi.phase == PHASE_SELECTION || + info->scsi.phase == PHASE_SELSTEPS) && info->SCpnt) { Scsi_Cmnd *SCpnt = info->SCpnt; info->origSCpnt = SCpnt; info->SCpnt = NULL; - if (info->device[SCpnt->target].negstate == syncneg_sent) - info->device[SCpnt->target].negstate = syncneg_start; + if (info->device[SCpnt->target].wide_state == neg_inprogress) + info->device[SCpnt->target].wide_state = neg_wait; + if (info->device[SCpnt->target].sync_state == neg_inprogress) + info->device[SCpnt->target].sync_state = neg_wait; } #ifdef DEBUG_CONNECT @@ -607,15 +882,14 @@ fas216_reselected_intr(FAS216_Info *info) fas216_target(info), info->scsi.phase); #endif - msgqueue_flush(&info->scsi.msgs); - if ((inb(REG_CFIS(info)) & CFIS_CF) != 2) { printk(KERN_ERR "scsi%d.H: incorrect number of bytes after reselect\n", info->host->host_no); outb(CMD_SETATN, REG_CMD(info)); - msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT); - info->scsi.phase = PHASE_MSGOUT; outb(CMD_MSGACCEPTED, REG_CMD(info)); + msgqueue_flush(&info->scsi.msgs); + msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR); + info->scsi.phase = PHASE_MSGOUT_EXPECT; return; } @@ -636,13 +910,14 @@ fas216_reselected_intr(FAS216_Info *info) if (!ok) { /* - * Something went wrong - abort the command on - * the target. Should this be INITIATOR_ERROR ? + * Something went wrong - send an initiator error to + * the target. */ outb(CMD_SETATN, REG_CMD(info)); - msgqueue_addmsg(&info->scsi.msgs, 1, ABORT); - info->scsi.phase = PHASE_MSGOUT; outb(CMD_MSGACCEPTED, REG_CMD(info)); + msgqueue_flush(&info->scsi.msgs); + msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR); + info->scsi.phase = PHASE_MSGOUT_EXPECT; return; } @@ -672,17 +947,20 @@ fas216_reselected_intr(FAS216_Info *info) if (!ok && queue_probetgtlun(&info->queues.disconnected, target, identify_msg)) ok = 1; + msgqueue_flush(&info->scsi.msgs); if (ok) { info->scsi.phase = PHASE_RECONNECTED; outb(target, REG_SDID(info)); } else { /* - * Our command structure not found - abort the command on the target - * Should this be INITIATOR_ERROR ? + * Our command structure not found - abort the + * command on the target. Since we have no + * record of this command, we can't send + * an INITIATOR DETECTED ERROR message. */ outb(CMD_SETATN, REG_CMD(info)); msgqueue_addmsg(&info->scsi.msgs, 1, ABORT); - info->scsi.phase = PHASE_MSGOUT; + info->scsi.phase = PHASE_MSGOUT_EXPECT; } outb(CMD_MSGACCEPTED, REG_CMD(info)); } @@ -733,8 +1011,14 @@ fas216_finish_reconnect(FAS216_Info *info) } if (!info->SCpnt) { outb(CMD_SETATN, REG_CMD(info)); - msgqueue_addmsg(&info->scsi.msgs, 1, ABORT); - info->scsi.phase = PHASE_MSGOUT; + msgqueue_flush(&info->scsi.msgs); +#if 0 + if (info->scsi.reconnected.tag) + msgqueue_addmsg(&info->scsi.msgs, 2, ABORT_TAG, info->scsi.reconnected.tag); + else +#endif + msgqueue_addmsg(&info->scsi.msgs, 1, ABORT); + info->scsi.phase = PHASE_MSGOUT_EXPECT; info->scsi.aborting = 1; } else { /* @@ -751,6 +1035,28 @@ fas216_finish_reconnect(FAS216_Info *info) #endif } +static unsigned char fas216_get_msg_byte(FAS216_Info *info) +{ + int tout; + + outb(CMD_MSGACCEPTED, REG_CMD(info)); + for (tout = 1000000; tout; tout --) + if (inb(REG_STAT(info)) & STAT_INT) + break; + + inb(REG_INST(info)); + + outb(CMD_TRANSFERINFO, REG_CMD(info)); + + for (tout = 1000000; tout; tout --) + if (inb(REG_STAT(info)) & STAT_INT) + break; + + inb(REG_INST(info)); + + return inb(REG_FF(info)); +} + /* Function: void fas216_message(FAS216_Info *info) * Purpose : handle a function done interrupt from FAS216 chip * Params : info - interface which caused function done interrupt @@ -765,34 +1071,10 @@ static void fas216_message(FAS216_Info *info) message[0] = inb(REG_FF(info)); if (message[0] == EXTENDED_MESSAGE) { - int tout; - outb(CMD_MSGACCEPTED, REG_CMD(info)); - for (tout = 1000000; tout; tout--) - if (inb(REG_STAT(info)) & STAT_INT) - break; - inb(REG_INST(info)); - outb(CMD_TRANSFERINFO, REG_CMD(info)); - for (tout = 1000000; tout; tout--) - if (inb(REG_STAT(info)) & STAT_INT) - break; - inb(REG_INST(info)); - - message[1] = inb(REG_FF(info)); + message[1] = fas216_get_msg_byte(info); - for (msglen = 2; msglen < message[1] + 2; msglen++) { - outb(CMD_MSGACCEPTED, REG_CMD(info)); - for (tout = 1000000; tout; tout--) - if (inb(REG_STAT(info)) & STAT_INT) - break; - inb(REG_INST(info)); - outb(CMD_TRANSFERINFO, REG_CMD(info)); - for (tout = 1000000; tout; tout--) - if (inb(REG_STAT(info)) & STAT_INT) - break; - inb(REG_INST(info)); - - message[msglen] = inb(REG_FF(info)); - } + for (msglen = 2; msglen < message[1] + 2; msglen++) + message[msglen] = fas216_get_msg_byte(info); } #ifdef DEBUG_MESSAGES @@ -806,6 +1088,7 @@ static void fas216_message(FAS216_Info *info) printk("\n"); } #endif + if (info->scsi.phase == PHASE_RECONNECTED) { if (message[0] == SIMPLE_QUEUE_TAG) info->scsi.reconnected.tag = message[1]; @@ -815,14 +1098,22 @@ static void fas216_message(FAS216_Info *info) switch (message[0]) { case COMMAND_COMPLETE: - printk("fas216: command complete with no status in MESSAGE_IN?\n"); + printk(KERN_ERR "scsi%d.%c: command complete with no " + "status in MESSAGE_IN?\n", + info->host->host_no, fas216_target(info)); break; case SAVE_POINTERS: /* * Save current data pointer to SAVED data pointer + * SCSI II standard says that we must not acknowledge + * this until we have really saved pointers. + * NOTE: we DO NOT save the command nor status pointers + * as required by the SCSI II standard. These always + * point to the start of their respective areas. */ info->SCpnt->SCp = info->scsi.SCp; + info->SCpnt->SCp.sent_command = 0; #if defined (DEBUG_MESSAGES) || defined (DEBUG_CONNECT) printk("scsi%d.%c: save data pointers: [%p, %X]\n", info->host->host_no, fas216_target(info), @@ -843,13 +1134,27 @@ static void fas216_message(FAS216_Info *info) break; case DISCONNECT: - info->scsi.phase = PHASE_DISCONNECT; + info->scsi.phase = PHASE_MSGIN_DISCONNECT; break; case MESSAGE_REJECT: - printk("scsi%d.%c: reject, last message %04X\n", - info->host->host_no, fas216_target(info), - info->scsi.last_message); + switch (fas216_get_last_msg(info, info->scsi.msgin_fifo)) { + case EXTENDED_MESSAGE | EXTENDED_SDTR << 8: + fas216_handlesync(info, message); + break; + + case EXTENDED_MESSAGE | EXTENDED_WDTR << 8: + fas216_handlewide(info, message); + break; + + default: + printk("scsi%d.%c: reject, last message %04X\n", + info->host->host_no, fas216_target(info), + fas216_get_last_msg(info, info->scsi.msgin_fifo)); + } + break; + + case NOP: break; case SIMPLE_QUEUE_TAG: @@ -862,49 +1167,18 @@ static void fas216_message(FAS216_Info *info) case EXTENDED_MESSAGE: switch (message[2]) { case EXTENDED_SDTR: /* Sync transfer negociation request/reply */ - switch (info->device[info->SCpnt->target].negstate) { - case syncneg_invalid: - msgqueue_flush(&info->scsi.msgs); - outb(CMD_SETATN, REG_CMD(info)); - msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT); - info->scsi.phase = PHASE_MSGOUT; - break; - - default: - if (message[4] > info->ifcfg.sync_max_depth) - message[4] = info->ifcfg.sync_max_depth; - if (message[3] < 1000 / info->ifcfg.clockrate) - message[3] = 1000 / info->ifcfg.clockrate; - - outb(CMD_SETATN, REG_CMD(info)); - msgqueue_addmsg(&info->scsi.msgs, 5, - EXTENDED_MESSAGE, 3, EXTENDED_SDTR, - message[3], message[4]); - info->scsi.phase = PHASE_MSGOUT; - case syncneg_sent: - info->device[info->SCpnt->target].negstate = syncneg_complete; - info->device[info->SCpnt->target].period = message[3]; - info->device[info->SCpnt->target].sof = message[4]; - info->device[info->SCpnt->target].stp = - fas216_syncperiod(info, message[3] * 4); - printk(KERN_NOTICE "scsi%d.%c: using synchronous transfer, offset %d, %d ns\n", - info->host->host_no, fas216_target(info), message[4], message[3] * 4); - fas216_set_sync(info, info->SCpnt->target); - break; - } + fas216_handlesync(info, message); break; case EXTENDED_WDTR: /* Wide transfer negociation request/reply */ - /* We don't do wide transfers - reject message */ + fas216_handlewide(info, message); + break; + default: printk("scsi%d.%c: unrecognised extended message %02X, rejecting\n", info->host->host_no, fas216_target(info), message[2]); - msgqueue_flush(&info->scsi.msgs); - outb(CMD_SETATN, REG_CMD(info)); - msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT); - info->scsi.phase = PHASE_MSGOUT; - break; + goto reject_message; } break; @@ -912,13 +1186,17 @@ static void fas216_message(FAS216_Info *info) printk("scsi%d.%c: unrecognised message %02X, rejecting\n", info->host->host_no, fas216_target(info), message[0]); - msgqueue_flush(&info->scsi.msgs); - outb(CMD_SETATN, REG_CMD(info)); - msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT); - info->scsi.phase = PHASE_MSGOUT; - break; + goto reject_message; } outb(CMD_MSGACCEPTED, REG_CMD(info)); + return; + +reject_message: + outb(CMD_SETATN, REG_CMD(info)); + outb(CMD_MSGACCEPTED, REG_CMD(info)); + msgqueue_flush(&info->scsi.msgs); + msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT); + info->scsi.phase = PHASE_MSGOUT_EXPECT; } /* Function: void fas216_send_command(FAS216_Info *info) @@ -935,201 +1213,46 @@ static void fas216_send_command(FAS216_Info *info) outb(CMD_FLUSHFIFO, REG_CMD(info)); /* load command */ - for (i = 0; i < info->SCpnt->cmd_len; i++) + for (i = info->scsi.SCp.sent_command; i < info->SCpnt->cmd_len; i++) outb(info->SCpnt->cmnd[i], REG_FF(info)); outb(CMD_TRANSFERINFO, REG_CMD(info)); -} - -/* Function: int fas216_busservice_selection(FAS216_Info *info, unsigned int stat) - * Purpose : handle bus service in selection phase - * Params : info - interface which caused bus service - * Returns : 0 if unable to service this interrupt - */ -static int fas216_busservice_selection(FAS216_Info *info, unsigned int stat) -{ - fas216_checkmagic(info, "fas216_busservice_selection"); - - switch (stat & STAT_BUSMASK) { - case STAT_DATAOUT: /* data out phase */ - fas216_starttransfer(info, DMA_OUT, 1); - return 1; - - case STAT_DATAIN: /* data in phase */ - fas216_starttransfer(info, DMA_IN, 0); - return 1; - - case STAT_STATUS: /* status phase */ - info->scsi.phase = PHASE_STATUS; - outb(CMD_INITCMDCOMPLETE, REG_CMD(info)); - return 1; - - case STAT_MESGIN: /* message in phase */ - info->scsi.phase = PHASE_MSGIN; - outb(CMD_TRANSFERINFO, REG_CMD(info)); - return 1; - - case STAT_MESGOUT:{ /* message out phase */ - char *msg; - int start = 1, msglen; - - /* load message bytes, but don't forget to miss the first - * byte! - */ - while ((msg = msgqueue_getnextmsg(&info->scsi.msgs, &msglen)) != NULL) { - int i; - - for (i = start; i < msglen; i++) - outb(msg[i], REG_FF(info)); - start = 0; - } - outb(CMD_TRANSFERINFO, REG_CMD(info)); - info->scsi.phase = PHASE_MESSAGESENT; - return 1; - } - default: - return 0; - } -} - -/* Function: int fas216_busservice_messagesent(FAS216_Info *info, unsigned int stat) - * Purpose : handle bus service after the IDENTIFY message has been sent - * Params : info - interface which caused bus service - * Returns : 0 if unable to service this interrupt - */ -static int fas216_busservice_messagesent(FAS216_Info *info, unsigned int stat) -{ - fas216_checkmagic(info, "fas216_busservice_messagesent"); - - switch (stat & STAT_BUSMASK) { - case STAT_MESGIN: /* message in phase */ - info->scsi.phase = PHASE_MSGIN; - outb(CMD_FLUSHFIFO, REG_CMD(info)); - outb(CMD_TRANSFERINFO, REG_CMD(info)); - return 1; - case STAT_COMMAND: /* command phase */ - fas216_send_command(info); - return 1; - - default: - return 0; - } + info->scsi.phase = PHASE_COMMAND; } -/* Function: int fas216_busservice_dataphase(FAS216_Info *info, unsigned int stat) - * Purpose : handle bus service in a data in/out phase. - * Params : info - interface which caused bus service - * Returns : 0 if unable to service this interrupt - * Note : We do not allow the device to change the data direction! - */ -static int fas216_busservice_dataphase(FAS216_Info *info, unsigned int stat) -{ - fas216_checkmagic(info, "fas216_busservice_dataphase"); - - switch (stat & STAT_BUSMASK) { - case STAT_DATAIN: /* continue data in phase */ - if (info->scsi.phase == PHASE_DATAIN) { - fas216_starttransfer(info, DMA_IN, 0); - return 1; - } else - return 0; - - case STAT_DATAOUT: /* continue data out phase */ - if (info->scsi.phase == PHASE_DATAOUT) { - fas216_starttransfer(info, DMA_OUT, 0); - return 1; - } else - return 0; - - case STAT_STATUS: /* status in phase */ - fas216_stoptransfer(info); - info->scsi.phase = PHASE_STATUS; - outb(CMD_INITCMDCOMPLETE, REG_CMD(info)); - return 1; - - case STAT_MESGIN: /* message in phase */ - fas216_stoptransfer(info); - info->scsi.phase = PHASE_MSGIN; - outb(CMD_TRANSFERINFO, REG_CMD(info)); - return 1; - - default: - return 0; - } -} - -/* Function: int fas216_busservice_reconnected(FAS216_Info *info, unsigned int stat) - * Purpose : handle bus service in after a reconnection +/* Function: void fas216_send_messageout(FAS216_Info *info, int start) + * Purpose : handle bus service to send a message * Params : info - interface which caused bus service - * Returns : 0 if unable to service this interrupt * Note : We do not allow the device to change the data direction! */ -static int fas216_busservice_reconnected(FAS216_Info *info, unsigned int stat) +static void fas216_send_messageout(FAS216_Info *info, int start) { - fas216_checkmagic(info, "fas216_busservice_reconnected"); - - switch (stat & STAT_BUSMASK) { - case STAT_MESGIN: - outb(CMD_TRANSFERINFO, REG_CMD(info)); - return 1; - - case STAT_STATUS: - fas216_finish_reconnect(info); - info->scsi.phase = PHASE_STATUS; - outb(CMD_INITCMDCOMPLETE, REG_CMD(info)); - return 1; - - case STAT_DATAOUT: /* data out phase */ - fas216_finish_reconnect(info); - fas216_starttransfer(info, DMA_OUT, 1); - return 1; + unsigned int tot_msglen = msgqueue_msglength(&info->scsi.msgs); - case STAT_DATAIN: /* data in phase */ - fas216_finish_reconnect(info); - fas216_starttransfer(info, DMA_IN, 0); - return 1; + fas216_checkmagic(info, "fas216_send_messageout"); - default: - return 0; - } -} + outb(CMD_FLUSHFIFO, REG_CMD(info)); -/* Function: int fas216_busservice_messageout(FAS216_Info *info, unsigned int stat) - * Purpose : handle bus service to send a message - * Params : info - interface which caused bus service - * Returns : 0 if unable to service this interrupt - * Note : We do not allow the device to change the data direction! - */ -static int fas216_busservice_messageout(FAS216_Info *info, unsigned int stat) -{ - fas216_checkmagic(info, "fas216_busservice_messageout"); + if (tot_msglen) { + struct message *msg; + int msgnr = 0; - if ((stat & STAT_BUSMASK) != STAT_MESGOUT) { - printk("scsi%d.%c: didn't manage MESSAGE OUT phase\n", - info->host->host_no, fas216_target(info)); - return 0; - } else { - unsigned int msglen = msgqueue_msglength(&info->scsi.msgs); + while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) { + int i; - outb(CMD_FLUSHFIFO, REG_CMD(info)); + for (i = start; i < msg->length; i++) + outb(msg->msg[i], REG_FF(info)); - if (msglen == 0) - outb(NOP, REG_FF(info)); - else { - char *msg; + msg->fifo = tot_msglen - (inb(REG_CFIS(info)) & CFIS_CF); + start = 0; + } + } else + outb(NOP, REG_FF(info)); - while ((msg = msgqueue_getnextmsg(&info->scsi.msgs, &msglen)) != NULL) { - int i; + outb(CMD_TRANSFERINFO, REG_CMD(info)); - for (i = 0; i < msglen; i++) - outb(msg[i], REG_FF(info)); - } - } - outb(CMD_TRANSFERINFO, REG_CMD(info)); - info->scsi.phase = PHASE_AFTERMSGOUT; - return 1; - } + info->scsi.phase = PHASE_MSGOUT; } /* Function: void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigned int ssr) @@ -1151,91 +1274,150 @@ static void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigne case IS_COMPLETE: /* last action completed */ outb(CMD_NOP, REG_CMD(info)); - switch (info->scsi.phase) { - case PHASE_SELECTION: /* while selecting - selected target */ - if (!fas216_busservice_selection(info, stat)) - printk("scsi%d.%c: bus phase %s after connect?\n", - info->host->host_no, fas216_target(info), - fas216_bus_phase(stat)); - break; - - case PHASE_MESSAGESENT: - if (!fas216_busservice_messagesent(info, stat)) - printk("scsi%d.%c: bus phase %s after message sent?\n", - info->host->host_no, fas216_target(info), - fas216_bus_phase(stat)); - break; +#define STATE(st,ph) ((ph) << 3 | (st)) + /* This table describes the legal SCSI state transitions, + * as described by the SCSI II spec. + */ + switch (STATE(stat & STAT_BUSMASK, info->scsi.phase)) { + /* Reselmsgin -> Data In */ + case STATE(STAT_DATAIN, PHASE_RECONNECTED): + fas216_finish_reconnect(info); + case STATE(STAT_DATAIN, PHASE_SELSTEPS):/* Sel w/ steps -> Data In */ + case STATE(STAT_DATAIN, PHASE_DATAIN): /* Data In -> Data In */ + case STATE(STAT_DATAIN, PHASE_MSGOUT): /* Message Out -> Data In */ + case STATE(STAT_DATAIN, PHASE_COMMAND): /* Command -> Data In */ + case STATE(STAT_DATAIN, PHASE_MSGIN): /* Message In -> Data In */ + fas216_starttransfer(info, DMA_IN, 0); + return; - case PHASE_DATAIN: /* while transfering data in */ - case PHASE_DATAOUT: /* while transfering data out */ - if (!fas216_busservice_dataphase(info, stat)) - printk("scsi%d.%c: bus phase %s after %s?\n", - info->host->host_no, fas216_target(info), - fas216_bus_phase(stat), fas216_drv_phase(info)); - break; + case STATE(STAT_DATAOUT, PHASE_DATAOUT):/* Data Out -> Data Out */ + fas216_starttransfer(info, DMA_OUT, 0); + return; + + /* Reselmsgin -> Data Out */ + case STATE(STAT_DATAOUT, PHASE_RECONNECTED): + fas216_finish_reconnect(info); + case STATE(STAT_DATAOUT, PHASE_SELSTEPS):/* Sel w/ steps-> Data Out */ + case STATE(STAT_DATAOUT, PHASE_MSGOUT): /* Message Out -> Data Out */ + case STATE(STAT_DATAOUT, PHASE_COMMAND):/* Command -> Data Out */ + case STATE(STAT_DATAOUT, PHASE_MSGIN): /* Message In -> Data Out */ + fas216_starttransfer(info, DMA_OUT, 1); + return; + + /* Reselmsgin -> Status */ + case STATE(STAT_STATUS, PHASE_RECONNECTED): + fas216_finish_reconnect(info); + goto status; + case STATE(STAT_STATUS, PHASE_DATAOUT): /* Data Out -> Status */ + case STATE(STAT_STATUS, PHASE_DATAIN): /* Data In -> Status */ + fas216_stoptransfer(info); + case STATE(STAT_STATUS, PHASE_SELSTEPS):/* Sel w/ steps -> Status */ + case STATE(STAT_STATUS, PHASE_MSGOUT): /* Message Out -> Status */ + case STATE(STAT_STATUS, PHASE_COMMAND): /* Command -> Status */ + case STATE(STAT_STATUS, PHASE_MSGIN): /* Message In -> Status */ + status: + outb(CMD_INITCMDCOMPLETE, REG_CMD(info)); + info->scsi.phase = PHASE_STATUS; + return; + + case STATE(STAT_MESGIN, PHASE_DATAOUT): /* Data Out -> Message In */ + case STATE(STAT_MESGIN, PHASE_DATAIN): /* Data In -> Message In */ + fas216_stoptransfer(info); + case STATE(STAT_MESGIN, PHASE_SELSTEPS):/* Sel w/ steps -> Message In */ + case STATE(STAT_MESGIN, PHASE_MSGOUT): /* Message Out -> Message In */ + info->scsi.msgin_fifo = inb(REG_CFIS(info)) & CFIS_CF; + outb(CMD_TRANSFERINFO, REG_CMD(info)); + info->scsi.phase = PHASE_MSGIN; + return; - case PHASE_RECONNECTED: /* newly reconnected device */ - /* - * Command reconnected - if MESGIN, get message - it may be - * the tag. If not, get command out of the disconnected queue + /* Reselmsgin -> Message In */ + case STATE(STAT_MESGIN, PHASE_RECONNECTED): + case STATE(STAT_MESGIN, PHASE_MSGIN): + info->scsi.msgin_fifo = inb(REG_CFIS(info)) & CFIS_CF; + outb(CMD_TRANSFERINFO, REG_CMD(info)); + return; + + /* Reselmsgin -> Command */ + case STATE(STAT_COMMAND, PHASE_RECONNECTED): + fas216_finish_reconnect(info); + case STATE(STAT_COMMAND, PHASE_MSGOUT): /* Message Out -> Command */ + case STATE(STAT_COMMAND, PHASE_MSGIN): /* Message In -> Command */ + fas216_send_command(info); + info->scsi.phase = PHASE_COMMAND; + return; + /* Selection -> Message Out */ + case STATE(STAT_MESGOUT, PHASE_SELECTION): + fas216_send_messageout(info, 1); + return; + /* Any -> Message Out */ + case STATE(STAT_MESGOUT, PHASE_MSGOUT_EXPECT): + fas216_send_messageout(info, 0); + return; + + /* Error recovery rules. + * These either attempt to abort or retry the operation. + * TODO: we need more of these + */ + case STATE(STAT_COMMAND, PHASE_COMMAND):/* Command -> Command */ + /* error - we've sent out all the command bytes + * we have. + * NOTE: we need SAVE DATA POINTERS/RESTORE DATA POINTERS + * to include the command bytes sent for this to work + * correctly. */ - if (!fas216_busservice_reconnected(info, stat)) - printk("scsi%d.%c: bus phase %s after reconnect?\n", - info->host->host_no, fas216_target(info), - fas216_bus_phase(stat)); - break; - - case PHASE_MSGIN: - case PHASE_AFTERMSGOUT: - switch (stat & STAT_BUSMASK) { - case STAT_MESGIN: - info->scsi.phase = PHASE_MSGIN; - outb(CMD_TRANSFERINFO, REG_CMD(info)); - break; - - case STAT_COMMAND: /* command phase */ - fas216_send_command(info); - info->scsi.phase = PHASE_SELECTION; - break; - - default: - printk("scsi%d.%c: bus phase %s after %s?\n", - info->host->host_no, fas216_target(info), - fas216_bus_phase(stat), - fas216_drv_phase(info)); - } - break; + printk(KERN_ERR "scsi%d.%c: " + "target trying to receive more command bytes\n", + info->host->host_no, fas216_target(info)); + outb(CMD_SETATN, REG_CMD(info)); + outb(15, REG_STCL(info)); + outb(0, REG_STCM(info)); + outb(0, REG_STCH(info)); + outb(CMD_PADBYTES | CMD_WITHDMA, REG_CMD(info)); + msgqueue_flush(&info->scsi.msgs); + msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR); + info->scsi.phase = PHASE_MSGOUT_EXPECT; + return; + + /* Selection -> Message Out */ + case STATE(STAT_MESGOUT, PHASE_SELSTEPS): + case STATE(STAT_MESGOUT, PHASE_MSGOUT): /* Message Out -> Message Out */ + /* If we get another message out phase, this + * usually means some parity error occurred. + * Resend complete set of messages. If we have + * more than 1 byte to send, we need to assert + * ATN again. + */ + if (msgqueue_msglength(&info->scsi.msgs) > 1) + outb(CMD_SETATN, REG_CMD(info)); - case PHASE_MSGOUT: - if (!fas216_busservice_messageout(info, stat)) - printk("scsi%d.%c: bus phase %s instead of message out?\n", - info->host->host_no, fas216_target(info), - fas216_bus_phase(stat)); - break; + fas216_send_messageout(info, 0); + return; + } - case PHASE_DISCONNECT: - printk("scsi%d.%c: disconnect message received, but bus service %s?\n", + if (info->scsi.phase == PHASE_MSGIN_DISCONNECT) { + printk(KERN_ERR "scsi%d.%c: disconnect message received, but bus service %s?\n", info->host->host_no, fas216_target(info), fas216_bus_phase(stat)); + msgqueue_flush(&info->scsi.msgs); outb(CMD_SETATN, REG_CMD(info)); msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR); - info->scsi.phase = PHASE_MSGOUT; + info->scsi.phase = PHASE_MSGOUT_EXPECT; info->scsi.aborting = 1; outb(CMD_TRANSFERINFO, REG_CMD(info)); - break; - - default: - printk("scsi%d.%c: internal phase %s for bus service?" - " What do I do with this?\n", - info->host->host_no, fas216_target(info), - fas216_drv_phase(info)); + return; } - break; + printk(KERN_ERR "scsi%d.%c: bus phase %s after %s?\n", + info->host->host_no, fas216_target(info), + fas216_bus_phase(stat), + fas216_drv_phase(info)); + print_debug_list(); + return; default: printk("scsi%d.%c: bus service at step %d?\n", info->host->host_no, fas216_target(info), ssr & IS_BITS); + print_debug_list(); } } @@ -1269,6 +1451,7 @@ static void fas216_funcdone_intr(FAS216_Info *info, unsigned int stat, unsigned case PHASE_MSGIN: /* message in phase */ case PHASE_RECONNECTED: /* reconnected command */ if ((stat & STAT_BUSMASK) == STAT_MESGIN) { + info->scsi.msgin_fifo = inb(REG_CFIS(info)) & CFIS_CF; fas216_message(info); break; } @@ -1300,10 +1483,11 @@ void fas216_intr(struct Scsi_Host *instance) if (stat & STAT_INT) { if (isr & INST_BUSRESET) - printk("scsi%d.H: fas216: bus reset detected\n", instance->host_no); - else if (isr & INST_ILLEGALCMD) + printk(KERN_DEBUG "scsi%d.H: bus reset detected\n", instance->host_no); + else if (isr & INST_ILLEGALCMD) { printk(KERN_CRIT "scsi%d.H: illegal command given\n", instance->host_no); - else if (isr & INST_DISCONNECT) + fas216_dumpstate(info); + } else if (isr & INST_DISCONNECT) fas216_disconnect_intr(info); else if (isr & INST_RESELECTED) /* reselected */ fas216_reselected_intr(info); @@ -1327,7 +1511,7 @@ void fas216_intr(struct Scsi_Host *instance) static void fas216_kick(FAS216_Info *info) { Scsi_Cmnd *SCpnt; - int i, msglen, from_queue = 0; + int tot_msglen, from_queue = 0; fas216_checkmagic(info, "fas216_kick"); @@ -1380,7 +1564,8 @@ static void fas216_kick(FAS216_Info *info) if (from_queue) { #ifdef SCSI2_TAG - if (SCpnt->device->tagged_queue && SCpnt->cmnd[0] != REQUEST_SENSE) { + if (SCpnt->device->tagged_queue && SCpnt->cmnd[0] != REQUEST_SENSE && + SCpnt->cmnd[0] != INQUIRY) { SCpnt->device->current_tag += 1; if (SCpnt->device->current_tag == 0) SCpnt->device->current_tag = 1; @@ -1409,6 +1594,7 @@ static void fas216_kick(FAS216_Info *info) /* build outgoing message bytes */ msgqueue_flush(&info->scsi.msgs); + if (info->device[SCpnt->target].disconnect_ok) msgqueue_addmsg(&info->scsi.msgs, 1, IDENTIFY(1, SCpnt->lun)); else @@ -1418,15 +1604,29 @@ static void fas216_kick(FAS216_Info *info) if (SCpnt->tag) msgqueue_addmsg(&info->scsi.msgs, 2, SIMPLE_QUEUE_TAG, SCpnt->tag); - /* add synchronous negociation */ - if (SCpnt->cmnd[0] == REQUEST_SENSE && - info->device[SCpnt->target].negstate == syncneg_start) { - info->device[SCpnt->target].negstate = syncneg_sent; +#ifdef SCSI2_WIDE + if (info->device[SCpnt->target].wide_state == neg_wait) { + info->device[SCpnt->target].wide_state = neg_inprogress; + msgqueue_addmsg(&info->scsi.msgs, 4, + EXTENDED_MESSAGE, 2, EXTENDED_WDTR, + info->ifcfg.wide_max_size); + } +#ifdef SCSI2_SYNC + else +#endif +#endif +#ifdef SCSI2_SYNC + if ((info->device[SCpnt->target].sync_state == neg_wait || + info->device[SCpnt->target].sync_state == neg_complete) && + (SCpnt->cmnd[0] == REQUEST_SENSE || + SCpnt->cmnd[0] == INQUIRY)) { + info->device[SCpnt->target].sync_state = neg_inprogress; msgqueue_addmsg(&info->scsi.msgs, 5, EXTENDED_MESSAGE, 3, EXTENDED_SDTR, 1000 / info->ifcfg.clockrate, info->ifcfg.sync_max_depth); } +#endif /* following what the ESP driver says */ outb(0, REG_STCL(info)); @@ -1444,25 +1644,29 @@ static void fas216_kick(FAS216_Info *info) /* synchronous transfers */ fas216_set_sync(info, SCpnt->target); - msglen = msgqueue_msglength(&info->scsi.msgs); + tot_msglen = msgqueue_msglength(&info->scsi.msgs); - if (msglen == 1 || msglen == 3) { + if (tot_msglen == 1 || tot_msglen == 3) { /* * We have an easy message length to send... */ - char *msg; + struct message *msg; + int msgnr = 0, i; + + info->scsi.phase = PHASE_SELSTEPS; /* load message bytes */ - while ((msg = msgqueue_getnextmsg(&info->scsi.msgs, &msglen)) != NULL) { - for (i = 0; i < msglen; i++) - outb(msg[i], REG_FF(info)); + while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) { + for (i = 0; i < msg->length; i++) + outb(msg->msg[i], REG_FF(info)); + msg->fifo = tot_msglen - (inb(REG_CFIS(info)) & CFIS_CF); } /* load command */ for (i = 0; i < SCpnt->cmd_len; i++) outb(SCpnt->cmnd[i], REG_FF(info)); - if (msglen == 1) + if (tot_msglen == 1) outb(CMD_SELECTATN, REG_CMD(info)); else outb(CMD_SELECTATN3, REG_CMD(info)); @@ -1471,17 +1675,11 @@ static void fas216_kick(FAS216_Info *info) * We have an unusual number of message bytes to send. * Load first byte into fifo, and issue SELECT with ATN and * stop steps. - * Note: we only peek at t his message - we need the rest - * later on! */ - int thismsg; - char *msg = msgqueue_peeknextmsg(&info->scsi.msgs, &thismsg); + struct message *msg = msgqueue_getmsg(&info->scsi.msgs, 0); - if (!msg || thismsg < 1) - printk(KERN_CRIT "scsi%d.%c: no message to send, but %d bytes\n", - info->host->host_no, fas216_target(info), msglen); - else - outb(msg[0], REG_FF(info)); + outb(msg->msg[0], REG_FF(info)); + msg->fifo = 1; outb(CMD_SELECTATNSTOP, REG_CMD(info)); } @@ -1525,11 +1723,15 @@ static void fas216_done(FAS216_Info *info, unsigned int result) /* * In theory, this should not happen, but just in case it does. */ - if (info->scsi.SCp.ptr && result == DID_OK) { + if (info->scsi.SCp.ptr && + info->scsi.SCp.this_residual && + result == DID_OK) { switch (SCpnt->cmnd[0]) { case INQUIRY: case START_STOP: case READ_CAPACITY: + case TEST_UNIT_READY: + case MODE_SENSE: break; default: @@ -1579,6 +1781,7 @@ static void fas216_done(FAS216_Info *info, unsigned int result) int fas216_queue_command(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) { FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata; + unsigned long flags; fas216_checkmagic(info, "fas216_queue_command"); @@ -1618,27 +1821,18 @@ int fas216_queue_command(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) info->stats.queues += 1; SCpnt->tag = 0; - if (info->scsi.irq != NO_IRQ) { - unsigned long flags; - - /* add command into execute queue and let it complete under - * the drivers interrupts. - */ - if (!queue_add_cmd_ordered(&info->queues.issue, SCpnt)) { - SCpnt->result = DID_ERROR << 16; - done(SCpnt); - } - save_flags_cli(flags); - if (!info->SCpnt || info->scsi.disconnectable) - fas216_kick(info); - restore_flags(flags); - } else { - /* no interrupts to rely on - we'll have to handle the - * command ourselves. For now, we give up. - */ + /* add command into execute queue and let it complete under + * whatever scheme we're using. + */ + if (!queue_add_cmd_ordered(&info->queues.issue, SCpnt)) { SCpnt->result = DID_ERROR << 16; done(SCpnt); } + save_flags_cli(flags); + if (!info->SCpnt || info->scsi.disconnectable) + fas216_kick(info); + restore_flags(flags); + return 0; } @@ -1672,15 +1866,28 @@ int fas216_command(Scsi_Cmnd *SCpnt) /* * This wastes time, since we can't return until the command is - * complete. We can't seep either since we may get re-entered! + * complete. We can't sleep either since we may get re-entered! * However, we must re-enable interrupts, or else we'll be * waiting forever. */ save_flags(flags); sti(); - while (!info->internal_done) - barrier(); + while (!info->internal_done) { + /* + * If we don't have an IRQ, then we must + * poll the card for it's interrupt, and + * use that to call this driver's interrupt + * routine. That way, we keep the command + * progressing. + */ + if (info->scsi.irq == NO_IRQ) { + sti(); + while (!(inb(REG_STAT(info)) & STAT_INT)); + cli(); + fas216_intr(info->host); + } + } restore_flags(flags); @@ -1752,6 +1959,95 @@ int fas216_eh_host_reset(Scsi_Cmnd *SCpnt) return FAILED; } +enum res_abort { res_not_running, res_success, res_success_clear, res_snooze }; + +/* + * Prototype: enum res_abort fas216_do_abort(FAS216_Info *info, Scsi_Cmnd *SCpnt) + * Purpose : abort a command on this host + * Params : SCpnt - command to abort + * Returns : abort status + */ +static enum res_abort +fas216_do_abort(FAS216_Info *info, Scsi_Cmnd *SCpnt) +{ + enum res_abort res = res_not_running; + + if (queue_removecmd(&info->queues.issue, SCpnt)) { + /* + * The command was on the issue queue, and has not been + * issued yet. We can remove the command from the queue, + * and acknowledge the abort. Neither the devices nor the + * interface know about the command. + */ + printk("on issue queue "); + + res = res_success; + } else if (queue_removecmd(&info->queues.disconnected, SCpnt)) { + /* + * The command was on the disconnected queue. Simply + * acknowledge the abort condition, and when the target + * reconnects, we will give it an ABORT message. The + * target should then disconnect, and we will clear + * the busylun bit. + */ + printk("on disconnected queue "); + + res = res_success; + } else if (info->SCpnt == SCpnt) { + unsigned long flags; + + printk("executing "); + + save_flags(flags); + cli(); + switch (info->scsi.phase) { + /* + * If the interface is idle, and the command is 'disconnectable', + * then it is the same as on the disconnected queue. We simply + * remove all traces of the command. When the target reconnects, + * we will give it an ABORT message since the command could not + * be found. When the target finally disconnects, we will clear + * the busylun bit. + */ + case PHASE_IDLE: + if (info->scsi.disconnectable) { + info->scsi.disconnectable = 0; + info->SCpnt = NULL; + res = res_success; + } + break; + + /* + * If the command has connected and done nothing futher, + * simply force a disconnect. We also need to clear the + * busylun bit. + */ + case PHASE_SELECTION: +// info->SCpnt = NULL; +// res = res_success_clear; +// break; + + default: + res = res_snooze; + break; + } + restore_flags(flags); + } else if (info->origSCpnt == SCpnt) { + /* + * The command will be executed next, but a command + * is currently using the interface. This is similar to + * being on the issue queue, except the busylun bit has + * been set. + */ + info->origSCpnt = NULL; + printk("waiting for execution "); + res = res_success_clear; + } else + printk("unknown "); + + return res; +} + /* Function: int fas216_abort(Scsi_Cmnd *SCpnt) * Purpose : abort a command if something horrible happens. * Params : SCpnt - Command that is believed to be causing a problem. @@ -1769,46 +2065,51 @@ int fas216_abort(Scsi_Cmnd *SCpnt) print_debug_list(); fas216_dumpstate(info); fas216_dumpinfo(info); - printk(KERN_WARNING "scsi%d: fas216_abort: ", info->host->host_no); - do { - /* If command is waiting in the issue queue, then we can - * simply remove the command and return abort status - */ - if (queue_removecmd(&info->queues.issue, SCpnt)) { - SCpnt->result = DID_ABORT << 16; - SCpnt->scsi_done(SCpnt); - printk("command on issue queue"); - result = SCSI_ABORT_SUCCESS; - break; - } + printk(KERN_WARNING "scsi%d: abort ", info->host->host_no); - /* If the command is on the disconencted queue, we need to - * reconnect to the device - */ - if (queue_cmdonqueue(&info->queues.disconnected, SCpnt)) - printk("command on disconnected queue"); + switch (fas216_do_abort(info, SCpnt)) { + /* + * We managed to find the command and cleared it out. + * We do not expect the command to be executing on the + * target, but we have set the busylun bit. + */ + case res_success_clear: + printk("clear "); + clear_bit(SCpnt->target * 8 + SCpnt->lun, info->busyluns); - /* If the command is connected, we need to flag that the - * command needs to be aborted - */ - if (info->SCpnt == SCpnt) - printk("command executing"); + /* + * We found the command, and cleared it out. Either + * the command is still known to be executing on the + * target, or the busylun bit is not set. + */ + case res_success: + printk("success\n"); + SCpnt->result = DID_ABORT << 16; + SCpnt->scsi_done(SCpnt); + result = SCSI_ABORT_SUCCESS; + break; - /* If the command is pending for execution, then again - * this is simple - we remove it and report abort status - */ - if (info->origSCpnt == SCpnt) { - info->origSCpnt = NULL; - SCpnt->result = DID_ABORT << 16; - SCpnt->scsi_done(SCpnt); - printk("command waiting for execution"); - result = SCSI_ABORT_SUCCESS; - break; - } - } while (0); + /* + * We did find the command, but unfortunately we couldn't + * unhook it from ourselves. Wait some more, and if it + * still doesn't complete, reset the interface. + */ + case res_snooze: + printk("snooze\n"); + result = SCSI_ABORT_SNOOZE; + break; - printk("\n"); + /* + * The command could not be found (either because it completed, + * or it got dropped. + */ + default: + case res_not_running: + result = SCSI_ABORT_SNOOZE; + printk("not running\n"); + break; + } return result; } @@ -1819,7 +2120,7 @@ int fas216_abort(Scsi_Cmnd *SCpnt) */ static void fas216_reset_state(FAS216_Info *info) { - syncneg_t negstate; + neg_t sync_state, wide_state; int i; fas216_checkmagic(info, "fas216_reset_state"); @@ -1833,26 +2134,37 @@ static void fas216_reset_state(FAS216_Info *info) info->scsi.reconnected.lun = 0; info->scsi.reconnected.tag = 0; info->scsi.disconnectable = 0; - info->scsi.last_message = 0; info->scsi.aborting = 0; info->scsi.phase = PHASE_IDLE; - info->scsi.async_stp = fas216_syncperiod(info, info->ifcfg.asyncperiod); + info->scsi.async_stp = + fas216_syncperiod(info, info->ifcfg.asyncperiod); + + if (info->ifcfg.wide_max_size == 0) + wide_state = neg_invalid; + else +#ifdef SCSI2_WIDE + wide_state = neg_wait; +#else + wide_state = neg_invalid; +#endif if (info->host->dma_channel == NO_DMA || !info->dma.setup) - negstate = syncneg_invalid; + sync_state = neg_invalid; else #ifdef SCSI2_SYNC - negstate = syncneg_start; + sync_state = neg_wait; #else - negstate = syncneg_invalid; + sync_state = neg_invalid; #endif for (i = 0; i < 8; i++) { - info->device[i].disconnect_ok = info->ifcfg.disconnect_ok; - info->device[i].negstate = negstate; - info->device[i].period = info->ifcfg.asyncperiod / 4; - info->device[i].stp = info->scsi.async_stp; - info->device[i].sof = 0; + info->device[i].disconnect_ok = info->ifcfg.disconnect_ok; + info->device[i].sync_state = sync_state; + info->device[i].wide_state = wide_state; + info->device[i].period = info->ifcfg.asyncperiod / 4; + info->device[i].stp = info->scsi.async_stp; + info->device[i].sof = 0; + info->device[i].wide_xfer = 0; } } @@ -1908,9 +2220,11 @@ int fas216_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags) info->stats.resets += 1; print_debug_list(); - printk(KERN_WARNING "scsi%d: fas216_reset: ", info->host->host_no); + printk(KERN_WARNING "scsi%d: reset ", info->host->host_no); if (SCpnt) - printk(" for target %d ", SCpnt->target); + printk("for target %d ", SCpnt->target); + + printk("\n"); outb(info->scsi.cfg[3], REG_CNTL3(info)); @@ -1963,8 +2277,6 @@ int fas216_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags) SCpnt->scsi_done(SCpnt); } - printk("\n"); - return result | SCSI_RESET_SUCCESS; } @@ -2083,6 +2395,49 @@ int fas216_release(struct Scsi_Host *instance) return 0; } +int fas216_print_stats(FAS216_Info *info, char *buffer) +{ + return sprintf(buffer, + "Queued commands: %-10u Issued commands: %-10u\n" + "Done commands : %-10u Reads : %-10u\n" + "Writes : %-10u Others : %-10u\n" + "Disconnects : %-10u Aborts : %-10u\n" + "Resets : %-10u\n", + info->stats.queues, info->stats.removes, + info->stats.fins, info->stats.reads, + info->stats.writes, info->stats.miscs, + info->stats.disconnects, info->stats.aborts, + info->stats.resets); +} + +int fas216_print_device(FAS216_Info *info, Scsi_Device *scd, char *buffer) +{ + struct fas216_device *dev = &info->device[scd->id]; + int len = 0; + char *p; + + proc_print_scsidevice(scd, buffer, &len, 0); + p = buffer + len; + + p += sprintf(p, " Extensions: "); + + if (scd->tagged_supported) + p += sprintf(p, "TAG %sabled [%d] ", + scd->tagged_queue ? "en" : "dis", + scd->current_tag); + + p += sprintf(p, "\n Transfers : %d-bit ", + 8 << dev->wide_xfer); + + if (dev->sof) + p += sprintf(p, "sync offset %d, %d ns\n", + dev->sof, dev->period * 4); + else + p += sprintf(p, "async\n"); + + return p - buffer; +} + EXPORT_SYMBOL(fas216_init); EXPORT_SYMBOL(fas216_abort); EXPORT_SYMBOL(fas216_reset); @@ -2094,7 +2449,8 @@ EXPORT_SYMBOL(fas216_eh_abort); EXPORT_SYMBOL(fas216_eh_device_reset); EXPORT_SYMBOL(fas216_eh_bus_reset); EXPORT_SYMBOL(fas216_eh_host_reset); - +EXPORT_SYMBOL(fas216_print_stats); +EXPORT_SYMBOL(fas216_print_device); #ifdef MODULE int init_module(void) diff --git a/drivers/acorn/scsi/fas216.h b/drivers/acorn/scsi/fas216.h index 6518fbb42..351666638 100644 --- a/drivers/acorn/scsi/fas216.h +++ b/drivers/acorn/scsi/fas216.h @@ -40,6 +40,7 @@ #define CMD_TRANSFERINFO 0x10 #define CMD_INITCMDCOMPLETE 0x11 #define CMD_MSGACCEPTED 0x12 +#define CMD_PADBYTES 0x18 #define CMD_SETATN 0x1a #define CMD_RSETATN 0x1b @@ -171,15 +172,17 @@ typedef enum { PHASE_IDLE, /* we're not planning on doing anything */ PHASE_SELECTION, /* selecting a device */ + PHASE_SELSTEPS, /* selection with command steps */ + PHASE_COMMAND, /* command sent */ PHASE_MESSAGESENT, /* selected, and we're sending cmd */ PHASE_RECONNECTED, /* reconnected */ PHASE_DATAOUT, /* data out to device */ PHASE_DATAIN, /* data in from device */ PHASE_MSGIN, /* message in from device */ - PHASE_MSGOUT, /* message out to device */ - PHASE_AFTERMSGOUT, /* after message out phase */ + PHASE_MSGIN_DISCONNECT, /* disconnecting from bus */ + PHASE_MSGOUT, /* after message out phase */ + PHASE_MSGOUT_EXPECT, /* expecting message out */ PHASE_STATUS, /* status from device */ - PHASE_DISCONNECT, /* disconnecting from bus */ PHASE_DONE /* Command complete */ } phase_t; @@ -197,13 +200,15 @@ typedef enum { } fasdmatype_t; typedef enum { - syncneg_start, /* Negociate with device for Sync xfers */ - syncneg_sent, /* Sync Xfer negociation sent */ - syncneg_complete, /* Sync Xfer complete */ - syncneg_invalid /* Sync Xfer not supported */ -} syncneg_t; + neg_wait, /* Negociate with device */ + neg_inprogress, /* Negociation sent */ + neg_complete, /* Negociation complete */ + neg_targcomplete, /* Target completed negociation */ + neg_invalid /* Negociation not supported */ +} neg_t; #define MAGIC 0x441296bdUL +#define NR_MSGS 8 typedef struct { unsigned long magic_start; @@ -231,7 +236,7 @@ typedef struct { MsgQueue_t msgs; /* message queue for connected device */ unsigned int async_stp; /* Async transfer STP value */ - unsigned short last_message; /* last message to be sent */ + unsigned char msgin_fifo; /* bytes in fifo at time of message in */ unsigned char disconnectable:1; /* this command can be disconnected */ unsigned char aborting:1; /* aborting command */ @@ -255,6 +260,7 @@ typedef struct { unsigned char clockrate; /* clock rate of FAS device (MHz) */ unsigned char select_timeout; /* timeout (R5) */ unsigned char sync_max_depth; /* Synchronous xfer max fifo depth */ + unsigned char wide_max_size; /* Maximum wide transfer size */ unsigned char cntl3; /* Control Reg 3 */ unsigned int asyncperiod; /* Async transfer period (ns) */ unsigned int disconnect_ok:1; /* Disconnects allowed? */ @@ -267,12 +273,14 @@ typedef struct { } queues; /* per-device info */ - struct { + struct fas216_device { unsigned char disconnect_ok:1; /* device can disconnect */ - unsigned int period; /* sync xfer period (*4ns) */ + unsigned char period; /* sync xfer period in (*4ns) */ unsigned char stp; /* synchronous transfer period */ unsigned char sof; /* synchronous offset register */ - syncneg_t negstate; /* synchronous transfer mode */ + unsigned char wide_xfer; /* currently negociated wide transfer */ + neg_t sync_state; /* synchronous transfer mode */ + neg_t wide_state; /* wide transfer mode */ } device[8]; unsigned char busyluns[8]; /* array of bits indicating LUNs busy */ @@ -340,6 +348,9 @@ extern void fas216_intr (struct Scsi_Host *instance); */ extern int fas216_release (struct Scsi_Host *instance); +extern int fas216_print_stats(FAS216_Info *info, char *buffer); +extern int fas216_print_device(FAS216_Info *info, Scsi_Device *scd, char *buffer); + /* Function: int fas216_eh_abort(Scsi_Cmnd *SCpnt) * Purpose : abort this command * Params : SCpnt - command to abort diff --git a/drivers/acorn/scsi/msgqueue.c b/drivers/acorn/scsi/msgqueue.c index 7621b4d2b..f28784496 100644 --- a/drivers/acorn/scsi/msgqueue.c +++ b/drivers/acorn/scsi/msgqueue.c @@ -83,45 +83,25 @@ int msgqueue_msglength(MsgQueue_t *msgq) int length = 0; for (mq = msgq->qe; mq; mq = mq->next) - length += mq->length; + length += mq->msg.length; return length; } /* - * Function: char *msgqueue_getnextmsg(MsgQueue_t *msgq, int *length) - * Purpose : return a message & its length + * Function: struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno) + * Purpose : return a message * Params : msgq - queue to obtain message from - * length - pointer to int for message length + * : msgno - message number * Returns : pointer to message string, or NULL */ -char *msgqueue_getnextmsg(MsgQueue_t *msgq, int *length) +struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno) { struct msgqueue_entry *mq; - if ((mq = msgq->qe) != NULL) { - msgq->qe = mq->next; - mqe_free(msgq, mq); - *length = mq->length; - } - - return mq ? mq->msg : NULL; -} - -/* - * Function: char *msgqueue_peeknextmsg(MsgQueue_t *msgq, int *length) - * Purpose : return next message & length without removing it from the list - * Params : msgq - queue to obtain message from - * : length - pointer to int for message length - * Returns : pointer to message string, or NULL - */ -char *msgqueue_peeknextmsg(MsgQueue_t *msgq, int *length) -{ - struct msgqueue_entry *mq = msgq->qe; - - *length = mq ? mq->length : 0; + for (mq = msgq->qe; mq && msgno; mq = mq->next, msgno--); - return mq ? mq->msg : NULL; + return mq ? &mq->msg : NULL; } /* @@ -143,10 +123,11 @@ int msgqueue_addmsg(MsgQueue_t *msgq, int length, ...) va_start(ap, length); for (i = 0; i < length; i++) - mq->msg[i] = va_arg(ap, unsigned char); + mq->msg.msg[i] = va_arg(ap, unsigned char); va_end(ap); - mq->length = length; + mq->msg.length = length; + mq->msg.fifo = 0; mq->next = NULL; mqp = &msgq->qe; @@ -178,8 +159,7 @@ void msgqueue_flush(MsgQueue_t *msgq) EXPORT_SYMBOL(msgqueue_initialise); EXPORT_SYMBOL(msgqueue_free); EXPORT_SYMBOL(msgqueue_msglength); -EXPORT_SYMBOL(msgqueue_getnextmsg); -EXPORT_SYMBOL(msgqueue_peeknextmsg); +EXPORT_SYMBOL(msgqueue_getmsg); EXPORT_SYMBOL(msgqueue_addmsg); EXPORT_SYMBOL(msgqueue_flush); diff --git a/drivers/acorn/scsi/msgqueue.h b/drivers/acorn/scsi/msgqueue.h index cca30255c..8016dcf4e 100644 --- a/drivers/acorn/scsi/msgqueue.h +++ b/drivers/acorn/scsi/msgqueue.h @@ -6,9 +6,14 @@ #ifndef MSGQUEUE_H #define MSGQUEUE_H -struct msgqueue_entry { +struct message { char msg[8]; int length; + int fifo; +}; + +struct msgqueue_entry { + struct message msg; struct msgqueue_entry *next; }; @@ -21,60 +26,51 @@ typedef struct { } MsgQueue_t; /* - * Function: void msgqueue_initialise (MsgQueue_t *msgq) + * Function: void msgqueue_initialise(MsgQueue_t *msgq) * Purpose : initialise a message queue * Params : msgq - queue to initialise */ -extern void msgqueue_initialise (MsgQueue_t *msgq); +extern void msgqueue_initialise(MsgQueue_t *msgq); /* - * Function: void msgqueue_free (MsgQueue_t *msgq) + * Function: void msgqueue_free(MsgQueue_t *msgq) * Purpose : free a queue * Params : msgq - queue to free */ -extern void msgqueue_free (MsgQueue_t *msgq); +extern void msgqueue_free(MsgQueue_t *msgq); /* - * Function: int msgqueue_msglength (MsgQueue_t *msgq) + * Function: int msgqueue_msglength(MsgQueue_t *msgq) * Purpose : calculate the total length of all messages on the message queue * Params : msgq - queue to examine * Returns : number of bytes of messages in queue */ -extern int msgqueue_msglength (MsgQueue_t *msgq); +extern int msgqueue_msglength(MsgQueue_t *msgq); /* - * Function: char *msgqueue_getnextmsg (MsgQueue_t *msgq, int *length) + * Function: struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno) * Purpose : return a message & its length * Params : msgq - queue to obtain message from - * length - pointer to int for message length - * Returns : pointer to message string, or NULL - */ -extern char *msgqueue_getnextmsg (MsgQueue_t *msgq, int *length); - -/* - * Function: char *msgqueue_peeknextmsg(MsgQueue_t *msgq, int *length) - * Purpose : return next message & length without removing it from the list - * Params : msgq - queue to obtain message from - * : length - pointer to int for message length + * : msgno - message number * Returns : pointer to message string, or NULL */ -extern char *msgqueue_peeknextmsg(MsgQueue_t *msgq, int *length); +extern struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno); /* - * Function: int msgqueue_addmsg (MsgQueue_t *msgq, int length, ...) + * Function: int msgqueue_addmsg(MsgQueue_t *msgq, int length, ...) * Purpose : add a message onto a message queue * Params : msgq - queue to add message on * length - length of message * ... - message bytes * Returns : != 0 if successful */ -extern int msgqueue_addmsg (MsgQueue_t *msgq, int length, ...); +extern int msgqueue_addmsg(MsgQueue_t *msgq, int length, ...); /* - * Function: void msgqueue_flush (MsgQueue_t *msgq) + * Function: void msgqueue_flush(MsgQueue_t *msgq) * Purpose : flush all messages from message queue * Params : msgq - queue to flush */ -extern void msgqueue_flush (MsgQueue_t *msgq); +extern void msgqueue_flush(MsgQueue_t *msgq); #endif diff --git a/drivers/acorn/scsi/powertec.c b/drivers/acorn/scsi/powertec.c index caae8a8ba..95dbc3050 100644 --- a/drivers/acorn/scsi/powertec.c +++ b/drivers/acorn/scsi/powertec.c @@ -114,6 +114,8 @@ static const expansioncard_ops_t powertecscsi_ops = { powertecscsi_irqenable, powertecscsi_irqdisable, NULL, + NULL, + NULL, NULL }; @@ -271,8 +273,9 @@ powertecscsi_detect(Scsi_Host_Template *tpnt) info->info.ifcfg.select_timeout = 255; info->info.ifcfg.asyncperiod = POWERTEC_ASYNC_PERIOD; info->info.ifcfg.sync_max_depth = POWERTEC_SYNC_DEPTH; - info->info.ifcfg.cntl3 = CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK; + info->info.ifcfg.cntl3 = /*CNTL3_BS8 |*/ CNTL3_FASTSCSI | CNTL3_FASTCLK; info->info.ifcfg.disconnect_ok = 1; + info->info.ifcfg.wide_max_size = 0; info->info.dma.setup = powertecscsi_dma_setup; info->info.dma.pseudo = NULL; info->info.dma.stop = powertecscsi_dma_stop; @@ -443,31 +446,12 @@ int powertecscsi_proc_info(char *buffer, char **start, off_t offset, host->io_port, host->irq, host->dma_channel, info->info.scsi.type, info->control.terms ? "on" : "off"); - pos += sprintf(buffer+pos, - "Queued commands: %-10u Issued commands: %-10u\n" - "Done commands : %-10u Reads : %-10u\n" - "Writes : %-10u Others : %-10u\n" - "Disconnects : %-10u Aborts : %-10u\n" - "Resets : %-10u\n", - info->info.stats.queues, info->info.stats.removes, - info->info.stats.fins, info->info.stats.reads, - info->info.stats.writes, info->info.stats.miscs, - info->info.stats.disconnects, info->info.stats.aborts, - info->info.stats.resets); + pos += fas216_print_stats(&info->info, buffer + pos); - pos += sprintf (buffer+pos, "\nAttached devices:%s\n", host->host_queue ? "" : " none"); + pos += sprintf (buffer+pos, "\nAttached devices:\n"); for (scd = host->host_queue; scd; scd = scd->next) { - int len; - - proc_print_scsidevice (scd, buffer, &len, pos); - pos += len; - pos += sprintf (buffer+pos, "Extensions: "); - if (scd->tagged_supported) - pos += sprintf (buffer+pos, "TAG %sabled [%d] ", - scd->tagged_queue ? "en" : "dis", - scd->current_tag); - pos += sprintf (buffer+pos, "\n"); + pos += fas216_print_device(&info->info, scd, buffer + pos); if (pos + begin < offset) { begin += pos; diff --git a/drivers/acorn/scsi/queue.c b/drivers/acorn/scsi/queue.c index b8f5ba0ad..31e9d3dfc 100644 --- a/drivers/acorn/scsi/queue.c +++ b/drivers/acorn/scsi/queue.c @@ -55,6 +55,7 @@ int queue_initialise (Queue_t *queue) q->magic = QUEUE_MAGIC_FREE; q->SCpnt = NULL; } + q -= 1; q->next = NULL; } diff --git a/drivers/block/Config.in b/drivers/block/Config.in index 47fc9eac1..a9b9a000f 100644 --- a/drivers/block/Config.in +++ b/drivers/block/Config.in @@ -38,16 +38,16 @@ else bool ' RZ1000 chipset bugfix/support' CONFIG_BLK_DEV_RZ1000 bool ' Generic PCI IDE chipset support' CONFIG_BLK_DEV_IDEPCI if [ "$CONFIG_BLK_DEV_IDEPCI" = "y" ]; then - bool ' Generic PCI bus-master DMA support' CONFIG_BLK_DEV_IDEDMA + bool ' Generic PCI bus-master DMA support' CONFIG_BLK_DEV_IDEDMA_PCI + if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" ]; then + bool ' Use PCI DMA by default when available' CONFIG_IDEDMA_PCI_AUTO + fi bool ' Boot off-board chipsets first support' CONFIG_BLK_DEV_OFFBOARD bool ' AEC6210 chipset support' CONFIG_BLK_DEV_AEC6210 - if [ "$CONFIG_BLK_DEV_IDEDMA" = "y" ]; then - bool ' Use DMA by default when available' CONFIG_IDEDMA_AUTO - fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then bool ' OPTi 82C621 chipset enhanced support (EXPERIMENTAL)' CONFIG_BLK_DEV_OPTI621 bool ' Intel PIIXn chipsets support (EXPERIMENTAL)' CONFIG_BLK_DEV_PIIX - if [ "$CONFIG_BLK_DEV_IDEDMA" = "y" ]; then + if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" ]; then bool ' Tekram TRM290 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_TRM290 bool ' NS87415 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_NS87415 bool ' VIA82C586 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_VIA82C586 @@ -66,7 +66,7 @@ else fi fi fi - if [ "$CONFIG_PPC" = "y" ]; then + if [ "$CONFIG_PPC" = "y" -o "$CONFIG_ARM" = "y" ]; then bool ' Winbond SL82c105 support' CONFIG_BLK_DEV_SL82C105 fi fi @@ -75,11 +75,25 @@ else if [ "$CONFIG_BLK_DEV_IDE_PMAC" != "n" ]; then bool ' PowerMac IDE DMA support' CONFIG_BLK_DEV_IDEDMA_PMAC if [ "$CONFIG_BLK_DEV_IDEDMA_PMAC" = "y" ]; then - define_bool CONFIG_BLK_DEV_IDEDMA y bool ' Use DMA by default' CONFIG_PMAC_IDEDMA_AUTO fi fi fi + if [ "$CONFIG_ARCH_ACORN" = "y" ]; then + bool ' ICS IDE interface support' CONFIG_BLK_DEV_IDE_ICSIDE + if [ "$CONFIG_BLK_DEV_IDE_ICSIDE" = "y" ]; then + bool ' ICS DMA support' CONFIG_BLK_DEV_IDEDMA_ICS + if [ "$CONFIG_BLK_DEV_IDEDMA_ICS" = "y" ]; then + bool ' Use ICS DMA by default' CONFIG_IDEDMA_ICS_AUTO + fi + fi + bool ' RapIDE interface support' CONFIG_BLK_DEV_IDE_RAPIDE + fi + if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" -o \ + "$CONFIG_BLK_DEV_IDEDMA_PMAC" = "y" -o \ + "$CONFIG_BLK_DEV_IDEDMA_ICS" = "y" ]; then + define_bool CONFIG_BLK_DEV_IDEDMA y + fi bool ' Other IDE chipset support' CONFIG_IDE_CHIPSETS if [ "$CONFIG_IDE_CHIPSETS" = "y" ]; then comment 'Note: most of these also require special kernel boot parameters' @@ -170,7 +184,8 @@ if [ "$CONFIG_BLK_DEV_CMD640" = "y" -o \ "$CONFIG_BLK_DEV_IDE_PMAC" = "y" -o \ "$CONFIG_BLK_DEV_CY82C693" = "y" -o \ "$CONFIG_BLK_DEV_HPT343" = "y" -o \ - "$CONFIG_BLK_DEV_PIIX" = "y" ]; then + "$CONFIG_BLK_DEV_PIIX" = "y" -o \ + "$CONFIG_BLK_DEV_SL82C105" = "y" ]; then define_bool CONFIG_BLK_DEV_IDE_MODES y else define_bool CONFIG_BLK_DEV_IDE_MODES n diff --git a/drivers/block/icside.c b/drivers/block/icside.c new file mode 100644 index 000000000..299bce7a6 --- /dev/null +++ b/drivers/block/icside.c @@ -0,0 +1,634 @@ +/* + * linux/drivers/block/icside.c + * + * Copyright (c) 1996,1997 Russell King. + * + * Changelog: + * 08-Jun-1996 RMK Created + * 12-Sep-1997 RMK Added interrupt enable/disable + * 17-Apr-1999 RMK Added support for V6 EASI + * 22-May-1999 RMK Added support for V6 DMA + */ + +#include <linux/string.h> +#include <linux/module.h> +#include <linux/ioport.h> +#include <linux/malloc.h> +#include <linux/blkdev.h> +#include <linux/errno.h> +#include <linux/hdreg.h> +#include <linux/ide.h> + +#include <asm/dma.h> +#include <asm/ecard.h> +#include <asm/io.h> + +/* + * Maximum number of interfaces per card + */ +#define MAX_IFS 2 + +#define ICS_IDENT_OFFSET 0x8a0 + +#define ICS_ARCIN_V5_INTRSTAT 0x000 +#define ICS_ARCIN_V5_INTROFFSET 0x001 +#define ICS_ARCIN_V5_IDEOFFSET 0xa00 +#define ICS_ARCIN_V5_IDEALTOFFSET 0xae0 +#define ICS_ARCIN_V5_IDESTEPPING 4 + +#define ICS_ARCIN_V6_IDEOFFSET_1 0x800 +#define ICS_ARCIN_V6_INTROFFSET_1 0x880 +#define ICS_ARCIN_V6_INTRSTAT_1 0x8a4 +#define ICS_ARCIN_V6_IDEALTOFFSET_1 0x8e0 +#define ICS_ARCIN_V6_IDEOFFSET_2 0xc00 +#define ICS_ARCIN_V6_INTROFFSET_2 0xc80 +#define ICS_ARCIN_V6_INTRSTAT_2 0xca4 +#define ICS_ARCIN_V6_IDEALTOFFSET_2 0xce0 +#define ICS_ARCIN_V6_IDESTEPPING 4 + +struct cardinfo { + unsigned int dataoffset; + unsigned int ctrloffset; + unsigned int stepping; +}; + +static struct cardinfo icside_cardinfo_v5 = { + ICS_ARCIN_V5_IDEOFFSET, + ICS_ARCIN_V5_IDEALTOFFSET, + ICS_ARCIN_V5_IDESTEPPING +}; + +static struct cardinfo icside_cardinfo_v6_1 = { + ICS_ARCIN_V6_IDEOFFSET_1, + ICS_ARCIN_V6_IDEALTOFFSET_1, + ICS_ARCIN_V6_IDESTEPPING +}; + +static struct cardinfo icside_cardinfo_v6_2 = { + ICS_ARCIN_V6_IDEOFFSET_2, + ICS_ARCIN_V6_IDEALTOFFSET_2, + ICS_ARCIN_V6_IDESTEPPING +}; + +static const card_ids icside_cids[] = { + { MANU_ICS, PROD_ICS_IDE }, + { MANU_ICS2, PROD_ICS2_IDE }, + { 0xffff, 0xffff } +}; + +typedef enum { + ics_if_unknown, + ics_if_arcin_v5, + ics_if_arcin_v6 +} iftype_t; + +/* ---------------- Version 5 PCB Support Functions --------------------- */ +/* Prototype: icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr) + * Purpose : enable interrupts from card + */ +static void icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr) +{ + unsigned int memc_port = (unsigned int)ec->irq_data; + outb (0, memc_port + ICS_ARCIN_V5_INTROFFSET); +} + +/* Prototype: icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr) + * Purpose : disable interrupts from card + */ +static void icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr) +{ + unsigned int memc_port = (unsigned int)ec->irq_data; + inb (memc_port + ICS_ARCIN_V5_INTROFFSET); +} + +static const expansioncard_ops_t icside_ops_arcin_v5 = { + icside_irqenable_arcin_v5, + icside_irqdisable_arcin_v5, + NULL, + NULL, + NULL, + NULL +}; + + +/* ---------------- Version 6 PCB Support Functions --------------------- */ +/* Prototype: icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr) + * Purpose : enable interrupts from card + */ +static void icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr) +{ + unsigned int ide_base_port = (unsigned int)ec->irq_data; + + outb (0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_1); + outb (0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_2); +} + +/* Prototype: icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr) + * Purpose : disable interrupts from card + */ +static void icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr) +{ + unsigned int ide_base_port = (unsigned int)ec->irq_data; + + inb (ide_base_port + ICS_ARCIN_V6_INTROFFSET_1); + inb (ide_base_port + ICS_ARCIN_V6_INTROFFSET_2); +} + +/* Prototype: icside_irqprobe(struct expansion_card *ec) + * Purpose : detect an active interrupt from card + */ +static int icside_irqpending_arcin_v6(struct expansion_card *ec) +{ + unsigned int ide_base_port = (unsigned int)ec->irq_data; + + return inb(ide_base_port + ICS_ARCIN_V6_INTRSTAT_1) & 1 || + inb(ide_base_port + ICS_ARCIN_V6_INTRSTAT_2) & 1; +} + +static const expansioncard_ops_t icside_ops_arcin_v6 = { + icside_irqenable_arcin_v6, + icside_irqdisable_arcin_v6, + icside_irqpending_arcin_v6, + NULL, + NULL, + NULL +}; + +/* Prototype: icside_identifyif (struct expansion_card *ec) + * Purpose : identify IDE interface type + * Notes : checks the description string + */ +static iftype_t icside_identifyif (struct expansion_card *ec) +{ + unsigned int addr; + iftype_t iftype; + int id = 0; + + iftype = ics_if_unknown; + + addr = ecard_address (ec, ECARD_IOC, ECARD_FAST) + ICS_IDENT_OFFSET; + + id = inb (addr) & 1; + id |= (inb (addr + 1) & 1) << 1; + id |= (inb (addr + 2) & 1) << 2; + id |= (inb (addr + 3) & 1) << 3; + + switch (id) { + case 0: /* A3IN */ + printk("icside: A3IN unsupported\n"); + break; + + case 1: /* A3USER */ + printk("icside: A3USER unsupported\n"); + break; + + case 3: /* ARCIN V6 */ + printk(KERN_DEBUG "icside: detected ARCIN V6 in slot %d\n", ec->slot_no); + iftype = ics_if_arcin_v6; + break; + + case 15:/* ARCIN V5 (no id) */ + printk(KERN_DEBUG "icside: detected ARCIN V5 in slot %d\n", ec->slot_no); + iftype = ics_if_arcin_v5; + break; + + default:/* we don't know - complain very loudly */ + printk("icside: ***********************************\n"); + printk("icside: *** UNKNOWN ICS INTERFACE id=%d ***\n", id); + printk("icside: ***********************************\n"); + printk("icside: please report this to linux@arm.linux.org.uk\n"); + printk("icside: defaulting to ARCIN V5\n"); + iftype = ics_if_arcin_v5; + break; + } + + return iftype; +} + +#ifdef CONFIG_BLK_DEV_IDEDMA_ICS +/* + * SG-DMA support. + * + * Similar to the BM-DMA, but we use the RiscPCs IOMD + * DMA controllers. There is only one DMA controller + * per card, which means that only one drive can be + * accessed at one time. NOTE! We do not inforce that + * here, but we rely on the main IDE driver spotting + * that both interfaces use the same IRQ, which should + * guarantee this. + * + * We are limited by the drives IOR/IOW pulse time. + * The closest that we can get to the requirements is + * a type C cycle for both mode 1 and mode 2. However, + * this does give a burst of 8MB/s. + * + * This has been tested with a couple of Conner + * Peripherals 1080MB CFS1081A drives, one on each + * interface, which deliver about 2MB/s each. I + * believe that this is limited by the lack of + * on-board drive cache. + */ +#define TABLE_SIZE 2048 + +static int +icside_build_dmatable(ide_drive_t *drive, int reading) +{ + struct request *rq = HWGROUP(drive)->rq; + struct buffer_head *bh = rq->bh; + unsigned long addr, size; + unsigned char *virt_addr; + unsigned int count = 0; + dmasg_t *sg = (dmasg_t *)HWIF(drive)->dmatable; + + do { + if (bh == NULL) { + /* paging requests have (rq->bh == NULL) */ + virt_addr = rq->buffer; + addr = virt_to_bus (virt_addr); + size = rq->nr_sectors << 9; + } else { + /* group sequential buffers into one large buffer */ + virt_addr = bh->b_data; + addr = virt_to_bus (virt_addr); + size = bh->b_size; + while ((bh = bh->b_reqnext) != NULL) { + if ((addr + size) != virt_to_bus (bh->b_data)) + break; + size += bh->b_size; + } + } + + if (addr & 3) { + printk("%s: misaligned DMA buffer\n", drive->name); + return 0; + } + + if (size) { + if (reading) + dma_cache_inv((unsigned int)virt_addr, size); + else + dma_cache_wback((unsigned int)virt_addr, size); + } + + sg[count].address = addr; + sg[count].length = size; + if (++count >= (TABLE_SIZE / sizeof(dmasg_t))) { + printk("%s: DMA table too small\n", drive->name); + return 0; + } + } while (bh != NULL); + + if (!count) + printk("%s: empty DMA table?\n", drive->name); + + return count; +} + +static int +icside_config_drive(ide_drive_t *drive, int mode) +{ + ide_hwif_t *hwif = HWIF(drive); + int speed, err; + + if (mode == 2) { + speed = XFER_MW_DMA_2; + drive->drive_data = 250; + } else { + speed = XFER_MW_DMA_1; + drive->drive_data = 250; + } + + /* + * Don't use ide_wait_cmd here - it will + * attempt to set_geometry and recalibrate, + * but for some reason these don't work at + * this point (lost interrupt). + */ + SELECT_DRIVE(hwif, drive); + OUT_BYTE(drive->ctl | 2, IDE_CONTROL_REG); + OUT_BYTE(speed, IDE_NSECTOR_REG); + OUT_BYTE(SETFEATURES_XFER, IDE_FEATURE_REG); + OUT_BYTE(WIN_SETFEATURES, IDE_COMMAND_REG); + + err = ide_wait_stat(drive, DRIVE_READY, + BUSY_STAT|DRQ_STAT|ERR_STAT, WAIT_CMD); + + if (err == 0) { + drive->id->dma_mword &= 0x00ff; + drive->id->dma_mword |= 256 << mode; + } else + drive->drive_data = 0; + + return err; +} + +static int +icside_dma_check(ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + ide_hwif_t *hwif = HWIF(drive); + int autodma = hwif->autodma; + + if (id && (id->capability & 1) && autodma) { + int dma_mode = 0; + + /* Consult the list of known "bad" drives */ + if (ide_dmaproc(ide_dma_bad_drive, drive)) + return hwif->dmaproc(ide_dma_off, drive); + + /* Enable DMA on any drive that has + * UltraDMA (mode 0/1/2) enabled + */ + if (id->field_valid & 4 && id->dma_ultra & 7) + dma_mode = 2; + + /* Enable DMA on any drive that has mode1 + * or mode2 multiword DMA enabled + */ + if (id->field_valid & 2 && id->dma_mword & 6) + dma_mode = id->dma_mword & 4 ? 2 : 1; + + /* Consult the list of known "good" drives */ + if (ide_dmaproc(ide_dma_good_drive, drive)) + dma_mode = 1; + + if (dma_mode && icside_config_drive(drive, dma_mode) == 0) + return hwif->dmaproc(ide_dma_on, drive); + } + return hwif->dmaproc(ide_dma_off_quietly, drive); +} + +static int +icside_dmaproc(ide_dma_action_t func, ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + int count, reading = 0; + + switch (func) { + case ide_dma_check: + return icside_dma_check(drive); + + case ide_dma_read: + reading = 1; + case ide_dma_write: + count = icside_build_dmatable(drive, reading); + if (!count) + return 1; + disable_dma(hwif->hw.dma); + + /* Route the DMA signals to + * to the correct interface. + */ + outb(hwif->select_data, hwif->config_data); + + /* Select the correct timing + * for this drive + */ + set_dma_speed(hwif->hw.dma, drive->drive_data); + + set_dma_sg(hwif->hw.dma, (dmasg_t *)hwif->dmatable, count); + set_dma_mode(hwif->hw.dma, reading ? DMA_MODE_READ + : DMA_MODE_WRITE); + + drive->waiting_for_dma = 1; + if (drive->media != ide_disk) + return 0; + + ide_set_handler(drive, &ide_dma_intr, WAIT_CMD); + OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, + IDE_COMMAND_REG); + + case ide_dma_begin: + enable_dma(hwif->hw.dma); + return 0; + + case ide_dma_end: + drive->waiting_for_dma = 0; + disable_dma(hwif->hw.dma); + return get_dma_residue(hwif->hw.dma) != 0; + + case ide_dma_test_irq: + return inb((unsigned long)hwif->hw.priv) & 1; + + default: + return ide_dmaproc(func, drive); + } +} + +static unsigned long +icside_alloc_dmatable(void) +{ + static unsigned long dmatable; + static unsigned int leftover; + unsigned long table; + + if (leftover < TABLE_SIZE) { +#if PAGE_SIZE == TABLE_SIZE * 2 + dmatable = __get_free_pages(GFP_KERNEL, 1); + leftover = PAGE_SIZE; +#else + dmatable = kmalloc(TABLE_SIZE, GFP_KERNEL); + leftover = TABLE_SIZE; +#endif + } + + table = dmatable; + if (table) { + dmatable += TABLE_SIZE; + leftover -= TABLE_SIZE; + } + + return table; +} + +static int +icside_setup_dma(ide_hwif_t *hwif, int autodma) +{ + unsigned long table = icside_alloc_dmatable(); + + printk(" %s: SG-DMA", hwif->name); + + if (!table) + printk(" -- ERROR, unable to allocate DMA table\n"); + else { + hwif->dmatable = (void *)table; + hwif->dmaproc = icside_dmaproc; + hwif->autodma = autodma; + + printk(" capable%s\n", autodma ? + ", auto-enable" : ""); + } + + return hwif->dmatable != NULL; +} +#endif + +static ide_hwif_t * +icside_find_hwif(unsigned long dataport) +{ + ide_hwif_t *hwif; + int index; + + for (index = 0; index < MAX_HWIFS; ++index) { + hwif = &ide_hwifs[index]; + if (hwif->hw.io_ports[IDE_DATA_OFFSET] == (ide_ioreg_t)dataport) + goto found; + } + + for (index = 0; index < MAX_HWIFS; ++index) { + hwif = &ide_hwifs[index]; + if (!hwif->hw.io_ports[IDE_DATA_OFFSET]) + goto found; + } + + return NULL; +found: + return hwif; +} + +static ide_hwif_t * +icside_setup(unsigned long base, struct cardinfo *info, int irq) +{ + unsigned long port = base + info->dataoffset; + ide_hwif_t *hwif; + + hwif = icside_find_hwif(base); + if (hwif) { + int i; + + memset(&hwif->hw, 0, sizeof(hw_regs_t)); + + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { + hwif->hw.io_ports[i] = (ide_ioreg_t)port; + port += 1 << info->stepping; + } + hwif->hw.io_ports[IDE_CONTROL_OFFSET] = base + info->ctrloffset; + hwif->hw.irq = irq; + hwif->hw.dma = NO_DMA; + hwif->noprobe = 0; + hwif->chipset = ide_acorn; + } + + return hwif; +} + +static int icside_register_v5(struct expansion_card *ec, int autodma) +{ + unsigned long slot_port; + ide_hwif_t *hwif; + + slot_port = ecard_address(ec, ECARD_MEMC, 0); + + ec->irqaddr = (unsigned char *)ioaddr(slot_port + ICS_ARCIN_V5_INTRSTAT); + ec->irqmask = 1; + ec->irq_data = (void *)slot_port; + ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v5; + + /* + * Be on the safe side - disable interrupts + */ + inb(slot_port + ICS_ARCIN_V5_INTROFFSET); + + hwif = icside_setup(slot_port, &icside_cardinfo_v5, ec->irq); + + return hwif ? 0 : -1; +} + +static int icside_register_v6(struct expansion_card *ec, int autodma) +{ + unsigned long slot_port, port; + ide_hwif_t *hwif, *mate; + int sel = 0; + + slot_port = ecard_address(ec, ECARD_IOC, ECARD_FAST); + port = ecard_address(ec, ECARD_EASI, ECARD_FAST); + + if (port == 0) + port = slot_port; + else + sel = 1 << 5; + + outb(sel, slot_port); + + ec->irq_data = (void *)port; + ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v6; + + /* + * Be on the safe side - disable interrupts + */ + inb(port + ICS_ARCIN_V6_INTROFFSET_1); + inb(port + ICS_ARCIN_V6_INTROFFSET_2); + + hwif = icside_setup(port, &icside_cardinfo_v6_1, ec->irq); + mate = icside_setup(port, &icside_cardinfo_v6_2, ec->irq); + +#ifdef CONFIG_BLK_DEV_IDEDMA_ICS + if (ec->dma != NO_DMA) { + if (request_dma(ec->dma, hwif->name)) + goto no_dma; + + if (hwif) { + hwif->config_data = slot_port; + hwif->select_data = sel; + hwif->hw.dma = ec->dma; + hwif->hw.priv = (void *) + (port + ICS_ARCIN_V6_INTRSTAT_1); + hwif->channel = 0; + icside_setup_dma(hwif, autodma); + } + if (mate) { + mate->config_data = slot_port; + mate->select_data = sel | 1; + mate->hw.dma = ec->dma; + mate->hw.priv = (void *) + (port + ICS_ARCIN_V6_INTRSTAT_2); + mate->channel = 1; + icside_setup_dma(mate, autodma); + } + } +#endif + +no_dma: + return hwif || mate ? 0 : -1; +} + +int icside_init(void) +{ + int autodma = 0; + +#ifdef CONFIG_IDEDMA_ICS_AUTO + autodma = 1; +#endif + + ecard_startfind (); + + do { + struct expansion_card *ec; + int result; + + ec = ecard_find(0, icside_cids); + if (ec == NULL) + break; + + ecard_claim(ec); + + switch (icside_identifyif(ec)) { + case ics_if_arcin_v5: + result = icside_register_v5(ec, autodma); + break; + + case ics_if_arcin_v6: + result = icside_register_v6(ec, autodma); + break; + + default: + result = -1; + break; + } + + if (result) + ecard_release(ec); + } while (1); + + return 0; +} diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index 9b29f4f55..185591b3a 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -603,7 +603,7 @@ void ll_rw_block(int rw, int nr, struct buffer_head * bh[]) /* Verify requested block sizes. */ for (i = 0; i < nr; i++) { - if (bh[i] && bh[i]->b_size != correct_size) { + if (bh[i]->b_size != correct_size) { printk(KERN_NOTICE "ll_rw_block: device %s: " "only %d-char blocks implemented (%lu)\n", kdevname(bh[0]->b_dev), diff --git a/drivers/acorn/block/ide-rapide.c b/drivers/block/rapide.c index d142cebff..468f2e3b1 100644 --- a/drivers/acorn/block/ide-rapide.c +++ b/drivers/block/rapide.c @@ -12,10 +12,9 @@ #include <linux/malloc.h> #include <linux/blkdev.h> #include <linux/errno.h> -#include <asm/ecard.h> -#include <asm/ide.h> +#include <linux/ide.h> -#include "../../block/ide.h" +#include <asm/ecard.h> static const card_ids rapide_cids[] = { { MANU_YELLOWSTONE, PROD_YELLOWSTONE_RAPIDE32 }, @@ -28,14 +27,20 @@ static int result[MAX_ECARDS]; static inline int rapide_register(struct expansion_card *ec) { unsigned long port = ecard_address (ec, ECARD_MEMC, 0); - ide_ioregspec_t spec; + hw_regs_t hw; + + int i; + + memset(&hw, 0, sizeof(hw)); - spec.base = port; - spec.ctrl = port + 0x206; - spec.offset = 1 << 4; - spec.irq = ec->irq; + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { + hw.io_ports[i] = (ide_ioreg_t)port; + port += 1 << 4; + } + hw.io_ports[IDE_CONTROL_OFFSET] = port + 0x206; + hw.irq = ec->irq; - return ide_register_port(&spec); + return ide_register_hw(&hw, NULL); } int rapide_init(void) diff --git a/drivers/char/n_hdlc.c b/drivers/char/n_hdlc.c index 9319b777a..0dc5a7cca 100644 --- a/drivers/char/n_hdlc.c +++ b/drivers/char/n_hdlc.c @@ -9,6 +9,7 @@ * Al Longyear <longyear@netcom.com>, Paul Mackerras <Paul.Mackerras@cs.anu.edu.au> * * Original release 01/11/99 + * ==FILEDATE 19990524== * * This code is released under the GNU General Public License (GPL) * @@ -72,7 +73,7 @@ */ #define HDLC_MAGIC 0x239e -#define HDLC_VERSION "1.0" +#define HDLC_VERSION "1.2" #include <linux/version.h> #include <linux/config.h> @@ -813,6 +814,8 @@ static int n_hdlc_tty_ioctl (struct tty_struct *tty, struct file * file, { struct n_hdlc *n_hdlc = tty2n_hdlc (tty); int error = 0; + int count; + unsigned long flags; if (debuglevel >= DEBUG_LEVEL_INFO) printk("%s(%d)n_hdlc_tty_ioctl() called %d\n", @@ -824,21 +827,29 @@ static int n_hdlc_tty_ioctl (struct tty_struct *tty, struct file * file, switch (cmd) { case FIONREAD: - { - /* report count of read data available */ - /* in next available frame (if any) */ - int count; - unsigned long flags; - spin_lock_irqsave(&n_hdlc->rx_buf_list.spinlock,flags); - if (n_hdlc->rx_buf_list.head) - count = n_hdlc->rx_buf_list.head->count; - else - count = 0; - spin_unlock_irqrestore(&n_hdlc->rx_buf_list.spinlock,flags); - PUT_USER (error, count, (int *) arg); - } + /* report count of read data available */ + /* in next available frame (if any) */ + spin_lock_irqsave(&n_hdlc->rx_buf_list.spinlock,flags); + if (n_hdlc->rx_buf_list.head) + count = n_hdlc->rx_buf_list.head->count; + else + count = 0; + spin_unlock_irqrestore(&n_hdlc->rx_buf_list.spinlock,flags); + PUT_USER (error, count, (int *) arg); break; - + + case TIOCOUTQ: + /* get the pending tx byte count in the driver */ + count = tty->driver.chars_in_buffer ? + tty->driver.chars_in_buffer(tty) : 0; + /* add size of next output frame in queue */ + spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock,flags); + if (n_hdlc->tx_buf_list.head) + count += n_hdlc->tx_buf_list.head->count; + spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock,flags); + PUT_USER (error, count, (int*)arg); + break; + default: error = n_tty_ioctl (tty, file, cmd, arg); break; diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c index 75c8a68b9..3b6c7eacf 100644 --- a/drivers/char/synclink.c +++ b/drivers/char/synclink.c @@ -1,6 +1,8 @@ /* * linux/drivers/char/synclink.c * + * ==FILEDATE 19990610== + * * Device driver for Microgate SyncLink ISA and PCI * high speed multiprotocol serial adapters. * @@ -43,14 +45,15 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ - + #define VERSION(ver,rel,seq) (((ver)<<16) | ((rel)<<8) | (seq)) #define BREAKPOINT() asm(" int $3"); #define MAX_ISA_DEVICES 10 -#include <linux/config.h> +#include <linux/config.h> #include <linux/module.h> +#include <linux/version.h> #include <linux/errno.h> #include <linux/signal.h> #include <linux/sched.h> @@ -68,7 +71,7 @@ #include <linux/mm.h> #include <linux/malloc.h> -#if LINUX_VERSION_CODE >= VERSION(2,1,0) +#if LINUX_VERSION_CODE >= VERSION(2,1,0) #include <linux/vmalloc.h> #include <linux/init.h> #include <asm/serial.h> @@ -209,8 +212,21 @@ typedef struct _BH_EVENT { } BH_EVENT, *BH_QUEUE; /* Queue of BH actions to be done. */ #define MAX_BH_QUEUE_ENTRIES 200 +#define IO_PIN_SHUTDOWN_LIMIT (MAX_BH_QUEUE_ENTRIES/4) #define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) + +struct _input_signal_events { + int ri_up; + int ri_down; + int dsr_up; + int dsr_down; + int dcd_up; + int dcd_down; + int cts_up; + int cts_down; +}; + /* * Device instance data structure */ @@ -266,6 +282,11 @@ struct mgsl_struct { int bh_running; /* Protection from multiple */ int isr_overflow; int bh_requested; + + int dcd_chkcount; /* check counts to prevent */ + int cts_chkcount; /* too many IRQs if a signal */ + int dsr_chkcount; /* is floating */ + int ri_chkcount; char *buffer_list; /* virtual address of Rx & Tx buffer lists */ unsigned long buffer_list_phys; @@ -327,6 +348,11 @@ struct mgsl_struct { char flag_buf[HDLC_MAX_FRAME_SIZE]; char char_buf[HDLC_MAX_FRAME_SIZE]; BOOLEAN drop_rts_on_tx_done; + + BOOLEAN loopmode_insert_requested; + BOOLEAN loopmode_send_done_requested; + + struct _input_signal_events input_signal_events; }; #define MGSL_MAGIC 0x5401 @@ -712,6 +738,13 @@ void usc_loopback_frame( struct mgsl_struct *info ); void mgsl_tx_timeout(unsigned long context); + +void usc_loopmode_cancel_transmit( struct mgsl_struct * info ); +void usc_loopmode_insert_request( struct mgsl_struct * info ); +int usc_loopmode_active( struct mgsl_struct * info); +void usc_loopmode_send_done( struct mgsl_struct * info ); +int usc_loopmode_send_active( struct mgsl_struct * info ); + /* * Defines a BUS descriptor value for the PCI adapter * local bus address ranges. @@ -820,7 +853,8 @@ static int mgsl_set_txidle(struct mgsl_struct * info, int idle_mode); static int mgsl_txenable(struct mgsl_struct * info, int enable); static int mgsl_txabort(struct mgsl_struct * info); static int mgsl_rxenable(struct mgsl_struct * info, int enable); -static int mgsl_wait_event(struct mgsl_struct * info, int mask); +static int mgsl_wait_event(struct mgsl_struct * info, int * mask); +static int mgsl_loopmode_send_done( struct mgsl_struct * info ); #define jiffies_from_ms(a) ((((a) * HZ)/1000)+1) @@ -865,7 +899,7 @@ MODULE_PARM(debug_level,"i"); #endif static char *driver_name = "SyncLink serial driver"; -static char *driver_version = "1.00"; +static char *driver_version = "1.7"; static struct tty_driver serial_driver, callout_driver; static int serial_refcount; @@ -1001,6 +1035,7 @@ void mgsl_format_bh_queue( struct mgsl_struct *info ) /* As a safety measure, mark the end of the chain with a NULL */ info->free_bh_queue_tail->link = NULL; + info->isr_overflow=0; } /* end of mgsl_format_bh_queue() */ @@ -1092,6 +1127,14 @@ int mgsl_bh_queue_get( struct mgsl_struct *info ) spin_unlock_irqrestore(&info->irq_spinlock,flags); return 1; } + + if ( info->isr_overflow ) { + if (debug_level >= DEBUG_LEVEL_BH) + printk("ISR overflow cleared.\n"); + info->isr_overflow=0; + usc_EnableMasterIrqBit(info); + usc_EnableDmaInterrupts(info,DICR_MASTER); + } /* Mark BH routine as complete */ info->bh_running = 0; @@ -1155,10 +1198,6 @@ void mgsl_bh_handler(void* Context) } } - if ( info->isr_overflow ) { - printk("ISR overflow detected.\n"); - } - if ( debug_level >= DEBUG_LEVEL_BH ) printk( "%s(%d):mgsl_bh_handler(%s) exit\n", __FILE__,__LINE__,info->device_name); @@ -1199,6 +1238,7 @@ void mgsl_bh_receive_dma( struct mgsl_struct *info, unsigned short status ) void mgsl_bh_transmit_data( struct mgsl_struct *info, unsigned short Datacount ) { struct tty_struct *tty = info->tty; + unsigned long flags; if ( debug_level >= DEBUG_LEVEL_BH ) printk( "%s(%d):mgsl_bh_transmit_data() entry on %s\n", @@ -1215,7 +1255,15 @@ void mgsl_bh_transmit_data( struct mgsl_struct *info, unsigned short Datacount ) } wake_up_interruptible(&tty->write_wait); } - + + /* if transmitter idle and loopmode_send_done_requested + * then start echoing RxD to TxD + */ + spin_lock_irqsave(&info->irq_spinlock,flags); + if ( !info->tx_active && info->loopmode_send_done_requested ) + usc_loopmode_send_done( info ); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } /* End Of mgsl_bh_transmit_data() */ /* mgsl_bh_status_handler() @@ -1240,6 +1288,23 @@ void mgsl_bh_status_handler( struct mgsl_struct *info, unsigned short status ) printk( "%s(%d):mgsl_bh_status_handler() entry on %s\n", __FILE__,__LINE__,info->device_name); + if (status & MISCSTATUS_RI_LATCHED) { + if (info->ri_chkcount) + (info->ri_chkcount)--; + } + if (status & MISCSTATUS_DSR_LATCHED) { + if (info->dsr_chkcount) + (info->dsr_chkcount)--; + } + if (status & MISCSTATUS_DCD_LATCHED) { + if (info->dcd_chkcount) + (info->dcd_chkcount)--; + } + if (status & MISCSTATUS_CTS_LATCHED) { + if (info->cts_chkcount) + (info->cts_chkcount)--; + } + } /* End Of mgsl_bh_status_handler() */ /* mgsl_isr_receive_status() @@ -1259,8 +1324,21 @@ void mgsl_isr_receive_status( struct mgsl_struct *info ) printk("%s(%d):mgsl_isr_receive_status status=%04X\n", __FILE__,__LINE__,status); - usc_ClearIrqPendingBits( info, RECEIVE_STATUS ); - usc_UnlatchRxstatusBits( info, status ); + if ( (status & RXSTATUS_ABORT_RECEIVED) && + info->loopmode_insert_requested && + usc_loopmode_active(info) ) + { + ++info->icount.rxabort; + info->loopmode_insert_requested = FALSE; + + /* clear CMR:13 to start echoing RxD to TxD */ + info->cmr_value &= ~BIT13; + usc_OutReg(info, CMR, info->cmr_value); + + /* disable received abort irq (no longer required) */ + usc_OutReg(info, RICR, + (usc_InReg(info, RICR) & ~RXSTATUS_ABORT_RECEIVED)); + } if (status & (RXSTATUS_EXITED_HUNT + RXSTATUS_IDLE_RECEIVED)) { if (status & RXSTATUS_EXITED_HUNT) @@ -1278,6 +1356,9 @@ void mgsl_isr_receive_status( struct mgsl_struct *info ) usc_RTCmd( info, RTCmd_PurgeRxFifo ); } + usc_ClearIrqPendingBits( info, RECEIVE_STATUS ); + usc_UnlatchRxstatusBits( info, status ); + } /* end of mgsl_isr_receive_status() */ /* mgsl_isr_transmit_status() @@ -1300,7 +1381,7 @@ void mgsl_isr_transmit_status( struct mgsl_struct *info ) usc_ClearIrqPendingBits( info, TRANSMIT_STATUS ); usc_UnlatchTxstatusBits( info, status ); - + if ( status & TXSTATUS_EOF_SENT ) info->icount.txok++; else if ( status & TXSTATUS_UNDERRUN ) @@ -1356,12 +1437,32 @@ void mgsl_isr_io_pin( struct mgsl_struct *info ) MISCSTATUS_DSR_LATCHED | MISCSTATUS_RI_LATCHED) ) { icount = &info->icount; /* update input line counters */ - if (status & MISCSTATUS_RI_LATCHED) + if (status & MISCSTATUS_RI_LATCHED) { + if ((info->ri_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT) + usc_DisablestatusIrqs(info,SICR_RI); icount->rng++; - if (status & MISCSTATUS_DSR_LATCHED) + if ( status & MISCSTATUS_RI ) + info->input_signal_events.ri_up++; + else + info->input_signal_events.ri_down++; + } + if (status & MISCSTATUS_DSR_LATCHED) { + if ((info->dsr_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT) + usc_DisablestatusIrqs(info,SICR_DSR); icount->dsr++; + if ( status & MISCSTATUS_DSR ) + info->input_signal_events.dsr_up++; + else + info->input_signal_events.dsr_down++; + } if (status & MISCSTATUS_DCD_LATCHED) { + if ((info->dcd_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT) + usc_DisablestatusIrqs(info,SICR_DCD); icount->dcd++; + if ( status & MISCSTATUS_DCD ) + info->input_signal_events.dcd_up++; + else + info->input_signal_events.dcd_down++; #ifdef CONFIG_HARD_PPS if ((info->flags & ASYNC_HARDPPS_CD) && (status & MISCSTATUS_DCD_LATCHED)) @@ -1369,7 +1470,15 @@ void mgsl_isr_io_pin( struct mgsl_struct *info ) #endif } if (status & MISCSTATUS_CTS_LATCHED) + { + if ((info->cts_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT) + usc_DisablestatusIrqs(info,SICR_CTS); icount->cts++; + if ( status & MISCSTATUS_CTS ) + info->input_signal_events.cts_up++; + else + info->input_signal_events.cts_down++; + } wake_up_interruptible(&info->status_event_wait_q); wake_up_interruptible(&info->event_wait_q); @@ -1411,6 +1520,8 @@ void mgsl_isr_io_pin( struct mgsl_struct *info ) } } + mgsl_bh_queue_put(info, BH_TYPE_STATUS, status); + /* for diagnostics set IRQ flag */ if ( status & MISCSTATUS_TXC_LATCHED ){ usc_OutReg( info, SICR, @@ -1642,8 +1753,10 @@ void mgsl_isr_receive_dma( struct mgsl_struct *info ) /* Post a receive event for BH processing. */ mgsl_bh_queue_put( info, BH_TYPE_RECEIVE_DMA, status ); - if ( status & BIT3 ) + if ( status & BIT3 ) { info->rx_overflow = 1; + info->icount.buf_overrun++; + } } /* end of mgsl_isr_receive_dma() */ @@ -1696,9 +1809,9 @@ static void mgsl_interrupt(int irq, void *dev_id, struct pt_regs * regs) if ( info->isr_overflow ) { printk(KERN_ERR"%s(%d):%s isr overflow irq=%d\n", __FILE__,__LINE__,info->device_name, irq); - /* Interrupt overflow. Reset adapter and exit. */ -// UscReset(info); -// break; + usc_DisableMasterIrqBit(info); + usc_DisableDmaInterrupts(info,DICR_MASTER); + break; } } @@ -1980,6 +2093,11 @@ static void mgsl_change_params(struct mgsl_struct *info) usc_set_async_mode(info); usc_set_serial_signals(info); + + info->dcd_chkcount = 0; + info->cts_chkcount = 0; + info->ri_chkcount = 0; + info->dsr_chkcount = 0; /* enable modem signal IRQs and read initial signal states */ usc_EnableStatusIrqs(info,SICR_CTS+SICR_DSR+SICR_DCD+SICR_RI); @@ -2112,16 +2230,27 @@ static int mgsl_write(struct tty_struct * tty, int from_user, if ( info->params.mode == MGSL_MODE_HDLC ) { /* operating in synchronous (frame oriented) mode */ - + if (info->tx_active) { ret = 0; goto cleanup; } - + + /* if operating in HDLC LoopMode and the adapter */ + /* has yet to be inserted into the loop, we can't */ + /* transmit */ + + if ( (info->params.flags & HDLC_FLAG_HDLC_LOOPMODE) && + !usc_loopmode_active(info) ) + { + ret = 0; + goto cleanup; + } + if ( info->xmit_cnt ) { /* Send accumulated from send_char() calls */ /* as frame and wait before accepting more data. */ ret = 0; - + /* copy data from circular xmit_buf to */ /* transmit DMA buffer. */ mgsl_load_tx_dma_buffer(info, @@ -2578,8 +2707,19 @@ static int mgsl_txenable(struct mgsl_struct * info, int enable) spin_lock_irqsave(&info->irq_spinlock,flags); if ( enable ) { - if ( !info->tx_enabled ) + if ( !info->tx_enabled ) { + usc_start_transmitter(info); + /*-------------------------------------------------- + * if HDLC/SDLC Loop mode, attempt to insert the + * station in the 'loop' by setting CMR:13. Upon + * receipt of the next GoAhead (RxAbort) sequence, + * the OnLoop indicator (CCSR:7) should go active + * to indicate that we are on the loop + *--------------------------------------------------*/ + if ( info->params.flags & HDLC_FLAG_HDLC_LOOPMODE ) + usc_loopmode_insert_request( info ); + } } else { if ( info->tx_enabled ) usc_stop_transmitter(info); @@ -2604,7 +2744,12 @@ static int mgsl_txabort(struct mgsl_struct * info) spin_lock_irqsave(&info->irq_spinlock,flags); if ( info->tx_active && info->params.mode == MGSL_MODE_HDLC ) - usc_TCmd(info,TCmd_SendAbort); + { + if ( info->params.flags & HDLC_FLAG_HDLC_LOOPMODE ) + usc_loopmode_cancel_transmit( info ); + else + usc_TCmd(info,TCmd_SendAbort); + } spin_unlock_irqrestore(&info->irq_spinlock,flags); return 0; @@ -2640,25 +2785,39 @@ static int mgsl_rxenable(struct mgsl_struct * info, int enable) /* mgsl_wait_event() wait for specified event to occur * * Arguments: info pointer to device instance data - * mask bitmask of events to wait for - * Return Value: bit mask of triggering event, otherwise error code + * mask pointer to bitmask of events to wait for + * Return Value: 0 if successful and bit mask updated with + * of events triggerred, + * otherwise error code */ -static int mgsl_wait_event(struct mgsl_struct * info, int mask) +static int mgsl_wait_event(struct mgsl_struct * info, int * mask_ptr) { unsigned long flags; int s; int rc=0; u16 regval; struct mgsl_icount cprev, cnow; + int events = 0; + int mask; + struct _input_signal_events signal_events_prev, signal_events_now; + + COPY_FROM_USER(rc,&mask, mask_ptr, sizeof(int)); + if (rc) { + return -EFAULT; + } if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):mgsl_wait_event(%s,%d)\n", __FILE__,__LINE__, info->device_name, mask); - + spin_lock_irqsave(&info->irq_spinlock,flags); - + + usc_get_serial_signals(info); + s = info->serial_signals; + /* note the counters on entry */ cprev = info->icount; + signal_events_prev = info->input_signal_events; if (mask & MgslEvent_ExitHuntMode) { /* enable exit hunt mode IRQ */ @@ -2676,7 +2835,22 @@ static int mgsl_wait_event(struct mgsl_struct * info, int mask) spin_unlock_irqrestore(&info->irq_spinlock,flags); - while(!rc) { + /* Determine if any user requested events for input signals is currently TRUE */ + + events |= (mask & ((s & SerialSignal_DSR) ? + MgslEvent_DsrActive:MgslEvent_DsrInactive)); + + events |= (mask & ((s & SerialSignal_DCD) ? + MgslEvent_DcdActive:MgslEvent_DcdInactive)); + + events |= (mask & ((s & SerialSignal_CTS) ? + MgslEvent_CtsActive:MgslEvent_CtsInactive)); + + events |= (mask & ((s & SerialSignal_RI) ? + MgslEvent_RiActive:MgslEvent_RiInactive)); + + + while(!events) { /* sleep until event occurs */ interruptible_sleep_on(&info->event_wait_q); @@ -2687,39 +2861,52 @@ static int mgsl_wait_event(struct mgsl_struct * info, int mask) } spin_lock_irqsave(&info->irq_spinlock,flags); + /* get icount and serial signal states */ cnow = info->icount; - s = info->serial_signals; + signal_events_now = info->input_signal_events; spin_unlock_irqrestore(&info->irq_spinlock,flags); + + if (signal_events_now.dsr_up != signal_events_prev.dsr_up && + mask & MgslEvent_DsrActive ) + events |= MgslEvent_DsrActive; - rc = 0; + if (signal_events_now.dsr_down != signal_events_prev.dsr_down && + mask & MgslEvent_DsrInactive ) + events |= MgslEvent_DsrInactive; + + if (signal_events_now.dcd_up != signal_events_prev.dcd_up && + mask & MgslEvent_DcdActive ) + events |= MgslEvent_DcdActive; - if (cnow.dsr != cprev.dsr) - rc |= (mask & ((s & SerialSignal_DSR) ? - MgslEvent_DsrActive:MgslEvent_DsrInactive)); + if (signal_events_now.dcd_down != signal_events_prev.dcd_down && + mask & MgslEvent_DcdInactive ) + events |= MgslEvent_DcdInactive; + + if (signal_events_now.cts_up != signal_events_prev.cts_up && + mask & MgslEvent_CtsActive ) + events |= MgslEvent_CtsActive; + + if (signal_events_now.cts_down != signal_events_prev.cts_down && + mask & MgslEvent_CtsInactive ) + events |= MgslEvent_CtsInactive; + + if (signal_events_now.ri_up != signal_events_prev.ri_up && + mask & MgslEvent_RiActive ) + events |= MgslEvent_RiActive; + + if (signal_events_now.ri_down != signal_events_prev.ri_down && + mask & MgslEvent_RiInactive ) + events |= MgslEvent_RiInactive; - if (cnow.dcd != cprev.dcd) - rc |= (mask & ((s & SerialSignal_DCD) ? - MgslEvent_DcdActive:MgslEvent_DcdInactive)); - - if (cnow.cts != cprev.cts) - rc |= (mask & ((s & SerialSignal_CTS) ? - MgslEvent_CtsActive:MgslEvent_CtsInactive)); - - if (cnow.rng != cprev.rng) - rc |= (mask & ((s & SerialSignal_RI) ? - MgslEvent_RiActive:MgslEvent_RiInactive)); - if (cnow.exithunt != cprev.exithunt) - rc |= (mask & MgslEvent_ExitHuntMode); - + events |= (mask & MgslEvent_ExitHuntMode); + if (cnow.rxidle != cprev.rxidle) - rc |= (mask & MgslEvent_ExitHuntMode); - - if (!rc) - rc = -EIO; /* no change => error */ - + events |= (mask & MgslEvent_IdleReceived); + cprev = cnow; + signal_events_prev = signal_events_now; } if (mask & (MgslEvent_ExitHuntMode + MgslEvent_IdleReceived)) { @@ -2732,7 +2919,10 @@ static int mgsl_wait_event(struct mgsl_struct * info, int mask) } spin_unlock_irqrestore(&info->irq_spinlock,flags); } - + + if ( rc == 0 ) + PUT_USER(rc, events, mask_ptr); + return rc; } /* end of mgsl_wait_event() */ @@ -2772,7 +2962,7 @@ static int get_modem_info(struct mgsl_struct * info, unsigned int *value) if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):mgsl_get_modem_info %s value=%08X\n", - __FILE__,__LINE__, info->device_name, *value ); + __FILE__,__LINE__, info->device_name, result ); PUT_USER(err,result,value); return err; @@ -2928,7 +3118,9 @@ static int mgsl_ioctl(struct tty_struct *tty, struct file * file, case MGSL_IOCGSTATS: return mgsl_get_stats(info,(struct mgsl_icount*)arg); case MGSL_IOCWAITEVENT: - return mgsl_wait_event(info,(int)arg); + return mgsl_wait_event(info,(int*)arg); + case MGSL_IOCLOOPTXDONE: + return mgsl_loopmode_send_done(info); case MGSL_IOCCLRMODCOUNT: while(MOD_IN_USE) MOD_DEC_USE_COUNT; @@ -3626,11 +3818,6 @@ static inline int line_info(char *buf, struct mgsl_struct *info) } spin_unlock_irqrestore(&info->irq_spinlock,flags); -#if 0 && LINUX_VERSION_CODE >= VERSION(2,1,0) - ret += sprintf(buf+ret, "irq_spinlock=%08X\n", - info->irq_spinlock.lock ); -#endif - return ret; } /* end of line_info() */ @@ -4227,6 +4414,18 @@ int mgsl_enumerate_devices() if ( PCIBIOS_SUCCESSFUL == pcibios_find_device( MICROGATE_VENDOR_ID, SYNCLINK_DEVICE_ID, i, &bus, &func) ) { +#if LINUX_VERSION_CODE >= VERSION(2,1,0) + struct pci_dev *pdev = pci_find_slot(bus,func); + irq_line = pdev->irq; +#else + if (pcibios_read_config_byte(bus,func, + PCI_INTERRUPT_LINE,&irq_line) ) { + printk( "%s(%d):USC I/O addr not set.\n", + __FILE__,__LINE__); + continue; + } +#endif + if (pcibios_read_config_dword(bus,func, PCI_BASE_ADDRESS_3,&shared_mem_base) ) { printk( "%s(%d):Shared mem addr not set.\n", @@ -4248,13 +4447,6 @@ int mgsl_enumerate_devices() continue; } - if (pcibios_read_config_byte(bus,func, - PCI_INTERRUPT_LINE,&irq_line) ) { - printk( "%s(%d):USC I/O addr not set.\n", - __FILE__,__LINE__); - continue; - } - info = mgsl_allocate_device(); if ( !info ) { /* error allocating device instance data */ @@ -4671,29 +4863,53 @@ void usc_set_sdlc_mode( struct mgsl_struct *info ) { u16 RegValue; - /* Channel mode Register (CMR) - * - * <15..14> 00 Tx Sub modes, Underrun Action - * <13> 0 1 = Send Preamble before opening flag - * <12> 0 1 = Consecutive Idles share common 0 - * <11..8> 0110 Transmitter mode = HDLC/SDLC - * <7..4> 0000 Rx Sub modes, addr/ctrl field handling - * <3..0> 0110 Receiver mode = HDLC/SDLC - * - * 0000 0110 0000 0110 = 0x0606 - */ + if ( info->params.flags & HDLC_FLAG_HDLC_LOOPMODE ) + { + /* + ** Channel Mode Register (CMR) + ** + ** <15..14> 10 Tx Sub Modes, Send Flag on Underrun + ** <13> 0 0 = Transmit Disabled (initially) + ** <12> 0 1 = Consecutive Idles share common 0 + ** <11..8> 1110 Transmitter Mode = HDLC/SDLC Loop + ** <7..4> 0000 Rx Sub Modes, addr/ctrl field handling + ** <3..0> 0110 Receiver Mode = HDLC/SDLC + ** + ** 1000 1110 0000 0110 = 0x8e06 + */ + RegValue = 0x8e06; + + /*-------------------------------------------------- + * ignore user options for UnderRun Actions and + * preambles + *--------------------------------------------------*/ + } + else + { + /* Channel mode Register (CMR) + * + * <15..14> 00 Tx Sub modes, Underrun Action + * <13> 0 1 = Send Preamble before opening flag + * <12> 0 1 = Consecutive Idles share common 0 + * <11..8> 0110 Transmitter mode = HDLC/SDLC + * <7..4> 0000 Rx Sub modes, addr/ctrl field handling + * <3..0> 0110 Receiver mode = HDLC/SDLC + * + * 0000 0110 0000 0110 = 0x0606 + */ - RegValue = 0x0606; + RegValue = 0x0606; - if ( info->params.flags & HDLC_FLAG_UNDERRUN_ABORT15 ) - RegValue |= BIT14; - else if ( info->params.flags & HDLC_FLAG_UNDERRUN_FLAG ) - RegValue |= BIT15; - else if ( info->params.flags & HDLC_FLAG_UNDERRUN_CRC ) - RegValue |= BIT15 + BIT14; + if ( info->params.flags & HDLC_FLAG_UNDERRUN_ABORT15 ) + RegValue |= BIT14; + else if ( info->params.flags & HDLC_FLAG_UNDERRUN_FLAG ) + RegValue |= BIT15; + else if ( info->params.flags & HDLC_FLAG_UNDERRUN_CRC ) + RegValue |= BIT15 + BIT14; - if ( info->params.preamble != HDLC_PREAMBLE_PATTERN_NONE ) - RegValue |= BIT13; + if ( info->params.preamble != HDLC_PREAMBLE_PATTERN_NONE ) + RegValue |= BIT13; + } if ( info->params.flags & HDLC_FLAG_SHARE_ZERO ) RegValue |= BIT12; @@ -4862,6 +5078,8 @@ void usc_set_sdlc_mode( struct mgsl_struct *info ) RegValue |= 0x0003; /* RxCLK from DPLL */ else if ( info->params.flags & HDLC_FLAG_RXC_BRG ) RegValue |= 0x0004; /* RxCLK from BRG0 */ + else if ( info->params.flags & HDLC_FLAG_RXC_TXCPIN) + RegValue |= 0x0006; /* RxCLK from TXC Input */ else RegValue |= 0x0007; /* RxCLK from Port1 */ @@ -4869,6 +5087,8 @@ void usc_set_sdlc_mode( struct mgsl_struct *info ) RegValue |= 0x0018; /* TxCLK from DPLL */ else if ( info->params.flags & HDLC_FLAG_TXC_BRG ) RegValue |= 0x0020; /* TxCLK from BRG0 */ + else if ( info->params.flags & HDLC_FLAG_TXC_RXCPIN) + RegValue |= 0x0038; /* RxCLK from TXC Input */ else RegValue |= 0x0030; /* TxCLK from Port0 */ @@ -4922,10 +5142,24 @@ void usc_set_sdlc_mode( struct mgsl_struct *info ) /* of rounding up and then subtracting 1 we just don't subtract */ /* the one in this case. */ - Tc = (u16)((XtalSpeed/DpllDivisor)/info->params.clock_speed); - if ( !((((XtalSpeed/DpllDivisor) % info->params.clock_speed) * 2) - / info->params.clock_speed) ) - Tc--; + /*-------------------------------------------------- + * ejz: for DPLL mode, application should use the + * same clock speed as the partner system, even + * though clocking is derived from the input RxData. + * In case the user uses a 0 for the clock speed, + * default to 0xffffffff and don't try to divide by + * zero + *--------------------------------------------------*/ + if ( info->params.clock_speed ) + { + Tc = (u16)((XtalSpeed/DpllDivisor)/info->params.clock_speed); + if ( !((((XtalSpeed/DpllDivisor) % info->params.clock_speed) * 2) + / info->params.clock_speed) ) + Tc--; + } + else + Tc = -1; + /* Write 16-bit Time Constant for BRG1 */ usc_OutReg( info, TC1R, Tc ); @@ -6328,6 +6562,13 @@ void mgsl_load_tx_dma_buffer(struct mgsl_struct *info, const char *Buffer, if ( debug_level >= DEBUG_LEVEL_DATA ) mgsl_trace_block(info,Buffer,BufferSize,1); + if (info->params.flags & HDLC_FLAG_HDLC_LOOPMODE) { + /* set CMR:13 to start transmit when + * next GoAhead (abort) is received + */ + info->cmr_value |= BIT13; + } + /* Setup the status and RCC (Frame Size) fields of the 1st */ /* buffer entry in the transmit DMA buffer list. */ @@ -6381,7 +6622,7 @@ BOOLEAN mgsl_register_test( struct mgsl_struct *info ) unsigned int i; BOOLEAN rc = TRUE; unsigned long flags; - + spin_lock_irqsave(&info->irq_spinlock,flags); usc_reset(info); spin_unlock_irqrestore(&info->irq_spinlock,flags); @@ -6471,7 +6712,7 @@ BOOLEAN mgsl_irq_test( struct mgsl_struct *info ) usc_reset(info); spin_unlock_irqrestore(&info->irq_spinlock,flags); - if ( !info->irq_occurred ) + if ( !info->irq_occurred ) return FALSE; else return TRUE; @@ -6499,7 +6740,7 @@ BOOLEAN mgsl_dma_test( struct mgsl_struct *info ) volatile unsigned long EndTime; unsigned long flags; MGSL_PARAMS tmp_params; - + /* save current port options */ memcpy(&tmp_params,&info->params,sizeof(MGSL_PARAMS)); /* load default port options */ @@ -6657,7 +6898,7 @@ BOOLEAN mgsl_dma_test( struct mgsl_struct *info ) /**********************************/ /* WAIT FOR TRANSMIT FIFO TO FILL */ /**********************************/ - + /* Wait 100ms */ EndTime = jiffies + jiffies_from_ms(100); @@ -6724,7 +6965,7 @@ BOOLEAN mgsl_dma_test( struct mgsl_struct *info ) if ( rc == TRUE ){ /* CHECK FOR TRANSMIT ERRORS */ - if ( status & (BIT5 + BIT1) ) + if ( status & (BIT5 + BIT1) ) rc = FALSE; } @@ -6981,13 +7222,90 @@ void mgsl_tx_timeout(unsigned long context) if(info->tx_active && info->params.mode == MGSL_MODE_HDLC) { info->icount.txtimeout++; } - spin_lock_irqsave(&info->irq_spinlock,flags); info->tx_active = 0; info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + + if ( info->params.flags & HDLC_FLAG_HDLC_LOOPMODE ) + usc_loopmode_cancel_transmit( info ); + spin_unlock_irqrestore(&info->irq_spinlock,flags); mgsl_bh_transmit_data(info,0); } /* end of mgsl_tx_timeout() */ +/* signal that there are no more frames to send, so that + * line is 'released' by echoing RxD to TxD when current + * transmission is complete (or immediately if no tx in progress). + */ +static int mgsl_loopmode_send_done( struct mgsl_struct * info ) +{ + unsigned long flags; + + spin_lock_irqsave(&info->irq_spinlock,flags); + if (info->params.flags & HDLC_FLAG_HDLC_LOOPMODE) { + if (info->tx_active) + info->loopmode_send_done_requested = TRUE; + else + usc_loopmode_send_done(info); + } + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + return 0; +} + +/* release the line by echoing RxD to TxD + * upon completion of a transmit frame + */ +void usc_loopmode_send_done( struct mgsl_struct * info ) +{ + info->loopmode_send_done_requested = FALSE; + /* clear CMR:13 to 0 to start echoing RxData to TxData */ + info->cmr_value &= ~BIT13; + usc_OutReg(info, CMR, info->cmr_value); +} + +/* abort a transmit in progress while in HDLC LoopMode + */ +void usc_loopmode_cancel_transmit( struct mgsl_struct * info ) +{ + /* reset tx dma channel and purge TxFifo */ + usc_RTCmd( info, RTCmd_PurgeTxFifo ); + usc_DmaCmd( info, DmaCmd_ResetTxChannel ); + usc_loopmode_send_done( info ); +} + +/* for HDLC/SDLC LoopMode, setting CMR:13 after the transmitter is enabled + * is an Insert Into Loop action. Upon receipt of a GoAhead sequence (RxAbort) + * we must clear CMR:13 to begin repeating TxData to RxData + */ +void usc_loopmode_insert_request( struct mgsl_struct * info ) +{ + info->loopmode_insert_requested = TRUE; + + /* enable RxAbort irq. On next RxAbort, clear CMR:13 to + * begin repeating TxData on RxData (complete insertion) + */ + usc_OutReg( info, RICR, + (usc_InReg( info, RICR ) | RXSTATUS_ABORT_RECEIVED ) ); + + /* set CMR:13 to insert into loop on next GoAhead (RxAbort) */ + info->cmr_value |= BIT13; + usc_OutReg(info, CMR, info->cmr_value); +} + +/* return 1 if station is inserted into the loop, otherwise 0 + */ +int usc_loopmode_active( struct mgsl_struct * info) +{ + return usc_InReg( info, CCSR ) & BIT7 ? 1 : 0 ; +} + +/* return 1 if USC is in loop send mode, otherwise 0 + */ +int usc_loopmode_send_active( struct mgsl_struct * info ) +{ + return usc_InReg( info, CCSR ) & BIT6 ? 1 : 0 ; +} + diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 5007652d1..df5a8efc7 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -651,9 +651,7 @@ static inline ssize_t do_tty_write( ssize_t ret = 0, written = 0; struct inode *inode = file->f_dentry->d_inode; - up(&inode->i_sem); - if (down_interruptible(&inode->i_atomic_write)) { - down(&inode->i_sem); + if (down_interruptible(&inode->i_sem)) { return -ERESTARTSYS; } for (;;) { @@ -678,8 +676,7 @@ static inline ssize_t do_tty_write( file->f_dentry->d_inode->i_mtime = CURRENT_TIME; ret = written; } - up(&inode->i_atomic_write); - down(&inode->i_sem); + up(&inode->i_sem); return ret; } diff --git a/drivers/net/Makefile b/drivers/net/Makefile index f0f990aad..5e2a45bce 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -143,14 +143,6 @@ else endif endif -ifeq ($(CONFIG_ETHERH),y) -CONFIG_8390_BUILTIN = y -else - ifeq ($(CONFIG_ETHERH),m) - CONFIG_8390_MODULE = y - endif -endif - ifeq ($(CONFIG_WD80x3),y) L_OBJS += wd.o CONFIG_8390_BUILTIN = y @@ -171,14 +163,6 @@ else endif endif -ifeq ($(CONFIG_ETHERH),y) -CONFIG_8390_BUILTIN = y -else - ifeq ($(CONFIG_ETHERH),m) - CONFIG_8390_MODULE = y - endif -endif - ifeq ($(CONFIG_NE2K_PCI),y) L_OBJS += ne2k-pci.o CONFIG_8390_BUILTIN = y diff --git a/drivers/net/ibmtr.c b/drivers/net/ibmtr.c index f8d53cea4..29356bbbf 100644 --- a/drivers/net/ibmtr.c +++ b/drivers/net/ibmtr.c @@ -515,7 +515,7 @@ __initfunc(static int ibmtr_probe1(struct device *dev, int PIOaddr)) /* How much shared RAM is on adapter ? */ #ifdef PCMCIA ti->avail_shared_ram = pcmcia_reality_check(get_sram_size(ti)); - ibmtr_mem_base = ti->sram_base ; + ibmtr_mem_base = ti->sram_base << 12 ; #else ti->avail_shared_ram = get_sram_size(ti); #endif @@ -835,6 +835,9 @@ static int tok_close(struct device *dev) (int)readb(ti->srb + offsetof(struct srb_close_adapter, ret_code))); dev->start = 0; +#ifdef PCMCIA + ti->sram = 0 ; +#endif DPRINTK("Adapter closed.\n"); MOD_DEC_USE_COUNT; diff --git a/drivers/net/irda/irport.c b/drivers/net/irda/irport.c index 5fb0cd4f2..e813fe8a4 100644 --- a/drivers/net/irda/irport.c +++ b/drivers/net/irda/irport.c @@ -321,7 +321,7 @@ void irport_change_speed(struct irda_device *idev, int speed) /* Turn on interrups */ outb(UART_IER_RLSI|UART_IER_RDI|UART_IER_THRI, iobase+UART_IER); - spin_unlock_irqrestore(&self->lock, flags); + spin_unlock_irqrestore(&idev->lock, flags); } /* diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 4e1e3465d..9f5ecd98d 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -47,13 +47,23 @@ pci_find_slot(unsigned int bus, unsigned int devfn) struct pci_dev * pci_find_device(unsigned int vendor, unsigned int device, struct pci_dev *from) { - if (!from) - from = pci_devices; - else - from = from->next; - while (from && (from->vendor != vendor || from->device != device)) - from = from->next; - return from; + struct pci_dev *next; + + next = pci_devices; + if (from) + next = from->next; + + while (next) { + struct pci_dev *dev = next; + next = next->next; + if (vendor != PCI_ANY_ID && dev->vendor != vendor) + continue; + if (device != PCI_ANY_ID && dev->device != device) + continue; + + return dev; + } + return NULL; } @@ -178,10 +188,8 @@ __initfunc(unsigned int pci_scan_bus(struct pci_bus *bus)) if (pcibios_read_config_dword(bus->number, devfn, PCI_VENDOR_ID, &l) || /* some broken boards return 0 if a slot is empty: */ - l == 0xffffffff || l == 0x00000000 || l == 0x0000ffff || l == 0xffff0000) { - is_multi = 0; + l == 0xffffffff || l == 0x00000000 || l == 0x0000ffff || l == 0xffff0000) continue; - } dev = kmalloc(sizeof(*dev), GFP_ATOMIC); memset(dev, 0, sizeof(*dev)); diff --git a/drivers/sbus/audio/cs4215.h b/drivers/sbus/audio/cs4215.h index ff5367d6d..79d3945ef 100644 --- a/drivers/sbus/audio/cs4215.h +++ b/drivers/sbus/audio/cs4215.h @@ -11,11 +11,10 @@ struct cs4215 { __u8 data[4]; /* Data mode: Time slots 5-8 */ __u8 ctrl[4]; /* Ctrl mode: Time slots 1-4 */ - __volatile__ struct dbri_mem td; - __volatile__ struct dbri_mem rd; __u8 onboard; - __u32 status; - __u32 version; + __u8 offset; /* Bit offset from frame sync to time slot 1 */ + volatile __u32 status; + volatile __u32 version; }; diff --git a/drivers/sbus/audio/dbri.c b/drivers/sbus/audio/dbri.c index 23afc72b4..9f76c92e6 100644 --- a/drivers/sbus/audio/dbri.c +++ b/drivers/sbus/audio/dbri.c @@ -79,25 +79,25 @@ #define D_CMD (1<<2) #define D_MM (1<<3) #define D_USR (1<<4) +#define D_DESC (1<<5) -/* static int dbri_debug = D_GEN|D_INT|D_CMD|D_MM|D_USR; */ static int dbri_debug = 0; MODULE_PARM(dbri_debug, "i"); +static int dbri_trace = 0; +MODULE_PARM(dbri_trace, "i"); +#define tprintk(x) if(dbri_trace) printk x + static char *cmds[] = { "WAIT", "PAUSE", "JUMP", "IIQ", "REX", "SDP", "CDP", "DTS", "SSP", "CHI", "NT", "TE", "CDEC", "TEST", "CDM", "RESRV" }; -/* Bit hunting */ -#define dumpcmd {int i; for(i=0; i<n; i++) printk("DBRI: %x\n", dbri->dma->cmd[i]); } - #define DBRI_CMD(cmd, intr, value) ((cmd << 28) | (1 << 27) | value) #else #define dprintk(a, x) -#define dumpcmd #define DBRI_CMD(cmd, intr, value) ((cmd << 28) | (intr << 27) | value) #endif /* DBRI_DEBUG */ @@ -114,41 +114,42 @@ static int num_drivers = 0; **************************************************************************** ************** DBRI initialization and command synchronization ************* **************************************************************************** -*/ +Commands are sent to the DBRI by building a list of them in memory, +then writing the address of the first list item to DBRI register 8. +The list is terminated with a WAIT command, which can generate a +CPU interrupt if required. + +Since the DBRI can run in parallel with the CPU, several means of +synchronization present themselves. The original scheme (Rudolf's) +was to set a flag when we "cmdlock"ed the DBRI, clear the flag when +an interrupt signaled completion, and wait on a wait_queue if a routine +attempted to cmdlock while the flag was set. The problems arose when +we tried to cmdlock from inside an interrupt handler, which might +cause scheduling in an interrupt (if we waited), etc, etc + +A more sophisticated scheme might involve a circular command buffer +or an array of command buffers. A routine could fill one with +commands and link it onto a list. When a interrupt signaled +completion of the current command buffer, look on the list for +the next one. + +I've decided to implement something much simpler - after each command, +the CPU waits for the DBRI to finish the command by polling the P bit +in DBRI register 0. I've tried to implement this in such a way +that might make implementing a more sophisticated scheme easier. + +Every time a routine wants to write commands to the DBRI, it must +first call dbri_cmdlock() and get an initial pointer into dbri->dma->cmd +in return. After the commands have been writen, dbri_cmdsend() is +called with the final pointer value. + +Something a little more clever is required if this code is ever run +on an SMP machine. -/* - * Commands are sent to the DBRI by building a list of them in memory, - * then writing the address of the first list item to DBRI register 8. - * The list is terminated with a WAIT command, which can generate a - * CPU interrupt if required. - * - * Since the DBRI can run asynchronously to the CPU, several means of - * synchronization present themselves. The original scheme (Rudolf's) - * was to set a flag when we "cmdlock"ed the DBRI, clear the flag when - * an interrupt signaled completion, and wait on a wait_queue if a routine - * attempted to cmdlock while the flag was set. The problems arose when - * we tried to cmdlock from inside an interrupt handler, which might - * cause scheduling in an interrupt (if we waited), etc, etc - * - * A more sophisticated scheme might involve a circular command buffer - * or an array of command buffers. A routine could fill one with - * commands and link it onto a list. When a interrupt signaled - * completion of the current command buffer, look on the list for - * the next one. - * - * I've decided to implement something much simpler - after each command, - * the CPU waits for the DBRI to finish the command by polling the P bit - * in DBRI register 0. I've tried to implement this in such a way - * that might make implementing a more sophisticated scheme easier. - * - * Every time a routine wants to write commands to the DBRI, it must - * first call dbri_cmdlock() and get an initial pointer into dbri->dma->cmd - * in return. After the commands have been writen, dbri_cmdsend() is - * called with the final pointer value. - */ +*/ -static int dbri_locked = 0; /* XXX not SMP safe! XXX */ +static int dbri_locked = 0; static volatile int * dbri_cmdlock(struct dbri *dbri) { @@ -159,9 +160,21 @@ static volatile int * dbri_cmdlock(struct dbri *dbri) return dbri->dma->cmd; } +static void dbri_process_interrupt_buffer(struct dbri *); + static void dbri_cmdsend(struct dbri *dbri, volatile int * cmd) { - int maxloops = 1000000; + int MAXLOOPS = 1000000; + int maxloops = MAXLOOPS; + unsigned int flags; + volatile int * ptr; + + for (ptr = dbri->dma->cmd; ptr < cmd; ptr ++) { + dprintk(D_CMD, ("DBRI cmd: %08x:%08x\n", + (unsigned int) ptr, *ptr)); + } + + save_and_cli(flags); dbri_locked --; if (dbri_locked != 0) { @@ -170,14 +183,25 @@ static void dbri_cmdsend(struct dbri *dbri, volatile int * cmd) printk("DBRI: Command buffer overflow! (bug in driver)\n"); } else { *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0); - *(cmd++) = DBRI_CMD(D_WAIT, 0, 0); + *(cmd++) = DBRI_CMD(D_WAIT, 1, 0); + dbri->wait_seen = 0; dbri->regs->reg8 = (int)dbri->dma_dvma->cmd; - while ((maxloops--) > 0 && (dbri->regs->reg0 & D_P)); + while ((--maxloops) > 0 && (dbri->regs->reg0 & D_P)); + if (maxloops == 0) { + printk("DBRI: Chip never completed command buffer\n"); + } else { + while ((--maxloops) > 0 && (! dbri->wait_seen)) + dbri_process_interrupt_buffer(dbri); + if (maxloops == 0) { + printk("DBRI: Chip never acked WAIT\n"); + } else { + dprintk(D_INT, ("DBRI: Chip completed command buffer (%d)\n", + MAXLOOPS - maxloops)); + } + } } - if (maxloops == 0) { - printk("DBRI: Chip never completed command buffer\n"); - } + restore_flags(flags); } static void dbri_reset(struct dbri *dbri) @@ -198,7 +222,7 @@ static void dbri_detach(struct dbri *dbri) dbri_reset(dbri); free_irq(dbri->irq, dbri); sparc_free_io(dbri->regs, dbri->regs_size); - /* Should we release the DMA structure dbri->dma here? */ + release_region((unsigned long) dbri->dma, sizeof(struct dbri_dma)); kfree(dbri); } @@ -242,6 +266,17 @@ static void dbri_initialize(struct dbri *dbri) **************************************************************************** *************************** DBRI interrupt handler ************************* **************************************************************************** + +The DBRI communicates with the CPU mainly via a circular interrupt +buffer. When an interrupt is signaled, the CPU walks through the +buffer and calls dbri_process_one_interrupt() for each interrupt word. +Complicated interrupts are handled by dedicated functions (which +appear first in this file). Any pending interrupts can be serviced by +calling dbri_process_interrupt_buffer(), which works even if the CPU's +interrupts are disabled. This function is used by dbri_cmdsend() +to make sure we're synced up with the chip after each command sequence, +even if we're running cli'ed. + */ @@ -263,6 +298,7 @@ static __u32 reverse_bytes(__u32 b, int len) case 2: b = ((b & 0xaaaaaaaa) >> 1) | ((b & 0x55555555) << 1); case 1: + case 0: break; default: printk("DBRI reverse_bytes: unsupported length\n"); @@ -273,37 +309,39 @@ static __u32 reverse_bytes(__u32 b, int len) /* transmission_complete_intr() * * Called by main interrupt handler when DBRI signals transmission complete - * on a pipe. + * on a pipe (interrupt triggered by the B bit in a transmit descriptor). * * Walks through the pipe's list of transmit buffer descriptors, releasing - * each one's DMA buffer (if present) and signaling its callback routine - * (if present), before flaging the descriptor available and proceeding - * to the next one. - * - * Assumes that only the last in a chain of descriptors will have FINT - * sent to signal an interrupt, so that the chain will be completely - * transmitted by the time we get here, and there's no need to save - * any of the descriptors. In particular, use of the DBRI's CDP command - * is precluded, but I've not been able to get CDP working reliably anyway. + * each one's DMA buffer (if present), flagging the descriptor available, + * and signaling its callback routine (if present), before proceeding + * to the next one. Stops when the first descriptor is found without + * TBC (Transmit Buffer Complete) set, or we've run through them all. */ static void transmission_complete_intr(struct dbri *dbri, int pipe) { - int td = dbri->pipes[pipe].desc; + int td; int status; void *buffer; void (*callback)(void *, int); + void *callback_arg; - dbri->pipes[pipe].desc = -1; + td = dbri->pipes[pipe].desc; - for (; td >= 0; td = dbri->descs[td].next) { + while (td >= 0) { if (td >= DBRI_NO_DESCS) { printk("DBRI: invalid td on pipe %d\n", pipe); return; } - status = dbri->dma->desc[td].word4; + status = DBRI_TD_STATUS(dbri->dma->desc[td].word4); + + if (! (status & DBRI_TD_TBC)) { + break; + } + + dprintk(D_INT, ("DBRI: TD %d, status 0x%02x\n", td, status)); buffer = dbri->descs[td].buffer; if (buffer) { @@ -313,12 +351,16 @@ static void transmission_complete_intr(struct dbri *dbri, int pipe) } callback = dbri->descs[td].output_callback; - if (callback != NULL) { - callback(dbri->descs[td].output_callback_arg, - DBRI_TD_STATUS(status) & 0xe); - } + callback_arg = dbri->descs[td].output_callback_arg; dbri->descs[td].inuse = 0; + + td = dbri->descs[td].next; + dbri->pipes[pipe].desc = td; + + if (callback != NULL) { + callback(callback_arg, status & 0xe); + } } } @@ -335,7 +377,7 @@ static void reception_complete_intr(struct dbri *dbri, int pipe) } dbri->descs[rd].inuse = 0; - dbri->pipes[pipe].desc = -1; + dbri->pipes[pipe].desc = dbri->descs[rd].next; status = dbri->dma->desc[rd].word1; buffer = dbri->descs[rd].buffer; @@ -351,86 +393,147 @@ static void reception_complete_intr(struct dbri *dbri, int pipe) DBRI_RD_STATUS(status), DBRI_RD_CNT(status)-2); } + + dprintk(D_INT, ("DBRI: Recv RD %d, status 0x%02x, len %d\n", + rd, DBRI_RD_STATUS(status), DBRI_RD_CNT(status))); } -static void dbri_intr(int irq, void *opaque, struct pt_regs *regs) +static void dbri_process_one_interrupt(struct dbri *dbri, int x) { - struct dbri *dbri = (struct dbri *)opaque; - int x; - - /* - * Read it, so the interrupt goes away. - */ - x = dbri->regs->reg1; + int val = D_INTR_GETVAL(x); + int channel = D_INTR_GETCHAN(x); + int command = D_INTR_GETCMD(x); + int code = D_INTR_GETCODE(x); + int rval = D_INTR_GETRVAL(x); - if ( x & (D_MRR|D_MLE|D_LBG|D_MBE) ) { - /* - * What should I do here ? - */ - if(x & D_MRR) printk("DBRI: Multiple Error Ack on SBus\n"); - if(x & D_MLE) printk("DBRI: Multiple Late Error on SBus\n"); - if(x & D_LBG) printk("DBRI: Lost Bus Grant on SBus\n"); - if(x & D_MBE) printk("DBRI: Burst Error on SBus\n"); + if (channel == D_INTR_CMD) { + dprintk(D_INT,("DBRI: INTR: Command: %-5s Value:%d\n", + cmds[command], val)); + } else { + dprintk(D_INT,("DBRI: INTR: Chan:%d Code:%d Val:%#x\n", + channel, code, rval)); } - if (!(x & D_IR)) /* Not for us */ - return; + if (channel == D_INTR_CMD && command == D_WAIT) { + dbri->wait_seen ++; + } - x = dbri->dma->intr[dbri->dbri_irqp]; - while (x != 0) { - int val = D_INTR_GETVAL(x); - int channel = D_INTR_GETCHAN(x); + if (code == D_INTR_SBRI) { - dbri->dma->intr[dbri->dbri_irqp] = 0; + /* SBRI - BRI status change */ - if(D_INTR_GETCHAN(x) == D_INTR_CMD) { - dprintk(D_INT,("DBRI: INTR: Command: %-5s Value:%d\n", - cmds[D_INTR_GETCMD(x)], D_INTR_GETVAL(x))); - } else { - dprintk(D_INT,("DBRI: INTR: Chan:%d Code:%d Val:%#x\n", - D_INTR_GETCHAN(x), D_INTR_GETCODE(x), - D_INTR_GETRVAL(x))); - } + int liu_states[] = {1, 0, 8, 3, 4, 5, 6, 7}; + dbri->liu_state = liu_states[val & 0x7]; + if (dbri->liu_callback) + dbri->liu_callback(dbri->liu_callback_arg); + } - if (D_INTR_GETCODE(x) == D_INTR_SBRI) { + if (code == D_INTR_BRDY) { + reception_complete_intr(dbri, channel); + } - /* SBRI - BRI status change */ + if (code == D_INTR_XCMP) { + transmission_complete_intr(dbri, channel); + } - int liu_states[] = {1, 0, 8, 3, 4, 5, 6, 7}; - dbri->liu_state = liu_states[val & 0x7]; - if (dbri->liu_callback) - dbri->liu_callback(dbri->liu_callback_arg); - } + if (code == D_INTR_UNDR) { - if (D_INTR_GETCODE(x) == D_INTR_BRDY) { - reception_complete_intr(dbri, channel); - } + /* UNDR - Transmission underrun + * resend SDP command with clear pipe bit (C) set + */ - if (D_INTR_GETCODE(x) == D_INTR_XCMP) { - transmission_complete_intr(dbri, channel); - } + volatile int *cmd; + int pipe = channel; + int td = dbri->pipes[pipe].desc; - if (D_INTR_GETCODE(x) == D_INTR_FXDT) { + dbri->dma->desc[td].word4 = 0; - /* FXDT - Fixed data change */ + cmd = dbri_cmdlock(dbri); + *(cmd++) = DBRI_CMD(D_SDP, 0, + dbri->pipes[pipe].sdp + | D_SDP_P | D_SDP_C | D_SDP_2SAME); + *(cmd++) = (int) & dbri->dma_dvma->desc[td]; + dbri_cmdsend(dbri, cmd); + } - if (dbri->pipes[D_INTR_GETCHAN(x)].sdp & D_SDP_MSB) { - val = reverse_bytes(val, dbri->pipes[channel].length); - } + if (code == D_INTR_FXDT) { - if (dbri->pipes[D_INTR_GETCHAN(x)].recv_fixed_ptr) { - * dbri->pipes[channel].recv_fixed_ptr = val; - } + /* FXDT - Fixed data change */ + + if (dbri->pipes[channel].sdp & D_SDP_MSB) { + val = reverse_bytes(val, dbri->pipes[channel].length); } + if (dbri->pipes[channel].recv_fixed_ptr) { + * dbri->pipes[channel].recv_fixed_ptr = val; + } + } +} + +/* dbri_process_interrupt_buffer advances through the DBRI's interrupt + * buffer until it finds a zero word (indicating nothing more to do + * right now). Non-zero words require processing and are handed off + * to dbri_process_one_interrupt AFTER advancing the pointer. This + * order is important since we might recurse back into this function + * and need to make sure the pointer has been advanced first. + */ + +static void dbri_process_interrupt_buffer(struct dbri *dbri) +{ + int x; + + while ((x = dbri->dma->intr[dbri->dbri_irqp]) != 0) { + + dbri->dma->intr[dbri->dbri_irqp] = 0; dbri->dbri_irqp++; if (dbri->dbri_irqp == (DBRI_NO_INTS * DBRI_INT_BLK)) dbri->dbri_irqp = 1; else if ((dbri->dbri_irqp & (DBRI_INT_BLK-1)) == 0) dbri->dbri_irqp++; - x = dbri->dma->intr[dbri->dbri_irqp]; + + dbri_process_one_interrupt(dbri, x); + } +} + +static void dbri_intr(int irq, void *opaque, struct pt_regs *regs) +{ + struct dbri *dbri = (struct dbri *)opaque; + int x; + + /* + * Read it, so the interrupt goes away. + */ + x = dbri->regs->reg1; + + dprintk(D_INT, ("DBRI: Interrupt! (reg1=0x%08x)\n", x)); + + if ( x & (D_MRR|D_MLE|D_LBG|D_MBE) ) { + + if(x & D_MRR) printk("DBRI: Multiple Error Ack on SBus\n"); + if(x & D_MLE) printk("DBRI: Multiple Late Error on SBus\n"); + if(x & D_LBG) printk("DBRI: Lost Bus Grant on SBus\n"); + if(x & D_MBE) printk("DBRI: Burst Error on SBus\n"); + + /* Some of these SBus errors cause the chip's SBus circuitry + * to be disabled, so just re-enable and try to keep going. + * + * The only one I've seen is MRR, which will be triggered + * if you let a transmit pipe underrun, then try to CDP it. + * + * If these things persist, we should probably reset + * and re-init the chip. + */ + + dbri->regs->reg0 &= ~D_D; } + +#if 0 + if (!(x & D_IR)) /* Not for us */ + return; +#endif + + dbri_process_interrupt_buffer(dbri); } @@ -438,8 +541,22 @@ static void dbri_intr(int irq, void *opaque, struct pt_regs *regs) **************************************************************************** ************************** DBRI data pipe management *********************** **************************************************************************** + +While DBRI control functions use the command and interrupt buffers, the +main data path takes the form of data pipes, which can be short (command +and interrupt driven), or long (attached to DMA buffers). These functions +provide a rudimentary means of setting up and managing the DBRI's pipes, +but the calling functions have to make sure they respect the pipes' linked +list ordering, among other things. The transmit and receive functions +here interface closely with the transmit and receive interrupt code. + */ +static int pipe_active(struct dbri *dbri, int pipe) +{ + return (dbri->pipes[pipe].desc != -1); +} + /* reset_pipe(dbri, pipe) * @@ -449,6 +566,7 @@ static void dbri_intr(int irq, void *opaque, struct pt_regs *regs) static void reset_pipe(struct dbri *dbri, int pipe) { int sdp; + int desc; volatile int *cmd; if (pipe < 0 || pipe > 31) { @@ -467,6 +585,35 @@ static void reset_pipe(struct dbri *dbri, int pipe) *(cmd++) = 0; dbri_cmdsend(dbri, cmd); + desc = dbri->pipes[pipe].desc; + while (desc != -1) { + void *buffer = dbri->descs[desc].buffer; + void (*output_callback) (void *, int) + = dbri->descs[desc].output_callback; + void *output_callback_arg + = dbri->descs[desc].output_callback_arg; + void (*input_callback) (void *, int, unsigned int) + = dbri->descs[desc].input_callback; + void *input_callback_arg + = dbri->descs[desc].input_callback_arg; + + if (buffer) { + mmu_release_scsi_one(sbus_dvma_addr(buffer), + dbri->descs[desc].len, + dbri->sdev->my_bus); + } + + dbri->descs[desc].inuse = 0; + desc = dbri->descs[desc].next; + + if (output_callback) { + output_callback(output_callback_arg, -1); + } + if (input_callback) { + input_callback(input_callback_arg, -1, 0); + } + } + dbri->pipes[pipe].desc = -1; } @@ -482,140 +629,179 @@ static void setup_pipe(struct dbri *dbri, int pipe, int sdp) /* sdp &= 0xf800; */ } + /* If this is a fixed receive pipe, arrange for an interrupt + * every time its data changes + */ + + if (D_SDP_MODE(sdp) == D_SDP_FIXED && ! (sdp & D_SDP_TO_SER)) { + sdp |= D_SDP_CHANGE; + } + sdp |= D_PIPE(pipe); dbri->pipes[pipe].sdp = sdp; + dbri->pipes[pipe].desc = -1; reset_pipe(dbri, pipe); } -enum master_or_slave { CHImaster, CHIslave }; - -static void reset_chi(struct dbri *dbri, enum master_or_slave master_or_slave, - int bits_per_frame) +static void link_time_slot(struct dbri *dbri, int pipe, + enum in_or_out direction, int basepipe, + int length, int cycle) { volatile int *cmd; int val; + int prevpipe; + int nextpipe; - cmd = dbri_cmdlock(dbri); + if (pipe < 0 || pipe > 31 || basepipe < 0 || basepipe > 31) { + printk("DBRI: link_time_slot called with illegal pipe number\n"); + return; + } - /* Set CHI Anchor: Pipe 16 */ + if (dbri->pipes[pipe].sdp == 0 || dbri->pipes[basepipe].sdp == 0) { + printk("DBRI: link_time_slot called on uninitialized pipe\n"); + return; + } - val = D_DTS_VI | D_DTS_VO | D_DTS_INS | - D_DTS_PRVIN(D_P_16) | D_DTS_PRVOUT(D_P_16) | D_PIPE(D_P_16); - *(cmd++) = DBRI_CMD(D_DTS, 0, val); - *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(D_P_16); - *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(D_P_16); + /* Deal with CHI special case: + * "If transmission on edges 0 or 1 is desired, then cycle n + * (where n = # of bit times per frame...) must be used." + * - DBRI data sheet, page 11 + */ - dbri->pipes[16].sdp = 1; - dbri->pipes[16].nextpipe = 16; + if (basepipe == 16 && direction == PIPEoutput && cycle == 0) { + cycle = dbri->chi_bpf; + } - if (master_or_slave == CHIslave) { - /* Setup DBRI for CHI Slave - receive clock, frame sync (FS) - * - * CHICM = 0 (slave mode, 8 kHz frame rate) - * IR = give immediate CHI status interrupt - * EN = give CHI status interrupt upon change - */ - *(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(0) - | D_CHI_IR | D_CHI_EN); + if (basepipe == pipe) { + prevpipe = pipe; + nextpipe = pipe; } else { - /* Setup DBRI for CHI Master - generate clock, FS - * - * BPF = bits per 8 kHz frame - * 12.288 MHz / CHICM_divisor = clock rate - * FD = 1 - drive CHIFS on rising edge of CHICK + + /* We're not initializing a new linked list (basepipe != pipe), + * so run through the linked list and find where this pipe + * should be sloted in, based on its cycle. CHI confuses + * things a bit, since it has a single anchor for both its + * transmit and receive lists. */ - int clockrate = bits_per_frame * 8; - int divisor = 12288 / clockrate; + if (basepipe == 16) { + if (direction == PIPEinput) { + prevpipe = dbri->chi_in_pipe; + } else { + prevpipe = dbri->chi_out_pipe; + } + } else { + prevpipe = basepipe; + } + + nextpipe = dbri->pipes[prevpipe].nextpipe; - if (divisor > 255 || divisor * clockrate != 12288) { - printk("DBRI: illegal bits_per_frame in setup_chi\n"); + while (dbri->pipes[nextpipe].cycle < cycle + && dbri->pipes[nextpipe].nextpipe != basepipe) { + prevpipe = nextpipe; + nextpipe = dbri->pipes[nextpipe].nextpipe; } + } - *(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(divisor) | D_CHI_FD - | D_CHI_IR | D_CHI_EN - | D_CHI_BPF(bits_per_frame)); + if (prevpipe == 16) { + if (direction == PIPEinput) { + dbri->chi_in_pipe = pipe; + } else { + dbri->chi_out_pipe = pipe; + } + } else { + dbri->pipes[prevpipe].nextpipe = pipe; } - /* CHI Data Mode - * - * RCE = 0 - receive on falling edge of CHICK - * XCE = 1 - transmit on rising edge of CHICK - * XEN = 1 - enable transmitter - * REN = 1 - enable receiver - */ + dbri->pipes[pipe].nextpipe = nextpipe; + dbri->pipes[pipe].cycle = cycle; + dbri->pipes[pipe].length = length; - *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0); + cmd = dbri_cmdlock(dbri); - *(cmd++) = DBRI_CMD(D_CDM, 0, D_CDM_XCE|D_CDM_XEN|D_CDM_REN); + if (direction == PIPEinput) { + val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(prevpipe) | pipe; + *(cmd++) = DBRI_CMD(D_DTS, 0, val); + *(cmd++) = D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe); + *(cmd++) = 0; + } else { + val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(prevpipe) | pipe; + *(cmd++) = DBRI_CMD(D_DTS, 0, val); + *(cmd++) = 0; + *(cmd++) = D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe); + } dbri_cmdsend(dbri, cmd); } -enum in_or_out { PIPEinput, PIPEoutput }; +/* I don't use this function, so it's basically untested. */ -static void link_time_slot(struct dbri *dbri, int pipe, - enum in_or_out direction, int prevpipe, - int length, int cycle) +static void unlink_time_slot(struct dbri *dbri, int pipe, + enum in_or_out direction, int prevpipe, + int nextpipe) { volatile int *cmd; int val; - int nextpipe; if (pipe < 0 || pipe > 31 || prevpipe < 0 || prevpipe > 31) { - printk("DBRI: link_time_slot called with illegal pipe number\n"); - return; - } - - if (dbri->pipes[pipe].sdp == 0 || dbri->pipes[prevpipe].sdp == 0) { - printk("DBRI: link_time_slot called on uninitialized pipe\n"); + printk("DBRI: unlink_time_slot called with illegal pipe number\n"); return; } - if (pipe == prevpipe) { - nextpipe = pipe; - } else { - nextpipe = dbri->pipes[prevpipe].nextpipe; - } - - dbri->pipes[pipe].nextpipe = nextpipe; - dbri->pipes[pipe].cycle = cycle; - dbri->pipes[pipe].length = length; - cmd = dbri_cmdlock(dbri); if (direction == PIPEinput) { - val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(prevpipe) | pipe; + val = D_DTS_VI | D_DTS_DEL | D_DTS_PRVIN(prevpipe) | pipe; *(cmd++) = DBRI_CMD(D_DTS, 0, val); - *(cmd++) = D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe); + *(cmd++) = D_TS_NEXT(nextpipe); *(cmd++) = 0; } else { - val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(prevpipe) | pipe; + val = D_DTS_VO | D_DTS_DEL | D_DTS_PRVOUT(prevpipe) | pipe; *(cmd++) = DBRI_CMD(D_DTS, 0, val); *(cmd++) = 0; - *(cmd++) = D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe); + *(cmd++) = D_TS_NEXT(nextpipe); } dbri_cmdsend(dbri, cmd); } +/* xmit_fixed() / recv_fixed() + * + * Transmit/receive data on a "fixed" pipe - i.e, one whose contents are not + * expected to change much, and which we don't need to buffer. + * The DBRI only interrupts us when the data changes (receive pipes), + * or only changes the data when this function is called (transmit pipes). + * Only short pipes (numbers 16-31) can be used in fixed data mode. + * + * These function operate on a 32-bit field, no matter how large + * the actual time slot is. The interrupt handler takes care of bit + * ordering and alignment. An 8-bit time slot will always end up + * in the low-order 8 bits, filled either MSB-first or LSB-first, + * depending on the settings passed to setup_pipe() + */ + static void xmit_fixed(struct dbri *dbri, int pipe, unsigned int data) { volatile int *cmd; if (pipe < 16 || pipe > 31) { - printk("DBRI: xmit_fixed called with illegal pipe number\n"); + printk("DBRI: xmit_fixed: Illegal pipe number\n"); + return; + } + + if (D_SDP_MODE(dbri->pipes[pipe].sdp) == 0) { + printk("DBRI: xmit_fixed: Uninitialized pipe %d\n", pipe); return; } if (D_SDP_MODE(dbri->pipes[pipe].sdp) != D_SDP_FIXED) { - printk("DBRI: xmit_fixed called on non-fixed pipe\n"); + printk("DBRI: xmit_fixed: Non-fixed pipe %d\n", pipe); return; } if (! dbri->pipes[pipe].sdp & D_SDP_TO_SER) { - printk("DBRI: xmit_fixed called on receive pipe\n"); + printk("DBRI: xmit_fixed: Called on receive pipe %d\n", pipe); return; } @@ -633,21 +819,7 @@ static void xmit_fixed(struct dbri *dbri, int pipe, unsigned int data) dbri_cmdsend(dbri, cmd); } -/* recv_fixed() - * - * Receive data on a "fixed" pipe - i.e, one whose contents are not - * expected to change much, and which we don't need to read constantly - * into a buffer. The DBRI only interrupts us when the data changes. - * Only short pipes (numbers 16-31) can be used in fixed data mode. - * - * Pass this function a pointer to a 32-bit field, no matter how large - * the actual time slot is. The interrupt handler takes care of bit - * ordering and alignment. An 8-bit time slot will always end up - * in the low-order 8 bits, filled either MSB-first or LSB-first, - * depending on the settings passed to setup_pipe() - */ - -static void recv_fixed(struct dbri *dbri, int pipe, __u32 *ptr) +static void recv_fixed(struct dbri *dbri, int pipe, volatile __u32 *ptr) { if (pipe < 16 || pipe > 31) { printk("DBRI: recv_fixed called with illegal pipe number\n"); @@ -655,12 +827,12 @@ static void recv_fixed(struct dbri *dbri, int pipe, __u32 *ptr) } if (D_SDP_MODE(dbri->pipes[pipe].sdp) != D_SDP_FIXED) { - printk("DBRI: recv_fixed called on non-fixed pipe\n"); + printk("DBRI: recv_fixed called on non-fixed pipe %d\n", pipe); return; } if (dbri->pipes[pipe].sdp & D_SDP_TO_SER) { - printk("DBRI: recv_fixed called on transmit pipe\n"); + printk("DBRI: recv_fixed called on transmit pipe %d\n", pipe); return; } @@ -668,37 +840,46 @@ static void recv_fixed(struct dbri *dbri, int pipe, __u32 *ptr) } +/* xmit_on_pipe() / recv_on_pipe() + * + * Transmit/receive data on a "long" pipe - i.e, one associated + * with a DMA buffer. + * + * Only pipe numbers 0-15 can be used in this mode. + * + * Both functions take pointer/len arguments pointing to a data buffer, + * and both provide callback functions (may be NULL) to notify higher + * level code when transmission/reception is complete. + * + * Both work by building chains of descriptors which identify the + * data buffers. Buffers too large for a single descriptor will + * be spread across multiple descriptors. + */ + static void xmit_on_pipe(struct dbri *dbri, int pipe, void * buffer, unsigned int len, void (*callback)(void *, int), void * callback_arg) { volatile int *cmd; + register unsigned int flags; int td = 0; int first_td = -1; - int last_td; + int last_td = -1; __u32 dvma_buffer; if (pipe < 0 || pipe > 15) { - printk("DBRI: xmit_on_pipe called with illegal pipe number\n"); + printk("DBRI: xmit_on_pipe: Illegal pipe number\n"); return; } if (dbri->pipes[pipe].sdp == 0) { - printk("DBRI: xmit_on_pipe called on uninitialized pipe\n"); + printk("DBRI: xmit_on_pipe: Uninitialized pipe %d\n", pipe); return; } if (! dbri->pipes[pipe].sdp & D_SDP_TO_SER) { - printk("DBRI: xmit_on_pipe called on receive pipe\n"); - return; - } - - /* XXX Fix this XXX - * Should be able to queue multiple buffers to send on a pipe - */ - - if (dbri->pipes[pipe].desc != -1) { - printk("DBRI: xmit_on_pipe called on active pipe\n"); + printk("DBRI: xmit_on_pipe: Called on receive pipe %d\n", + pipe); return; } @@ -707,10 +888,11 @@ static void xmit_on_pipe(struct dbri *dbri, int pipe, while (len > 0) { int mylen; - for (td; td < DBRI_NO_DESCS; td ++) { + for (; td < DBRI_NO_DESCS; td ++) { if (! dbri->descs[td].inuse) break; } if (td == DBRI_NO_DESCS) { + printk("DBRI: xmit_on_pipe: No descriptors\n"); break; } @@ -744,15 +926,10 @@ static void xmit_on_pipe(struct dbri *dbri, int pipe, len -= mylen; } - if (first_td == -1) { - printk("xmit_on_pipe: No descriptors available\n"); + if (first_td == -1 || last_td == -1) { return; } - if (len > 0) { - printk("xmit_on_pipe: Insufficient descriptors; data truncated\n"); - } - dbri->dma->desc[last_td].word1 |= DBRI_TD_I | DBRI_TD_F | DBRI_TD_B; dbri->descs[last_td].buffer = buffer; @@ -760,14 +937,54 @@ static void xmit_on_pipe(struct dbri *dbri, int pipe, dbri->descs[last_td].output_callback = callback; dbri->descs[last_td].output_callback_arg = callback_arg; - dbri->pipes[pipe].desc = first_td; + for (td=first_td; td != -1; td = dbri->descs[td].next) { + dprintk(D_DESC, ("DBRI TD %d: %08x %08x %08x %08x\n", + td, + dbri->dma->desc[td].word1, + dbri->dma->desc[td].ba, + dbri->dma->desc[td].nda, + dbri->dma->desc[td].word4)); + } - cmd = dbri_cmdlock(dbri); + save_and_cli(flags); - *(cmd++) = DBRI_CMD(D_SDP, 0, dbri->pipes[pipe].sdp | D_SDP_P | D_SDP_C); - *(cmd++) = (int) & dbri->dma_dvma->desc[first_td]; + if (pipe_active(dbri, pipe)) { - dbri_cmdsend(dbri, cmd); + /* Pipe is already active - find last TD in use + * and link our first TD onto its end. Then issue + * a CDP command to let the DBRI know there's more data. + */ + + last_td = dbri->pipes[pipe].desc; + while (dbri->descs[last_td].next != -1) + last_td = dbri->descs[last_td].next; + + dbri->descs[last_td].next = first_td; + dbri->dma->desc[last_td].nda = + (int) & dbri->dma_dvma->desc[first_td]; + + cmd = dbri_cmdlock(dbri); + *(cmd++) = DBRI_CMD(D_CDP, 0, pipe); + dbri_cmdsend(dbri,cmd); + + } else { + + /* Pipe isn't active - issue an SDP command to start + * our chain of TDs running. + */ + + dbri->pipes[pipe].desc = first_td; + + cmd = dbri_cmdlock(dbri); + *(cmd++) = DBRI_CMD(D_SDP, 0, + dbri->pipes[pipe].sdp + | D_SDP_P | D_SDP_EVERY | D_SDP_C); + *(cmd++) = (int) & dbri->dma_dvma->desc[first_td]; + dbri_cmdsend(dbri, cmd); + + } + + restore_flags(flags); } static void recv_on_pipe(struct dbri *dbri, int pipe, @@ -776,69 +993,107 @@ static void recv_on_pipe(struct dbri *dbri, int pipe, void * callback_arg) { volatile int *cmd; + int first_rd = -1; + int last_rd = -1; int rd; + __u32 bus_buffer; if (pipe < 0 || pipe > 15) { - printk("DBRI: recv_on_pipe called with illegal pipe number\n"); + printk("DBRI: recv_on_pipe: Illegal pipe number\n"); return; } if (dbri->pipes[pipe].sdp == 0) { - printk("DBRI: recv_on_pipe called on uninitialized pipe\n"); + printk("DBRI: recv_on_pipe: Uninitialized pipe %d\n", pipe); return; } if (dbri->pipes[pipe].sdp & D_SDP_TO_SER) { - printk("DBRI: recv_on_pipe called on transmit pipe\n"); + printk("DBRI: recv_on_pipe: Called on transmit pipe %d\n", + pipe); return; } /* XXX Fix this XXX - * Should be able to queue multiple buffers to send on a pipe + * Should be able to queue multiple buffers to receive on a pipe */ if (dbri->pipes[pipe].desc != -1) { - printk("DBRI: recv_on_pipe called on active pipe\n"); + printk("DBRI: recv_on_pipe: Called on active pipe %d\n", pipe); return; } - /* XXX Fix this XXX - * Use multiple descriptors, if needed, to fit in all the data - */ - - if (len > (1 << 13) - 1) { - printk("recv_on_pipe called with len=%d; truncated\n", len); - len = (1 << 13) - 1; - } - /* Make sure buffer size is multiple of four */ len &= ~3; - for (rd = 0; rd < DBRI_NO_DESCS; rd ++) { - if (! dbri->descs[rd].inuse) break; + bus_buffer = mmu_get_scsi_one(buffer, len, dbri->sdev->my_bus); + + while (len > 0) { + int rd; + int mylen; + + if (len > (1 << 13) - 4) { + mylen = (1 << 13) - 4; + } else { + mylen = len; + } + + for (rd = 0; rd < DBRI_NO_DESCS; rd ++) { + if (! dbri->descs[rd].inuse) break; + } + if (rd == DBRI_NO_DESCS) { + printk("DBRI recv_on_pipe: No descriptors\n"); + break; + } + + dbri->dma->desc[rd].word1 = 0; + dbri->dma->desc[rd].ba = bus_buffer; + dbri->dma->desc[rd].nda = 0; + dbri->dma->desc[rd].word4 = DBRI_RD_B | DBRI_RD_BCNT(mylen); + + dbri->descs[rd].buffer = NULL; + dbri->descs[rd].len = 0; + dbri->descs[rd].input_callback = NULL; + dbri->descs[rd].output_callback = NULL; + dbri->descs[rd].next = -1; + dbri->descs[rd].inuse = 1; + + if (first_rd == -1) first_rd = rd; + if (last_rd != -1) { + dbri->dma->desc[last_rd].nda = + (int) & dbri->dma_dvma->desc[rd]; + dbri->descs[last_rd].next = rd; + } + last_rd = rd; + + bus_buffer += mylen; + len -= mylen; } - if (rd == DBRI_NO_DESCS) { - printk("DBRI xmit_on_pipe: No descriptors available\n"); + + if (last_rd == -1 || first_rd == -1) { return; } - dbri->dma->desc[rd].word1 = 0; - dbri->dma->desc[rd].ba = mmu_get_scsi_one(buffer, len, - dbri->sdev->my_bus); - dbri->dma->desc[rd].nda = 0; - dbri->dma->desc[rd].word4 = DBRI_RD_B | DBRI_RD_BCNT(len); + for (rd=first_rd; rd != -1; rd = dbri->descs[rd].next) { + dprintk(D_DESC, ("DBRI RD %d: %08x %08x %08x %08x\n", + rd, + dbri->dma->desc[rd].word1, + dbri->dma->desc[rd].ba, + dbri->dma->desc[rd].nda, + dbri->dma->desc[rd].word4)); + } - dbri->descs[rd].buffer = buffer; - dbri->descs[rd].len = len; - dbri->descs[rd].input_callback = callback; - dbri->descs[rd].input_callback_arg = callback_arg; + dbri->descs[last_rd].buffer = buffer; + dbri->descs[last_rd].len = len; + dbri->descs[last_rd].input_callback = callback; + dbri->descs[last_rd].input_callback_arg = callback_arg; - dbri->pipes[pipe].desc = rd; + dbri->pipes[pipe].desc = first_rd; cmd = dbri_cmdlock(dbri); - *(cmd++) = DBRI_CMD(D_SDP, 0, dbri->pipes[pipe].sdp | D_SDP_P); - *(cmd++) = (int) & dbri->dma_dvma->desc[rd]; + *(cmd++) = DBRI_CMD(D_SDP, 0, dbri->pipes[pipe].sdp | D_SDP_P | D_SDP_C); + *(cmd++) = (int) & dbri->dma_dvma->desc[first_rd]; dbri_cmdsend(dbri, cmd); } @@ -846,8 +1101,123 @@ static void recv_on_pipe(struct dbri *dbri, int pipe, /* **************************************************************************** +************************** DBRI - CHI interface **************************** +**************************************************************************** + +The CHI is a four-wire (clock, frame sync, data in, data out) time-division +multiplexed serial interface which the DBRI can operate in either master +(give clock/frame sync) or slave (take clock/frame sync) mode. + +*/ + +enum master_or_slave { CHImaster, CHIslave }; + +static void reset_chi(struct dbri *dbri, enum master_or_slave master_or_slave, + int bits_per_frame) +{ + volatile int *cmd; + int val; + static int chi_initialized=0; + + if (!chi_initialized) { + + cmd = dbri_cmdlock(dbri); + + /* Set CHI Anchor: Pipe 16 */ + + val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(16) | D_PIPE(16); + *(cmd++) = DBRI_CMD(D_DTS, 0, val); + *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(16); + *(cmd++) = 0; + + val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(16) | D_PIPE(16); + *(cmd++) = DBRI_CMD(D_DTS, 0, val); + *(cmd++) = 0; + *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(16); + + dbri->pipes[16].sdp = 1; + dbri->pipes[16].nextpipe = 16; + dbri->chi_in_pipe = 16; + dbri->chi_out_pipe = 16; + +#if 0 + chi_initialized ++; +#endif + } else { + int pipe; + + for (pipe = dbri->chi_in_pipe; + pipe != 16; + pipe = dbri->pipes[pipe].nextpipe) { + unlink_time_slot(dbri, pipe, PIPEinput, + 16, dbri->pipes[pipe].nextpipe); + } + for (pipe = dbri->chi_out_pipe; + pipe != 16; + pipe = dbri->pipes[pipe].nextpipe) { + unlink_time_slot(dbri, pipe, PIPEoutput, + 16, dbri->pipes[pipe].nextpipe); + } + + dbri->chi_in_pipe = 16; + dbri->chi_out_pipe = 16; + + cmd = dbri_cmdlock(dbri); + + } + + if (master_or_slave == CHIslave) { + /* Setup DBRI for CHI Slave - receive clock, frame sync (FS) + * + * CHICM = 0 (slave mode, 8 kHz frame rate) + * IR = give immediate CHI status interrupt + * EN = give CHI status interrupt upon change + */ + *(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(0)); + } else { + /* Setup DBRI for CHI Master - generate clock, FS + * + * BPF = bits per 8 kHz frame + * 12.288 MHz / CHICM_divisor = clock rate + * FD = 1 - drive CHIFS on rising edge of CHICK + */ + + int clockrate = bits_per_frame * 8; + int divisor = 12288 / clockrate; + + if (divisor > 255 || divisor * clockrate != 12288) { + printk("DBRI: illegal bits_per_frame in setup_chi\n"); + } + + *(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(divisor) | D_CHI_FD + | D_CHI_BPF(bits_per_frame)); + } + + dbri->chi_bpf = bits_per_frame; + + /* CHI Data Mode + * + * RCE = 0 - receive on falling edge of CHICK + * XCE = 1 - transmit on rising edge of CHICK + * XEN = 1 - enable transmitter + * REN = 1 - enable receiver + */ + + *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0); + + *(cmd++) = DBRI_CMD(D_CDM, 0, D_CDM_XCE|D_CDM_XEN|D_CDM_REN); + + dbri_cmdsend(dbri, cmd); +} + +/* +**************************************************************************** *********************** CS4215 audio codec management ********************** **************************************************************************** + +In the standard SPARC audio configuration, the CS4215 codec is attached +to the DBRI via the CHI interface and few of the DBRI's PIO pins. + */ @@ -872,21 +1242,83 @@ static void mmcodec_default(struct cs4215 *mm) * 2: Serial enable, CHI master, 128 bits per frame, clock 1 * 3: Tests disabled */ - mm->ctrl[0] = CS4215_RSRVD_1; + mm->ctrl[0] = CS4215_RSRVD_1 | CS4215_MLB; mm->ctrl[1] = CS4215_DFR_ULAW | CS4215_FREQ[0].csval; mm->ctrl[2] = CS4215_XCLK | CS4215_BSEL_128 | CS4215_FREQ[0].xtal; mm->ctrl[3] = 0; } +static void mmcodec_setup_pipes(struct dbri *dbri) +{ + /* + * Data mode: + * Pipe 4: Send timeslots 1-4 (audio data) + * Pipe 20: Send timeslots 5-8 (part of ctrl data) + * Pipe 6: Receive timeslots 1-4 (audio data) + * Pipe 21: Receive timeslots 6-7. We can only receive 20 bits via + * interrupt, and the rest of the data (slot 5 and 8) is + * not relevant for us (only for doublechecking). + * + * Control mode: + * Pipe 17: Send timeslots 1-4 (slots 5-8 are readonly) + * Pipe 18: Receive timeslot 1 (clb). + * Pipe 19: Receive timeslot 7 (version). + */ + + setup_pipe(dbri, 4, D_SDP_MEM | D_SDP_TO_SER | D_SDP_MSB); + setup_pipe(dbri, 20, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB); + setup_pipe(dbri, 6, D_SDP_MEM | D_SDP_FROM_SER | D_SDP_MSB); + setup_pipe(dbri, 21, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB); + + setup_pipe(dbri, 17, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB); + setup_pipe(dbri, 18, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB); + setup_pipe(dbri, 19, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB); + + dbri->mm.status = 0; + + recv_fixed(dbri, 18, & dbri->mm.status); + recv_fixed(dbri, 19, & dbri->mm.version); +} + +static void mmcodec_setgain(struct dbri *dbri, int muted) +{ + if (muted || dbri->perchip_info.output_muted) { + dbri->mm.data[0] = 63; + dbri->mm.data[1] = 63; + } else { + int left_gain = (dbri->perchip_info.play.gain / 4) % 64; + int right_gain = (dbri->perchip_info.play.gain / 4) % 64; + + if (dbri->perchip_info.play.balance < AUDIO_MID_BALANCE) { + right_gain *= dbri->perchip_info.play.balance; + right_gain /= AUDIO_MID_BALANCE; + } else { + left_gain *= AUDIO_RIGHT_BALANCE + - dbri->perchip_info.play.balance; + left_gain /= AUDIO_MID_BALANCE; + } + + dprintk(D_MM, ("DBRI: Setting codec gain left: %d right: %d\n", + left_gain, right_gain)); + + dbri->mm.data[0] = CS4215_LE | CS4215_HE | (63 - left_gain); + dbri->mm.data[1] = CS4215_SE | (63 - right_gain); + } + + xmit_fixed(dbri, 20, *(int *)dbri->mm.data); +} + static void mmcodec_init_data(struct dbri *dbri) { + int data_width; + /* * Data mode: * Pipe 4: Send timeslots 1-4 (audio data) - * Pipe 17: Send timeslots 5-8 (part of ctrl data) + * Pipe 20: Send timeslots 5-8 (part of ctrl data) * Pipe 6: Receive timeslots 1-4 (audio data) - * Pipe 20: Receive timeslots 6-7. We can only receive 20 bits via + * Pipe 21: Receive timeslots 6-7. We can only receive 20 bits via * interrupt, and the rest of the data (slot 5 and 8) is * not relevant for us (only for doublechecking). * @@ -896,35 +1328,55 @@ static void mmcodec_init_data(struct dbri *dbri) */ + dbri->regs->reg0 &= ~D_C; /* Disable CHI */ + /* Switch CS4215 to data mode - set PIO3 to 1 */ dbri->regs->reg2 = D_ENPIO | D_PIO1 | D_PIO3 | (dbri->mm.onboard ? D_PIO0 : D_PIO2); - reset_chi(dbri, CHIslave, 0); + reset_chi(dbri, CHIslave, 128); + + /* Note: this next doesn't work for 8-bit stereo, because the two + * channels would be on timeslots 1 and 3, with 2 and 4 idle. + * (See CS4215 datasheet Fig 15) + * + * DBRI non-contiguous mode would be required to make this work. + */ - setup_pipe(dbri, 4, D_SDP_MEM | D_SDP_TO_SER | D_SDP_MSB); - setup_pipe(dbri, 17, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB); - setup_pipe(dbri, 6, D_SDP_MEM | D_SDP_FROM_SER | D_SDP_MSB); - setup_pipe(dbri, 20, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB); + data_width = dbri->perchip_info.play.channels + * dbri->perchip_info.play.precision; - /* Pipes 4 and 6 - Single time slot, 8 bit mono */ + link_time_slot(dbri, 20, PIPEoutput, 16, + 32, dbri->mm.offset + 32); + link_time_slot(dbri, 4, PIPEoutput, 16, + data_width, dbri->mm.offset); + link_time_slot(dbri, 6, PIPEinput, 16, + data_width, dbri->mm.offset); + link_time_slot(dbri, 21, PIPEinput, 16, + 16, dbri->mm.offset + 40); - link_time_slot(dbri, 17, PIPEoutput, 16, 32, 32); - link_time_slot(dbri, 4, PIPEoutput, 17, 8, 128); - link_time_slot(dbri, 6, PIPEinput, 16, 8, 0); - link_time_slot(dbri, 20, PIPEinput, 6, 16, 40); + mmcodec_setgain(dbri, 0); - xmit_fixed(dbri, 17, *(int *)dbri->mm.data); + dbri->regs->reg0 |= D_C; /* Enable CHI */ } /* * Send the control information (i.e. audio format) */ -static void mmcodec_setctrl(struct dbri *dbri) +static int mmcodec_setctrl(struct dbri *dbri) { int i, val; + /* XXX - let the CPU do something useful during these delays */ + + /* Temporarily mute outputs, and wait 1/8000 sec (125 us) + * to make sure this takes. This avoids clicking noises. + */ + + mmcodec_setgain(dbri, 1); + udelay(125); + /* * Enable Control mode: Set DBRI's PIO3 (4215's D/~C) to 0, then wait * 12 cycles <= 12/(5512.5*64) sec = 34.01 usec @@ -952,6 +1404,8 @@ static void mmcodec_setctrl(struct dbri *dbri) * frame sync signal by eight clock cycles. Anybody know why? */ + dbri->regs->reg0 &= ~D_C; /* Disable CHI */ + reset_chi(dbri, CHImaster, 128); /* @@ -961,27 +1415,26 @@ static void mmcodec_setctrl(struct dbri *dbri) * Pipe 19: Receive timeslot 7 (version). */ - setup_pipe(dbri, 17, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB); - setup_pipe(dbri, 18, D_SDP_FIXED | D_SDP_CHANGE | D_SDP_MSB); - setup_pipe(dbri, 19, D_SDP_FIXED | D_SDP_CHANGE | D_SDP_MSB); - - link_time_slot(dbri, 17, PIPEoutput, 16, 32, 128); - link_time_slot(dbri, 18, PIPEinput, 16, 8, 0); - link_time_slot(dbri, 19, PIPEinput, 18, 8, 48); - - recv_fixed(dbri, 18, & dbri->mm.status); - recv_fixed(dbri, 19, & dbri->mm.version); + link_time_slot(dbri, 17, PIPEoutput, 16, + 32, dbri->mm.offset); + link_time_slot(dbri, 18, PIPEinput, 16, + 8, dbri->mm.offset); + link_time_slot(dbri, 19, PIPEinput, 16, + 8, dbri->mm.offset + 48); /* Wait for the chip to echo back CLB (Control Latch Bit) as zero */ dbri->mm.ctrl[0] &= ~CS4215_CLB; xmit_fixed(dbri, 17, *(int *)dbri->mm.ctrl); - i = 1000000; - while ((! dbri->mm.status & CS4215_CLB) && i--); + dbri->regs->reg0 |= D_C; /* Enable CHI */ + + i = 10; + while (((dbri->mm.status & 0xe4) != 0x20) && --i) udelay(125); if (i == 0) { - printk("CS4215 didn't respond to CLB\n"); - return; + dprintk(D_MM, ("DBRI: CS4215 didn't respond to CLB (0x%02x)\n", + dbri->mm.status)); + return -1; } /* Terminate CS4215 control mode - data sheet says @@ -993,6 +1446,10 @@ static void mmcodec_setctrl(struct dbri *dbri) /* Two frames of control info @ 8kHz frame rate = 250 us delay */ udelay(250); + + mmcodec_setgain(dbri, 0); + + return 0; } static int mmcodec_init(struct sparcaudio_driver *drv) @@ -1024,16 +1481,24 @@ static int mmcodec_init(struct sparcaudio_driver *drv) } - /* Now talk to our baby */ - dbri->regs->reg0 |= D_C; /* Enable CHI */ + mmcodec_setup_pipes(dbri); mmcodec_default(&dbri->mm); dbri->mm.version = 0xff; - mmcodec_setctrl(dbri); - if(dbri->mm.version == 0xff) + dbri->mm.offset = dbri->mm.onboard ? 0 : 8; + if (mmcodec_setctrl(dbri) == -1 || dbri->mm.version == 0xff) { + dprintk(D_MM, ("DBRI: CS4215 failed probe at offset %d\n", + dbri->mm.offset)); return -EIO; + } + dprintk(D_MM, ("DBRI: Found CS4215 at offset %d\n", dbri->mm.offset)); + + dbri->perchip_info.play.channels = 1; + dbri->perchip_info.play.precision = 8; + dbri->perchip_info.play.gain = 255; + dbri->perchip_info.play.balance = AUDIO_MID_BALANCE; mmcodec_init_data(dbri); return 0; @@ -1044,37 +1509,25 @@ static int mmcodec_init(struct sparcaudio_driver *drv) **************************************************************************** ******************** Interface with sparcaudio midlevel ******************** **************************************************************************** -*/ +The sparcaudio midlevel is contained in the file audio.c. It interfaces +to the user process and performs buffering, intercepts SunOS-style ioctl's, +etc. It interfaces to a abstract audio device via a struct sparcaudio_driver. +This code presents such an interface for the DBRI with an attached CS4215. +All our routines are defined, and then comes our struct sparcaudio_driver. -static int dbri_open(struct inode * inode, struct file * file, - struct sparcaudio_driver *drv) -{ - struct dbri *dbri = (struct dbri *)drv->private; - - MOD_INC_USE_COUNT; +*/ - return 0; -} +/******************* sparcaudio midlevel - audio output *******************/ -static void dbri_release(struct inode * inode, struct file * file, - struct sparcaudio_driver *drv) -{ - MOD_DEC_USE_COUNT; -} - -static int dbri_ioctl(struct inode * inode, struct file * file, - unsigned int x, unsigned long y, - struct sparcaudio_driver *drv) -{ - return 0; -} static void dbri_audio_output_callback(void * callback_arg, int status) { struct sparcaudio_driver *drv = callback_arg; - sparcaudio_output_done(drv, 1); + if (status != -1) { + sparcaudio_output_done(drv, 1); + } } static void dbri_start_output(struct sparcaudio_driver *drv, @@ -1082,8 +1535,31 @@ static void dbri_start_output(struct sparcaudio_driver *drv, { struct dbri *dbri = (struct dbri *)drv->private; + dprintk(D_USR, ("DBRI: start audio output buf=%lx/%ld\n", + (unsigned long) buffer, count)); + /* Pipe 4 is audio transmit */ - xmit_on_pipe(dbri, 4, buffer, count, &dbri_audio_output_callback, drv); + + xmit_on_pipe(dbri, 4, buffer, count, + &dbri_audio_output_callback, drv); + +#if 0 + /* Notify midlevel that we're a DMA-capable driver that + * can accept another buffer immediately. We should probably + * check that we've got enough resources (i.e, descriptors) + * available before doing this, but the default midlevel + * settings only buffer 64KB, which we can handle with 16 + * of our DBRI_NO_DESCS (64) descriptors. + * + * This code is #ifdef'ed out because it's caused me more + * problems than it solved. It'd be nice to provide the + * DBRI with a chain of buffers, but the midlevel code is + * so tricky that I really don't want to deal with it. + */ + + sparcaudio_output_done(drv, 2); +#endif + } static void dbri_stop_output(struct sparcaudio_driver *drv) @@ -1093,28 +1569,55 @@ static void dbri_stop_output(struct sparcaudio_driver *drv) reset_pipe(dbri, 4); } +/******************* sparcaudio midlevel - audio input ********************/ + +static void dbri_audio_input_callback(void * callback_arg, int status, + unsigned int len) +{ + struct sparcaudio_driver * drv = + (struct sparcaudio_driver *) callback_arg; + + if (status != -1) { + sparcaudio_input_done(drv, 3); + } +} + static void dbri_start_input(struct sparcaudio_driver *drv, __u8 * buffer, unsigned long len) { + struct dbri *dbri = (struct dbri *)drv->private; + + /* Pipe 6 is audio receive */ + recv_on_pipe(dbri, 6, buffer, len, + &dbri_audio_input_callback, (void *)drv); + dprintk(D_USR, ("DBRI: start audio input buf=%lx/%ld\n", + (unsigned long) buffer, len)); } static void dbri_stop_input(struct sparcaudio_driver *drv) { -} + struct dbri *dbri = (struct dbri *)drv->private; -static void dbri_audio_getdev(struct sparcaudio_driver *drv, - audio_device_t *devptr) -{ + reset_pipe(dbri, 6); } +/******************* sparcaudio midlevel - volume & balance ***************/ + static int dbri_set_output_volume(struct sparcaudio_driver *drv, int volume) { + struct dbri *dbri = (struct dbri *)drv->private; + + dbri->perchip_info.play.gain = volume; + mmcodec_setgain(dbri, 0); + return 0; } static int dbri_get_output_volume(struct sparcaudio_driver *drv) { - return 0; + struct dbri *dbri = (struct dbri *)drv->private; + + return dbri->perchip_info.play.gain; } static int dbri_set_input_volume(struct sparcaudio_driver *drv, int volume) @@ -1139,12 +1642,19 @@ static int dbri_get_monitor_volume(struct sparcaudio_driver *drv) static int dbri_set_output_balance(struct sparcaudio_driver *drv, int balance) { + struct dbri *dbri = (struct dbri *)drv->private; + + dbri->perchip_info.play.balance = balance; + mmcodec_setgain(dbri, 0); + return 0; } static int dbri_get_output_balance(struct sparcaudio_driver *drv) { - return 0; + struct dbri *dbri = (struct dbri *)drv->private; + + return dbri->perchip_info.play.balance; } static int dbri_set_input_balance(struct sparcaudio_driver *drv, int balance) @@ -1157,107 +1667,205 @@ static int dbri_get_input_balance(struct sparcaudio_driver *drv) return 0; } +static int dbri_set_output_muted(struct sparcaudio_driver *drv, int mute) +{ + struct dbri *dbri = (struct dbri *)drv->private; + + dbri->perchip_info.output_muted = mute; + + return 0; +} + +static int dbri_get_output_muted(struct sparcaudio_driver *drv) +{ + struct dbri *dbri = (struct dbri *)drv->private; + + return dbri->perchip_info.output_muted; +} + +/******************* sparcaudio midlevel - encoding format ****************/ + static int dbri_set_output_channels(struct sparcaudio_driver *drv, int chan) { + struct dbri *dbri = (struct dbri *)drv->private; + + switch (chan) { + case 0: + return 0; + case 1: + dbri->mm.ctrl[1] &= ~CS4215_DFR_STEREO; + break; + case 2: + dbri->mm.ctrl[1] |= CS4215_DFR_STEREO; + break; + default: + return -1; + } + + dbri->perchip_info.play.channels = chan; + mmcodec_setctrl(dbri); + mmcodec_init_data(dbri); return 0; } static int dbri_get_output_channels(struct sparcaudio_driver *drv) { - return 0; + struct dbri *dbri = (struct dbri *)drv->private; + + return dbri->perchip_info.play.channels; } static int dbri_set_input_channels(struct sparcaudio_driver *drv, int chan) { - return 0; + return dbri_set_output_channels(drv, chan); } static int dbri_get_input_channels(struct sparcaudio_driver *drv) { - return 0; + return dbri_get_output_channels(drv); } static int dbri_set_output_precision(struct sparcaudio_driver *drv, int prec) { - return 8; + return 0; } static int dbri_get_output_precision(struct sparcaudio_driver *drv) { - return 8; + struct dbri *dbri = (struct dbri *)drv->private; + + return dbri->perchip_info.play.precision; } static int dbri_set_input_precision(struct sparcaudio_driver *drv, int prec) { - return 8; + return 0; } static int dbri_get_input_precision(struct sparcaudio_driver *drv) { - return 8; -} + struct dbri *dbri = (struct dbri *)drv->private; -static int dbri_set_output_port(struct sparcaudio_driver *drv, int port) -{ - return 0; + return dbri->perchip_info.play.precision; } -static int dbri_get_output_port(struct sparcaudio_driver *drv) +static int dbri_set_output_encoding(struct sparcaudio_driver *drv, int enc) { + struct dbri *dbri = (struct dbri *)drv->private; + + /* For ULAW and ALAW, audio.c enforces precision = 8, + * for LINEAR, precision must be 16 + */ + + switch (enc) { + case AUDIO_ENCODING_NONE: + return 0; + case AUDIO_ENCODING_ULAW: + dbri->mm.ctrl[1] &= ~3; + dbri->mm.ctrl[1] |= CS4215_DFR_ULAW; + dbri->perchip_info.play.encoding = enc; + dbri->perchip_info.play.precision = 8; + break; + case AUDIO_ENCODING_ALAW: + dbri->mm.ctrl[1] &= ~3; + dbri->mm.ctrl[1] |= CS4215_DFR_ALAW; + dbri->perchip_info.play.encoding = enc; + dbri->perchip_info.play.precision = 8; + break; + case AUDIO_ENCODING_LINEAR: + dbri->mm.ctrl[1] &= ~3; + dbri->mm.ctrl[1] |= CS4215_DFR_LINEAR16; + dbri->perchip_info.play.encoding = enc; + dbri->perchip_info.play.precision = 16; + break; + default: + return -1; + } + mmcodec_setctrl(dbri); + mmcodec_init_data(dbri); return 0; } -static int dbri_set_input_port(struct sparcaudio_driver *drv, int port) +static int dbri_get_output_encoding(struct sparcaudio_driver *drv) { - return 0; + struct dbri *dbri = (struct dbri *)drv->private; + + return dbri->perchip_info.play.encoding; } -static int dbri_get_input_port(struct sparcaudio_driver *drv) +static int dbri_set_input_encoding(struct sparcaudio_driver *drv, int enc) { - return 0; + return dbri_set_output_encoding(drv, enc); } -static int dbri_set_output_encoding(struct sparcaudio_driver *drv, int enc) +static int dbri_get_input_encoding(struct sparcaudio_driver *drv) { - return 0; + return dbri_get_output_encoding(drv); } -static int dbri_get_output_encoding(struct sparcaudio_driver *drv) +static int dbri_set_output_rate(struct sparcaudio_driver *drv, int rate) { + struct dbri *dbri = (struct dbri *)drv->private; + int i; + + if (rate == 0) { + return 0; + } + + for (i=0; CS4215_FREQ[i].freq; i++) { + if (CS4215_FREQ[i].freq == rate) break; + } + if (CS4215_FREQ[i].freq == 0) { + return -1; + } + + dbri->mm.ctrl[1] &= ~ 0x38; + dbri->mm.ctrl[1] |= CS4215_FREQ[i].csval; + dbri->mm.ctrl[2] &= ~ 0x70; + dbri->mm.ctrl[2] |= CS4215_FREQ[i].xtal; + + dbri->perchip_info.play.sample_rate = rate; + + mmcodec_setctrl(dbri); + mmcodec_init_data(dbri); return 0; } -static int dbri_set_input_encoding(struct sparcaudio_driver *drv, int enc) +static int dbri_get_output_rate(struct sparcaudio_driver *drv) { - return 0; + struct dbri *dbri = (struct dbri *)drv->private; + + return dbri->perchip_info.play.sample_rate; } -static int dbri_get_input_encoding(struct sparcaudio_driver *drv) +static int dbri_set_input_rate(struct sparcaudio_driver *drv, int rate) { - return 0; + return dbri_set_output_rate(drv, rate); } -static int dbri_set_output_rate(struct sparcaudio_driver *drv, int rate) +static int dbri_get_input_rate(struct sparcaudio_driver *drv) { - return 0; + return dbri_get_output_rate(drv); } -static int dbri_get_output_rate(struct sparcaudio_driver *drv) +/******************* sparcaudio midlevel - ports ***********************/ + +static int dbri_set_output_port(struct sparcaudio_driver *drv, int port) { return 0; } -static int dbri_set_input_rate(struct sparcaudio_driver *drv, int rate) +static int dbri_get_output_port(struct sparcaudio_driver *drv) { return 0; } -static int dbri_get_input_rate(struct sparcaudio_driver *drv) +static int dbri_set_input_port(struct sparcaudio_driver *drv, int port) { return 0; } -static int dbri_sunaudio_getdev_sunos(struct sparcaudio_driver *drv) +static int dbri_get_input_port(struct sparcaudio_driver *drv) { return 0; } @@ -1272,17 +1880,66 @@ static int dbri_get_input_ports(struct sparcaudio_driver *drv) return 0; } -static int dbri_set_output_muted(struct sparcaudio_driver *drv, int mute) +/******************* sparcaudio midlevel - driver ID ********************/ + +static void dbri_audio_getdev(struct sparcaudio_driver *drv, + audio_device_t *audinfo) { - return 0; + struct dbri *dbri = (struct dbri *)drv->private; + + strncpy(audinfo->name, "SUNW,DBRI", sizeof(audinfo->name) - 1); + + audinfo->version[0] = dbri->dbri_version; + audinfo->version[1] = '\0'; + + strncpy(audinfo->config, "onboard1", sizeof(audinfo->config) - 1); } -static int dbri_get_output_muted(struct sparcaudio_driver *drv) +static int dbri_sunaudio_getdev_sunos(struct sparcaudio_driver *drv) { - return 0; + return AUDIO_DEV_CODEC; +} + +/******************* sparcaudio midlevel - open & close ******************/ + +static int dbri_open(struct inode * inode, struct file * file, + struct sparcaudio_driver *drv) +{ + MOD_INC_USE_COUNT; + + /* SunOS 5.5.1 audio(7I) man page says: + * "Upon the initial open() of the audio device, the driver + * will reset the data format of the device to the default + * state of 8-bit, 8KHz, mono u-law data." + * + * I've also taken the liberty of setting half gain and + * mid balance, to put the codec in a known state. + */ + + dbri_set_output_channels(drv, 1); + dbri_set_output_encoding(drv, AUDIO_ENCODING_ULAW); + dbri_set_output_rate(drv, 8000); + + dbri_set_output_balance(drv, AUDIO_MID_BALANCE); + dbri_set_output_volume(drv, AUDIO_MAX_GAIN/2); + + return 0; } +static void dbri_release(struct inode * inode, struct file * file, + struct sparcaudio_driver *drv) +{ + MOD_DEC_USE_COUNT; +} +static int dbri_ioctl(struct inode * inode, struct file * file, + unsigned int x, unsigned long y, + struct sparcaudio_driver *drv) +{ + return -EINVAL; +} + +/*********** sparcaudio midlevel - struct sparcaudio_driver ************/ static struct sparcaudio_operations dbri_ops = { dbri_open, @@ -1357,8 +2014,8 @@ void dbri_isdn_init(struct dbri *dbri) setup_pipe(dbri,11, D_SDP_HDLC | D_SDP_TO_SER | D_SDP_LSB); link_time_slot(dbri, 0, PIPEinput, 0, 2, 17); - link_time_slot(dbri, 8, PIPEinput, 8, 8, 0); - link_time_slot(dbri, 9, PIPEinput, 9, 8, 8); + link_time_slot(dbri, 8, PIPEinput, 0, 8, 0); + link_time_slot(dbri, 9, PIPEinput, 8, 8, 8); link_time_slot(dbri, 1, PIPEoutput, 1, 2, 17); link_time_slot(dbri, 10, PIPEoutput, 1, 8, 0); @@ -1375,6 +2032,8 @@ int dbri_get_irqnum(int dev) dbri = (struct dbri *) drivers[dev].private; + tprintk(("dbri_get_irqnum()\n")); + /* On the sparc, the cpu's irq number is only part of the "irq" */ return (dbri->irq & NR_IRQS); } @@ -1389,6 +2048,8 @@ int dbri_get_liu_state(int dev) dbri = (struct dbri *) drivers[dev].private; + tprintk(("dbri_get_liu_state() returns %d\n", dbri->liu_state)); + return dbri->liu_state; } @@ -1404,12 +2065,14 @@ void dbri_liu_init(int dev, void (*callback)(void *), void *callback_arg) dbri = (struct dbri *) drivers[dev].private; + tprintk(("dbri_liu_init()\n")); + /* Set callback for LIU state change */ - dbri->liu_callback = callback; + dbri->liu_callback = callback; dbri->liu_callback_arg = callback_arg; - dbri_isdn_init(dbri); - dbri_liu_activate(dev, 0); + dbri_isdn_init(dbri); + dbri_liu_activate(dev, 0); } void dbri_liu_activate(int dev, int priority) @@ -1424,16 +2087,24 @@ void dbri_liu_activate(int dev, int priority) dbri = (struct dbri *) drivers[dev].private; - cmd = dbri_cmdlock(dbri); + tprintk(("dbri_liu_activate()\n")); - /* Turn on the ISDN TE interface and request activation */ - val = D_NT_IRM_IMM | D_NT_IRM_EN | D_NT_ACT; - *(cmd++) = DBRI_CMD(D_TE, 0, val); + if (dbri->liu_state <= 3) { - dbri_cmdsend(dbri, cmd); + cmd = dbri_cmdlock(dbri); + + /* Turn on the ISDN TE interface and request activation */ + val = D_NT_IRM_IMM | D_NT_IRM_EN | D_NT_ACT; +#ifdef LOOPBACK_D + val |= D_NT_LLB(4); +#endif + *(cmd++) = DBRI_CMD(D_TE, 0, val); - /* Activate the interface */ - dbri->regs->reg0 |= D_T; + dbri_cmdsend(dbri, cmd); + + /* Activate the interface */ + dbri->regs->reg0 |= D_T; + } } void dbri_liu_deactivate(int dev) @@ -1446,8 +2117,14 @@ void dbri_liu_deactivate(int dev) dbri = (struct dbri *) drivers[dev].private; + tprintk(("dbri_liu_deactivate()\n")); + +#if 0 /* Turn off the ISDN TE interface */ dbri->regs->reg0 &= ~D_T; + + dbri->liu_state = 0; +#endif } void dbri_dxmit(int dev, __u8 *buffer, unsigned int count, @@ -1613,6 +2290,11 @@ static int dbri_attach(struct sparcaudio_driver *drv, "DBRI DMA Cmd Block", &dma_dvma); dbri->dma_dvma = (struct dbri_dma *) dma_dvma; + memset((void *) dbri->dma, 0, sizeof(struct dbri_dma)); + + dprintk(D_GEN, ("DBRI: DMA Cmd Block 0x%08x (0x%08x)\n", + (int)dbri->dma, (int)dbri->dma_dvma)); + dbri->dbri_version = sdev->prom_name[9]; dbri->sdev = sdev; @@ -1625,6 +2307,8 @@ static int dbri_attach(struct sparcaudio_driver *drv, "DBRI Registers", sdev->reg_addrs[0].which_io, 0); if (!dbri->regs) { printk(KERN_ERR "DBRI: could not allocate registers\n"); + release_region((unsigned long) dbri->dma, + sizeof(struct dbri_dma)); kfree(drv->private); return -EIO; } @@ -1637,6 +2321,8 @@ static int dbri_attach(struct sparcaudio_driver *drv, if (err) { printk(KERN_ERR "DBRI: Can't get irq %d\n", dbri->irq); sparc_free_io(dbri->regs, dbri->regs_size); + release_region((unsigned long) dbri->dma, + sizeof(struct dbri_dma)); kfree(drv->private); return err; } diff --git a/drivers/sbus/audio/dbri.h b/drivers/sbus/audio/dbri.h index 1cd598743..7b29bf533 100644 --- a/drivers/sbus/audio/dbri.h +++ b/drivers/sbus/audio/dbri.h @@ -46,13 +46,19 @@ struct dbri_dma { struct dbri_mem desc[DBRI_NO_DESCS]; /* Xmit/receive descriptors */ }; +enum in_or_out { PIPEinput, PIPEoutput }; + +enum direction { in, out }; + struct dbri_pipe { u32 sdp; /* SDP command word */ + enum direction direction; int nextpipe; /* Next pipe in linked list */ + int prevpipe; int cycle; /* Offset of timeslot (bits) */ int length; /* Length of timeslot (bits) */ int desc; /* Index of active descriptor*/ - __u32 *recv_fixed_ptr; /* Ptr to receive fixed data */ + volatile __u32 *recv_fixed_ptr; /* Ptr to receive fixed data */ }; struct dbri_desc { @@ -78,10 +84,15 @@ struct dbri { struct dbri_regs *regs; /* dbri HW regs */ int dbri_version; /* 'e' and up is OK */ int dbri_irqp; /* intr queue pointer */ + int wait_seen; struct dbri_pipe pipes[32]; /* DBRI's 32 data pipes */ struct dbri_desc descs[DBRI_NO_DESCS]; + int chi_in_pipe; + int chi_out_pipe; + int chi_bpf; + struct cs4215 mm; /* mmcodec special info */ #if 0 @@ -190,7 +201,7 @@ struct dbri { /* Time Slot defines */ #define D_TS_LEN(v) ((v)<<24) /* Number of bits in this time slot */ #define D_TS_CYCLE(v) ((v)<<14) /* Bit Count at start of TS */ -#define D_TS_DI(v) (1<<13) /* Data Invert */ +#define D_TS_DI (1<<13) /* Data Invert */ #define D_TS_1CHANNEL (0<<10) /* Single Channel / Normal mode */ #define D_TS_MONITOR (2<<10) /* Monitor pipe */ #define D_TS_NONCONTIG (3<<10) /* Non contiguous mode */ @@ -218,8 +229,8 @@ struct dbri { #define D_NT_IFA (1<<10) /* Inhibit Final Activation */ #define D_NT_ACT (1<<9) /* Activate Interface */ #define D_NT_MFE (1<<8) /* Multiframe Enable */ -#define D_NT_RLB(v) (1<<5) /* Remote Loopback */ -#define D_NT_LLB(v) (1<<2) /* Local Loopback */ +#define D_NT_RLB(v) ((v)<<5) /* Remote Loopback */ +#define D_NT_LLB(v) ((v)<<2) /* Local Loopback */ #define D_NT_FACT (1<<1) /* Force Activation */ #define D_NT_ABV (1<<0) /* Activate Bipolar Violation */ diff --git a/drivers/sbus/char/su.c b/drivers/sbus/char/su.c index a79dc6b6f..b80b93e92 100644 --- a/drivers/sbus/char/su.c +++ b/drivers/sbus/char/su.c @@ -1,4 +1,4 @@ -/* $Id: su.c,v 1.20 1999/06/03 15:02:40 davem Exp $ +/* $Id: su.c,v 1.21 1999/06/11 10:23:42 davem Exp $ * su.c: Small serial driver for keyboard/mouse interface on sparc32/PCI * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) @@ -1133,9 +1133,9 @@ static void su_put_char_kbd(unsigned char c) struct su_struct *info = su_table; int lsr; - if (!info->port_type != SU_PORT_KBD) + if (info->port_type != SU_PORT_KBD) ++info; - if (!info->port_type != SU_PORT_KBD) + if (info->port_type != SU_PORT_KBD) return; do { @@ -1151,9 +1151,9 @@ su_change_mouse_baud(int baud) { struct su_struct *info = su_table; - if (!info->port_type != SU_PORT_MS) + if (info->port_type != SU_PORT_MS) ++info; - if (!info->port_type != SU_PORT_MS) + if (info->port_type != SU_PORT_MS) return; info->cflag &= ~(CBAUDEX | CBAUD); @@ -2215,7 +2215,7 @@ done: */ __initfunc(static __inline__ void show_su_version(void)) { - char *revision = "$Revision: 1.20 $"; + char *revision = "$Revision: 1.21 $"; char *version, *p; version = strchr(revision, ' '); diff --git a/drivers/scsi/in2000.h b/drivers/scsi/in2000.h index 2a6ad2949..6388b2e8e 100644 --- a/drivers/scsi/in2000.h +++ b/drivers/scsi/in2000.h @@ -67,7 +67,7 @@ orl %%ecx, %%ecx \n \ jz 1f \n \ rep \n \ - insw %%dx \n \ + insw (%%dx),%%es:(%%edi) \n \ 1: " \ : "=D" (sp) /* output */ \ : "d" (f), "D" (sp), "c" (i) /* input */ \ @@ -79,7 +79,7 @@ orl %%ecx, %%ecx \n \ jz 1f \n \ rep \n \ - outsw %%dx \n \ + outsw %%ds:(%%esi),(%%dx) \n \ 1: " \ : "=S" (sp) /* output */ \ : "d" (f), "S" (sp), "c" (i) /* input */ \ diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 9bb08ce58..d71509548 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -1926,6 +1926,7 @@ scsi_error_handler(void * data) int rtn; DECLARE_MUTEX_LOCKED(sem); unsigned long flags; + struct fs_struct *fs; lock_kernel(); @@ -1936,16 +1937,18 @@ scsi_error_handler(void * data) */ exit_mm(current); - current->session = 1; current->pgrp = 1; - /* - * FIXME(eric) this is still a child process of the one that did the insmod. - * This needs to be attached to task[0] instead. - */ + + /* Become as one with the init task */ + + exit_fs(current); /* current->fs->count--; */ + fs = init_task.fs; + current->fs = fs; + atomic_inc(&fs->count); siginitsetinv(¤t->blocked, SHUTDOWN_SIGS); - current->fs->umask = 0; + /* * Set the name of this process. diff --git a/drivers/sound/es1370.c b/drivers/sound/es1370.c index ace8f3098..d7579ffa4 100644 --- a/drivers/sound/es1370.c +++ b/drivers/sound/es1370.c @@ -98,6 +98,8 @@ * (micz). From Kim.Berts@fisub.mail.abb.com * 11.05.99 0.22 Implemented the IMIX call to mute recording monitor. * Guenter Geiger <geiger@epy.co.at> + * 15.06.99 0.23 Fix bad allocation bug. + * Thanks to Deti Fliegl <fliegl@in.tum.de> * * some important things missing in Ensoniq documentation: * @@ -531,8 +533,9 @@ static int prog_dmabuf(struct es1370_state *s, struct dmabuf *db, unsigned rate, db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; if (!db->rawbuf) { db->ready = db->mapped = 0; - for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER && !db->rawbuf; order--) - db->rawbuf = (void *)__get_free_pages(GFP_KERNEL, order); + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) + if ((db->rawbuf = (void *)__get_free_pages(GFP_KERNEL, order))) + break; if (!db->rawbuf) return -ENOMEM; db->buforder = order; @@ -2317,7 +2320,7 @@ __initfunc(int init_es1370(void)) if (!pci_present()) /* No PCI bus in this machine! */ return -ENODEV; - printk(KERN_INFO "es1370: version v0.22 time " __TIME__ " " __DATE__ "\n"); + printk(KERN_INFO "es1370: version v0.23 time " __TIME__ " " __DATE__ "\n"); while (index < NR_DEVICE && (pcidev = pci_find_device(PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1370, pcidev))) { if (pcidev->base_address[0] == 0 || diff --git a/drivers/sound/es1371.c b/drivers/sound/es1371.c index d416d0e28..a0f3969e0 100644 --- a/drivers/sound/es1371.c +++ b/drivers/sound/es1371.c @@ -65,6 +65,8 @@ * reported by "Ivan N. Kokshaysky" <ink@jurassic.park.msu.ru> * Note: joystick address handling might still be wrong on archs * other than i386 + * 15.06.99 0.12 Fix bad allocation bug. + * Thanks to Deti Fliegl <fliegl@in.tum.de> * */ @@ -759,8 +761,9 @@ static int prog_dmabuf(struct es1371_state *s, struct dmabuf *db, unsigned rate, db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; if (!db->rawbuf) { db->ready = db->mapped = 0; - for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER && !db->rawbuf; order--) - db->rawbuf = (void *)__get_free_pages(GFP_KERNEL, order); + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) + if ((db->rawbuf = (void *)__get_free_pages(GFP_KERNEL, order))) + break; if (!db->rawbuf) return -ENOMEM; db->buforder = order; @@ -2732,7 +2735,7 @@ __initfunc(int init_es1371(void)) if (!pci_present()) /* No PCI bus in this machine! */ return -ENODEV; - printk(KERN_INFO "es1371: version v0.11 time " __TIME__ " " __DATE__ "\n"); + printk(KERN_INFO "es1371: version v0.12 time " __TIME__ " " __DATE__ "\n"); while (index < NR_DEVICE && (pcidev = pci_find_device(PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1371, pcidev))) { if (pcidev->base_address[0] == 0 || diff --git a/drivers/sound/sonicvibes.c b/drivers/sound/sonicvibes.c index af5ddcea0..83c5c88c0 100644 --- a/drivers/sound/sonicvibes.c +++ b/drivers/sound/sonicvibes.c @@ -68,6 +68,8 @@ * SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; * Alpha fixes reported by Peter Jones <pjones@redhat.com> * Note: dmaio hack might still be wrong on archs other than i386 + * 15.06.99 0.15 Fix bad allocation bug. + * Thanks to Deti Fliegl <fliegl@in.tum.de> * */ @@ -699,8 +701,9 @@ static int prog_dmabuf(struct sv_state *s, unsigned rec) db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; if (!db->rawbuf) { db->ready = db->mapped = 0; - for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER && !db->rawbuf; order--) - db->rawbuf = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA, order); + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) + if ((db->rawbuf = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA, order))) + break; if (!db->rawbuf) return -ENOMEM; db->buforder = order; @@ -2321,7 +2324,7 @@ __initfunc(int init_sonicvibes(void)) if (!pci_present()) /* No PCI bus in this machine! */ return -ENODEV; - printk(KERN_INFO "sv: version v0.14 time " __TIME__ " " __DATE__ "\n"); + printk(KERN_INFO "sv: version v0.15 time " __TIME__ " " __DATE__ "\n"); #if 0 if (!(wavetable_mem = __get_free_pages(GFP_KERNEL, 20-PAGE_SHIFT))) printk(KERN_INFO "sv: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n"); diff --git a/drivers/sound/vidc.c b/drivers/sound/vidc.c index 36a4eafb1..04fcd3413 100644 --- a/drivers/sound/vidc.c +++ b/drivers/sound/vidc.c @@ -24,7 +24,7 @@ void vidc_update_filler(int format, int channels) #define TYPE(fmt,ch) (((fmt)<<2) | ((ch)&3)) fillertype = TYPE(format, channels); -printk("filler type: %X\n", fillertype); + switch (fillertype) { default: @@ -61,10 +61,13 @@ void attach_vidc(struct address_info *hw_config) sprintf(name, "VIDC %d-bit sound", hw_config->card_subtype); conf_printf(name, hw_config); + memset(dma_buf, 0, sizeof(dma_buf)); for (i = 0; i < 2; i++) { dma_buf[i] = get_free_page(GFP_KERNEL); + if (!dma_buf[i]) + goto nomem; dma_pbuf[i] = virt_to_phys(dma_buf[i]); } @@ -78,9 +81,16 @@ void attach_vidc(struct address_info *hw_config) printk(KERN_ERR "VIDCsound: can't allocate DMA interrupt\n"); return; } + // vidc_synth_init(hw_config); vidc_audio_init(hw_config); vidc_mixer_init(hw_config); + return; + +nomem: + for (i = 0; i < 2; i++) + free_page(dma_buf[i]); + printk(KERN_ERR "VIDCsound: can't allocate required buffers\n"); } int probe_vidc(struct address_info *hw_config) diff --git a/drivers/sound/vidc_audio.c b/drivers/sound/vidc_audio.c index 7aa275084..3a1f162fb 100644 --- a/drivers/sound/vidc_audio.c +++ b/drivers/sound/vidc_audio.c @@ -9,6 +9,7 @@ #include <asm/hardware.h> #include <asm/io.h> #include <asm/system.h> +#include <asm/iomd.h> #include "sound_config.h" #include "vidc.h" @@ -42,7 +43,6 @@ int vidc_audio_set_volume(int newvol) static int vidc_audio_set_bits(int fmt) { -printk("setting format: %d\n", fmt); switch (fmt) { case AFMT_QUERY: @@ -219,10 +219,11 @@ static void vidc_audio_output_block(int dev, unsigned long buf, int total_count, if (!(adev->flags & DMA_ACTIVE)) { unsigned long flags; -printk("kicking output: %lX+%lX [%lX]\n", dma_start, dma_count, *(unsigned long *)dma_start); save_flags_cli(flags); + vidc_sound_dma_irq(0, NULL, NULL); outb(DMA_CR_E | 0x10, IOMD_SD0CR); + restore_flags(flags); } } diff --git a/drivers/sound/vidc_fill.S b/drivers/sound/vidc_fill.S index b8b1e6620..53fc2ed01 100644 --- a/drivers/sound/vidc_fill.S +++ b/drivers/sound/vidc_fill.S @@ -9,6 +9,7 @@ #include <linux/linkage.h> #include <asm/assembler.h> #include <asm/hardware.h> +#include <asm/iomd.h> .text diff --git a/drivers/sound/waveartist.c b/drivers/sound/waveartist.c index 5a707a4b7..85a9efaec 100644 --- a/drivers/sound/waveartist.c +++ b/drivers/sound/waveartist.c @@ -40,12 +40,20 @@ #include <linux/delay.h> #include <linux/smp.h> +#include <asm/dec21285.h> #include <asm/hardware.h> #include "soundmodule.h" #include "sound_config.h" #include "waveartist.h" +#ifndef _ISA_DMA +#define _ISA_DMA(x) (x) +#endif +#ifndef _ISA_IRQ +#define _ISA_IRQ(x) (x) +#endif + #define VNC_TIMER_PERIOD (HZ/4) //check slider 4 times/sec #define MIXER_PRIVATE3_RESET 0x53570000 @@ -141,8 +149,269 @@ typedef struct wavnc_port_info { static int nr_waveartist_devs; static wavnc_info adev_info[MAX_AUDIO_DEV]; + +static int waveartist_mixer_set(wavnc_info *devc, int whichDev, unsigned int level); + +/* + * Corel Netwinder specifics... + */ static struct timer_list vnc_timer; +extern spinlock_t gpio_lock; + +static void +vnc_mute(wavnc_info *devc, int mute) +{ + unsigned long flags; + + spin_lock_irqsave(&gpio_lock, flags); + cpld_modify(CPLD_UNMUTE, mute ? 0 : CPLD_UNMUTE); + spin_unlock_irqrestore(&gpio_lock, flags); + + devc->mute_state = mute; +} + +static int +vnc_volume_slider(wavnc_info *devc) +{ + static signed int old_slider_volume; + unsigned long flags; + signed int volume = 255; + + *CSR_TIMER1_LOAD = 0x00ffffff; + + save_flags(flags); + cli(); + + outb(0xFF, 0x201); + *CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV1; + + while (volume && (inb(0x201) & 0x01)) + volume--; + + *CSR_TIMER1_CNTL = 0; + + restore_flags(flags); + + volume = 0x00ffffff - *CSR_TIMER1_VALUE; + + +#ifndef REVERSE + volume = 150 - (volume >> 5); +#else + volume = (volume >> 6) - 25; +#endif + + if (volume < 0) + volume = 0; + + if (volume > 100) + volume = 100; + + /* + * slider quite often reads +-8, so debounce this random noise + */ + if ((volume - old_slider_volume) > 7 || + (old_slider_volume - volume) > 7) { + old_slider_volume = volume; + + DEB(printk("Slider volume: %d.\n", old_slider_volume)); + } + + return old_slider_volume; +} + +static int +vnc_slider(wavnc_info *devc) +{ + signed int slider_volume; + unsigned int temp; + + /* + * read the "buttons" state. + * Bit 4 = handset present, + * Bit 5 = offhook + */ + // the state should be "querable" via a private IOCTL call + temp = inb(0x201) & 0x30; + + if (!devc->handset_mute_sw && + (temp ^ devc->handset_state) & VNC_INTERNAL_MIC) { + devc->handset_state = temp; + devc->handset_mute_sw = 0; + + vnc_mute(devc, (temp & VNC_INTERNAL_MIC) ? 1 : 0); + } + + slider_volume = vnc_volume_slider(devc); + + /* + * If we're using software controlled volume, and + * the slider moves by more than 20%, then we + * switch back to slider controlled volume. + */ + if (devc->slider_vol > slider_volume) { + if (devc->slider_vol - slider_volume > 20) + devc->use_slider = 1; + } else { + if (slider_volume - devc->slider_vol > 20) + devc->use_slider = 1; + } + + /* + * use only left channel + */ + temp = levels[SOUND_MIXER_VOLUME] & 0xFF; + + if (slider_volume != temp && devc->use_slider) { + devc->slider_vol = slider_volume; + + waveartist_mixer_set(devc, SOUND_MIXER_VOLUME, + slider_volume | slider_volume << 8); + + return 1; + } + + return 0; +} + +static void +vnc_slider_tick(unsigned long data) +{ + int next_timeout; + + if (vnc_slider(adev_info + data)) + next_timeout = 5; // mixer reported change + else + next_timeout = VNC_TIMER_PERIOD; + + mod_timer(&vnc_timer, jiffies + next_timeout); +} + +static int +vnc_private_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc; + int val, temp; + + if (cmd == SOUND_MIXER_PRIVATE1) { + /* + * Use this call to override the automatic handset + * behaviour - ignore handset. + * bit 7 = total control over handset - do not + * to plug/unplug + * bit 4 = internal mic + * bit 0 = mute internal speaker + */ + if (get_user(val, (int *)arg)) + return -EFAULT; + + devc->handset_mute_sw = val; + + temp = val & VNC_INTERNAL_SPKR; + if (devc->mute_state != temp) + vnc_mute(devc, temp); + + devc->handset_state = val & VNC_INTERNAL_MIC; + waveartist_mixer_set(devc, SOUND_MIXER_RECSRC, SOUND_MASK_MIC); + return 0; + } +#if 0 + if (cmd == SOUND_MIXER_PRIVATE2) { +#define VNC_SOUND_PAUSE 0x53 //to pause the DSP +#define VNC_SOUND_RESUME 0x57 //to unpause the DSP + int val; + + val = *(int *) arg; + + printk("MIXER_PRIVATE2: passed parameter = 0x%X.\n",val); + if (val == VNC_SOUND_PAUSE) { + wa_sendcmd(0x16); //PAUSE the ADC + } else if (val == VNC_SOUND_RESUME) { + wa_sendcmd(0x18); //RESUME the ADC + } else { + return -EINVAL; //invalid parameters... + } + return 0; + } + + if (cmd == SOUND_MIXER_PRIVATE3) { + long unsigned flags; + int mixer_reg[15]; //reg 14 is actually a command: read,write,reset + + int val; + int i; + + val = *(int *) arg; + + if (verify_area(VERIFY_READ, (void *) val, sizeof(mixer_reg) == -EFAULT)) + return (-EFAULT); + + memcpy_fromfs(&mixer_reg, (void *) val, sizeof(mixer_reg)); + + if (mixer_reg[0x0E] == MIXER_PRIVATE3_RESET) { //reset command?? + wavnc_mixer_reset(devc); + return (0); + } else if (mixer_reg[0x0E] == MIXER_PRIVATE3_WRITE) { //write command?? +// printk("WaveArtist Mixer: Private write command.\n"); + + wa_sendcmd(0x32); //Pair1 - word 1 and 5 + wa_sendcmd(mixer_reg[0]); + wa_sendcmd(mixer_reg[4]); + + wa_sendcmd(0x32); //Pair2 - word 2 and 6 + wa_sendcmd(mixer_reg[1]); + wa_sendcmd(mixer_reg[5]); + + wa_sendcmd(0x32); //Pair3 - word 3 and 7 + wa_sendcmd(mixer_reg[2]); + wa_sendcmd(mixer_reg[6]); + + wa_sendcmd(0x32); //Pair4 - word 4 and 8 + wa_sendcmd(mixer_reg[3]); + wa_sendcmd(mixer_reg[7]); + + wa_sendcmd(0x32); //Pair5 - word 9 and 10 + wa_sendcmd(mixer_reg[8]); + wa_sendcmd(mixer_reg[9]); + + wa_sendcmd(0x0031); //set left and right PCM + wa_sendcmd(mixer_reg[0x0A]); + wa_sendcmd(mixer_reg[0x0B]); + + wa_sendcmd(0x0131); //set left and right FM + wa_sendcmd(mixer_reg[0x0C]); + wa_sendcmd(mixer_reg[0x0D]); + + return 0; + } else if (mixer_reg[0x0E] == MIXER_PRIVATE3_READ) { //read command? +// printk("WaveArtist Mixer: Private read command.\n"); + + //first read all current values... + save_flags(flags); + cli(); + + for (i = 0; i < 14; i++) { + wa_sendcmd((i << 8) + 0x30); // get ready for command nn30H + + while (!(inb(STATR) & CMD_RF)) { + }; //wait for response ready... + + mixer_reg[i] = inw(CMDR); + } + restore_flags(flags); + + if (verify_area(VERIFY_WRITE, (void *) val, sizeof(mixer_reg) == -EFAULT)) + return (-EFAULT); + + memcpy_tofs((void *) val, &mixer_reg, sizeof(mixer_reg)); + return 0; + } else + return -EINVAL; + } +#endif + return -EINVAL; +} static inline void waveartist_set_ctlr(struct address_info *hw, unsigned char clear, unsigned char set) @@ -1368,138 +1637,12 @@ static int waveartist_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) { wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc; -#if 0 - //use this call to override the automatic handset behaviour - ignore handset - //bit 0x80 = total control over handset - do not react to plug/unplug - //bit 0x10 = 1 == internal mic, otherwise handset mic - //bit 0x01 = 1 == mute internal speaker, otherwise unmute - - if (cmd == SOUND_MIXER_PRIVATE1) { - int val, temp; - - val = *(int *) arg; - -// printk("MIXER_PRIVATE1: passed parameter = 0x%X.\n",val); - return -EINVAL; //check if parameter is logical... - - devc->soft_mute_flag = val; - - temp = val & VNC_INTERNAL_SPKR; - if (temp != devc->mute_state) { -// printk("MIXER_PRIVATE1: mute_mono(0x%X).\n",temp); - vnc_mute(devc, temp); - } - -// temp = devc->handset_state; - - // do not check if it is not already in - // the right setting, since we are - // laying about the current state... - -// if ((val & VNC_INTERNAL_MIC) != temp) { - devc->handset_state = val & VNC_INTERNAL_MIC; -// printk("MIXER_PRIVATE1: mixer_set(0x%X).\n",devc->handset_state); - wa_mixer_set(devc, SOUND_MIXER_RECSRC, SOUND_MASK_MIC); -// devc->handset_state = temp; - } - return 0; - } -#if 0 - if (cmd == SOUND_MIXER_PRIVATE2) { -#define VNC_SOUND_PAUSE 0x53 //to pause the DSP -#define VNC_SOUND_RESUME 0x57 //to unpause the DSP - int val; - - val = *(int *) arg; - - printk("MIXER_PRIVATE2: passed parameter = 0x%X.\n",val); - - if (val == VNC_SOUND_PAUSE) { - wa_sendcmd(0x16); //PAUSE the ADC - } else if (val == VNC_SOUND_RESUME) { - wa_sendcmd(0x18); //RESUME the ADC - } else { - return -EINVAL; //invalid parameters... - } - return 0; - } -#endif - if (cmd == SOUND_MIXER_PRIVATE3) { - long unsigned flags; - int mixer_reg[15]; //reg 14 is actually a command: read,write,reset - - int val; - int i; - - val = *(int *) arg; - - if (verify_area(VERIFY_READ, (void *) val, sizeof(mixer_reg) == -EFAULT)) - return (-EFAULT); - - memcpy_fromfs(&mixer_reg, (void *) val, sizeof(mixer_reg)); - - if (mixer_reg[0x0E] == MIXER_PRIVATE3_RESET) { //reset command?? - wavnc_mixer_reset(devc); - return (0); - } else if (mixer_reg[0x0E] == MIXER_PRIVATE3_WRITE) { //write command?? -// printk("WaveArtist Mixer: Private write command.\n"); - - wa_sendcmd(0x32); //Pair1 - word 1 and 5 - wa_sendcmd(mixer_reg[0]); - wa_sendcmd(mixer_reg[4]); + if (cmd == SOUND_MIXER_PRIVATE1 || + cmd == SOUND_MIXER_PRIVATE2 || + cmd == SOUND_MIXER_PRIVATE3) + return vnc_private_ioctl(dev, cmd, arg); - wa_sendcmd(0x32); //Pair2 - word 2 and 6 - wa_sendcmd(mixer_reg[1]); - wa_sendcmd(mixer_reg[5]); - - wa_sendcmd(0x32); //Pair3 - word 3 and 7 - wa_sendcmd(mixer_reg[2]); - wa_sendcmd(mixer_reg[6]); - - wa_sendcmd(0x32); //Pair4 - word 4 and 8 - wa_sendcmd(mixer_reg[3]); - wa_sendcmd(mixer_reg[7]); - - wa_sendcmd(0x32); //Pair5 - word 9 and 10 - wa_sendcmd(mixer_reg[8]); - wa_sendcmd(mixer_reg[9]); - - wa_sendcmd(0x0031); //set left and right PCM - wa_sendcmd(mixer_reg[0x0A]); - wa_sendcmd(mixer_reg[0x0B]); - - wa_sendcmd(0x0131); //set left and right FM - wa_sendcmd(mixer_reg[0x0C]); - wa_sendcmd(mixer_reg[0x0D]); - - return 0; - } else if (mixer_reg[0x0E] == MIXER_PRIVATE3_READ) { //read command? -// printk("WaveArtist Mixer: Private read command.\n"); - - //first read all current values... - save_flags(flags); - cli(); - - for (i = 0; i < 14; i++) { - wa_sendcmd((i << 8) + 0x30); // get ready for command nn30H - - while (!(inb(STATR) & CMD_RF)) { - }; //wait for response ready... - - mixer_reg[i] = inw(CMDR); - } - restore_flags(flags); - - if (verify_area(VERIFY_WRITE, (void *) val, sizeof(mixer_reg) == -EFAULT)) - return (-EFAULT); - - memcpy_tofs((void *) val, &mixer_reg, sizeof(mixer_reg)); - return 0; - } else - return -EINVAL; - } -#endif if (((cmd >> 8) & 0xff) == 'M') { if (_SIOC_DIR(cmd) & _SIOC_WRITE) { int val; @@ -1660,139 +1803,6 @@ nomem: return -1; } -/* - * Corel Netwinder specifics... - */ -static void -vnc_mute(wavnc_info *devc, int mute) -{ - extern spinlock_t gpio_lock; - unsigned long flags; - - spin_lock_irqsave(&gpio_lock, flags); - cpld_modify(CPLD_UNMUTE, mute ? 0 : CPLD_UNMUTE); - spin_unlock_irqrestore(&gpio_lock, flags); - - devc->mute_state = mute; -} - -static int -vnc_volume_slider(wavnc_info *devc) -{ - static signed int old_slider_volume; - unsigned long flags; - signed int volume = 255; - - *CSR_TIMER1_LOAD = 0x00ffffff; - - save_flags(flags); - cli(); - - outb(0xFF, 0x201); - *CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV1; - - while (volume && (inb(0x201) & 0x01)) - volume--; - - *CSR_TIMER1_CNTL = 0; - - restore_flags(flags); - - volume = 0x00ffffff - *CSR_TIMER1_VALUE; - - -#ifndef REVERSE - volume = 150 - (volume >> 5); -#else - volume = (volume >> 6) - 25; -#endif - - if (volume < 0) - volume = 0; - - if (volume > 100) - volume = 100; - - /* - * slider quite often reads +-8, so debounce this random noise - */ - if ((volume - old_slider_volume) > 7 || - (old_slider_volume - volume) > 7) { - old_slider_volume = volume; - - DEB(printk("Slider volume: %d.\n", old_slider_volume)); - } - - return old_slider_volume; -} - -static int -vnc_slider(wavnc_info *devc) -{ - signed int slider_volume; - unsigned int temp; - - /* - * read the "buttons" state. - * Bit 4 = handset present, - * Bit 5 = offhook - */ - // the state should be "querable" via a private IOCTL call - temp = inb(0x201) & 0x30; - - if (!devc->handset_mute_sw && - (temp ^ devc->handset_state) & VNC_INTERNAL_MIC) { - devc->handset_state = temp; - devc->handset_mute_sw = 0; - - vnc_mute(devc, (temp & VNC_INTERNAL_MIC) ? 1 : 0); - } - - slider_volume = vnc_volume_slider(devc); - - /* - * If we're using software controlled volume, and - * the slider moves by more than 20%, then we - * switch back to slider controlled volume. - */ - if (devc->slider_vol > slider_volume) { - if (devc->slider_vol - slider_volume > 20) - devc->use_slider = 1; - } else { - if (slider_volume - devc->slider_vol > 20) - devc->use_slider = 1; - } - - /* - * use only left channel - */ - temp = levels[SOUND_MIXER_VOLUME] & 0xFF; - - if (slider_volume != temp && devc->use_slider) { - devc->slider_vol = slider_volume; - - waveartist_mixer_set(devc, SOUND_MIXER_VOLUME, - slider_volume | slider_volume << 8); - - return 1; - } - - return 0; -} - -static void -vnc_slider_tick(unsigned long data) -{ - int next_timeout; - - if (vnc_slider(adev_info + data)) - next_timeout = 5; // mixer reported change - else - next_timeout = VNC_TIMER_PERIOD; - - mod_timer(&vnc_timer, jiffies + next_timeout); -} - int probe_waveartist(struct address_info *hw_config) { @@ -1808,12 +1818,12 @@ probe_waveartist(struct address_info *hw_config) return 0; } - if (hw_config->irq > 31 || hw_config->irq < 16) { + if (hw_config->irq > _ISA_IRQ(15) || hw_config->irq < _ISA_IRQ(0)) { printk("WaveArtist: Bad IRQ %d\n", hw_config->irq); return 0; } - if (hw_config->dma != 3) { + if (hw_config->dma != _ISA_DMA(3)) { printk("WaveArtist: Bad DMA %d\n", hw_config->dma); return 0; } diff --git a/drivers/sound/waveartist.h b/drivers/sound/waveartist.h index 360bc95f1..45b62d1b5 100644 --- a/drivers/sound/waveartist.h +++ b/drivers/sound/waveartist.h @@ -2,16 +2,11 @@ // def file for Rockwell RWA010 chip set, as installed in Corel NetWinder //registers -#define WA_BASE 0 -//x250 - -#define CMDR WA_BASE+0 -#define DATR WA_BASE+2 - -#define CTLR WA_BASE+4 -#define STATR WA_BASE+5 - -#define IRQSTAT WA_BASE+12 +#define CMDR 0 +#define DATR 2 +#define CTLR 4 +#define STATR 5 +#define IRQSTAT 12 //bit defs //reg STATR diff --git a/drivers/usb/acm.c b/drivers/usb/acm.c index d4796e28c..10c837d5a 100644 --- a/drivers/usb/acm.c +++ b/drivers/usb/acm.c @@ -50,7 +50,7 @@ static struct acm_state static_acm_state; spinlock_t usb_acm_lock = SPIN_LOCK_UNLOCKED; -static int acm_irq(int state, void *__buffer, void *dev_id) +static int acm_irq(int state, void *__buffer, int len, void *dev_id) { // unsigned char *data = __buffer; struct acm_state *acm = &static_acm_state; diff --git a/drivers/usb/audio.c b/drivers/usb/audio.c index 45a276772..9743ec89e 100644 --- a/drivers/usb/audio.c +++ b/drivers/usb/audio.c @@ -27,7 +27,7 @@ static struct usb_driver usb_audio_driver = }; -static int usb_audio_irq(int state, void *buffer, void *dev_id) +static int usb_audio_irq(int state, void *buffer, int len, void *dev_id) { struct usb_audio *aud = (struct usb_audio*) dev_id; return 1; diff --git a/drivers/usb/cpia.c b/drivers/usb/cpia.c index 87e1e4254..2402d3425 100644 --- a/drivers/usb/cpia.c +++ b/drivers/usb/cpia.c @@ -451,7 +451,7 @@ printk("copying\n"); } } -static int cpia_isoc_irq(int status, void *__buffer, void *dev_id) +static int cpia_isoc_irq(int status, void *__buffer, int len, void *dev_id) { struct usb_cpia *cpia = dev_id; struct usb_device *dev = cpia->dev; diff --git a/drivers/usb/hub.c b/drivers/usb/hub.c index 1cd7d7ccb..0a1ec1f01 100644 --- a/drivers/usb/hub.c +++ b/drivers/usb/hub.c @@ -33,7 +33,7 @@ static int khubd_pid = 0; * the low-level driver that it wants to be re-activated, * or zero to say "I'm done". */ -static int hub_irq(int status, void *__buffer, void *dev_id) +static int hub_irq(int status, void *__buffer, int len, void *dev_id) { struct usb_hub *hub = dev_id; unsigned long flags; diff --git a/drivers/usb/keyboard.c b/drivers/usb/keyboard.c index 5d93a5a84..e87519d9f 100644 --- a/drivers/usb/keyboard.c +++ b/drivers/usb/keyboard.c @@ -92,7 +92,7 @@ usb_kbd_repeat(unsigned long dev_id) } static int -usb_kbd_irq(int state, void *buffer, void *dev_id) +usb_kbd_irq(int state, void *buffer, int len, void *dev_id) { struct usb_keyboard *kbd = (struct usb_keyboard*) dev_id; unsigned long *down = (unsigned long*) buffer; diff --git a/drivers/usb/mouse.c b/drivers/usb/mouse.c index f094c0b0d..a79c10a07 100644 --- a/drivers/usb/mouse.c +++ b/drivers/usb/mouse.c @@ -60,7 +60,7 @@ static struct mouse_state static_mouse_state; spinlock_t usb_mouse_lock = SPIN_LOCK_UNLOCKED; -static int mouse_irq(int state, void *__buffer, void *dev_id) +static int mouse_irq(int state, void *__buffer, int len, void *dev_id) { signed char *data = __buffer; /* finding the mouse is easy when there's only one */ diff --git a/drivers/usb/ohci-hcd.c b/drivers/usb/ohci-hcd.c index 820efc5dc..8db61e08e 100644 --- a/drivers/usb/ohci-hcd.c +++ b/drivers/usb/ohci-hcd.c @@ -102,7 +102,7 @@ static int sohci_int_handler(void * ohci_in, unsigned int ep_addr, int ctrl_len, OHCI_DEBUG( for(i=0; i < data_len; i++ ) printk(" %02x", ((__u8 *) data)[i]);) OHCI_DEBUG( printk(" ret_status: %x\n", status); }) - ret = handler(cc_to_status[status & 0xf], data, dev_id); + ret = handler(cc_to_status[status & 0xf], data, data_len, dev_id); if(ret == 0) return 0; /* 0 .. do not requeue */ if(status > 0) return -1; /* error occured do not requeue ? */ ohci_trans_req(ohci, ep_addr, 0, NULL, data, 8, (__OHCI_BAG) handler, (__OHCI_BAG) dev_id); /* requeue int request */ diff --git a/drivers/usb/ohci.c b/drivers/usb/ohci.c index 4ef570abd..48191e11b 100644 --- a/drivers/usb/ohci.c +++ b/drivers/usb/ohci.c @@ -121,7 +121,7 @@ static struct ohci_td *ohci_add_td_to_ed(struct ohci_td *td, u32 new_dummy; if (ed->tail_td == 0) { - printk("eek! an ED without a dummy_td\n"); + printk(KERN_ERR "eek! an ED without a dummy_td\n"); return td; } @@ -234,8 +234,8 @@ void ohci_add_periodic_ed(struct ohci *ohci, struct ohci_ed *ed, int period) */ int_ed = &root_hub->ed[ms_to_ed_int(period)]; #ifdef OHCI_DEBUG - printk("usb-ohci: Using INT ED queue %d for %dms period\n", - ms_to_ed_int(period), period); + printk(KERN_DEBUG "usb-ohci: Using INT ED queue %d for %dms period\n", + ms_to_ed_int(period), period); #endif spin_lock_irqsave(&ohci_edtd_lock, flags); @@ -264,6 +264,21 @@ inline void ohci_add_isoc_ed(struct ohci *ohci, struct ohci_ed *ed) */ DECLARE_WAIT_QUEUE_HEAD(start_of_frame_wakeup); +static void ohci_wait_sof(struct ohci_regs *regs) +{ + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue(&start_of_frame_wakeup, &wait); + + /* clear the SOF interrupt status and enable it */ + writel(OHCI_INTR_SF, ®s->intrstatus); + writel(OHCI_INTR_SF, ®s->intrenable); + + schedule_timeout(HZ/10); + + remove_wait_queue(&start_of_frame_wakeup, &wait); +} + /* * Guarantee that an ED is safe to be modified by the HCD (us). * @@ -296,21 +311,11 @@ void ohci_wait_for_ed_safe(struct ohci_regs *regs, struct ohci_ed *ed, int ed_ty * at least the next frame. */ if (virt_to_bus(ed) == readl(hw_listcurrent)) { - DECLARE_WAITQUEUE(wait, current); - #ifdef OHCI_DEBUG - printk("Waiting a frame for OHC to finish with ED %p [%x %x %x %x]\n", ed, FIELDS_OF_ED(ed)); + printk(KERN_INFO "Waiting a frame for OHC to finish with ED %p [%x %x %x %x]\n", ed, FIELDS_OF_ED(ed)); #endif + ohci_wait_sof(regs); - add_wait_queue(&start_of_frame_wakeup, &wait); - - /* clear the SOF interrupt status and enable it */ - writel(OHCI_INTR_SF, ®s->intrstatus); - writel(OHCI_INTR_SF, ®s->intrenable); - - schedule_timeout(HZ/10); - - remove_wait_queue(&start_of_frame_wakeup, &wait); } return; /* The ED is now safe */ @@ -334,6 +339,7 @@ void ohci_remove_norm_ed_from_hw(struct ohci *ohci, struct ohci_ed *ed, int ed_t if (ed == NULL || !bus_ed) return; + ed->status |= cpu_to_le32(OHCI_ED_SKIP); switch (ed_type) { case HCD_ED_CONTROL: @@ -343,16 +349,10 @@ void ohci_remove_norm_ed_from_hw(struct ohci *ohci, struct ohci_ed *ed, int ed_t hw_listhead_p = ®s->ed_bulkhead; break; default: - printk("Unknown HCD ED type %d.\n", ed_type); + printk(KERN_ERR "Unknown HCD ED type %d.\n", ed_type); return; } - /* - * Tell the controller to this skip ED and make sure it is not the - * being accessed by the HC as we speak. - */ - ohci_wait_for_ed_safe(regs, ed, ed_type); - bus_cur = readl(hw_listhead_p); if (bus_cur == 0) @@ -364,7 +364,7 @@ void ohci_remove_norm_ed_from_hw(struct ohci *ohci, struct ohci_ed *ed, int ed_t /* if its the head ED, move the head */ if (bus_cur == bus_ed) { - writel(cur->next_ed, hw_listhead_p); + writel(le32_to_cpup(&cur->next_ed), hw_listhead_p); } else if (cur->next_ed != 0) { struct ohci_ed *prev; @@ -373,7 +373,7 @@ void ohci_remove_norm_ed_from_hw(struct ohci *ohci, struct ohci_ed *ed, int ed_t prev = cur; cur = bus_to_virt(le32_to_cpup(&cur->next_ed)); - if (virt_to_bus(cur) == bus_ed) { + if (cur == ed) { /* unlink from the list */ prev->next_ed = cur->next_ed; break; @@ -381,6 +381,11 @@ void ohci_remove_norm_ed_from_hw(struct ohci *ohci, struct ohci_ed *ed, int ed_t } while (cur->next_ed != 0); } + /* + * Make sure this ED is not being accessed by the HC as we speak. + */ + ohci_wait_for_ed_safe(regs, ed, ed_type); + /* clear any links from the ED for safety */ ed->next_ed = 0; @@ -405,6 +410,68 @@ inline void ohci_remove_bulk_ed(struct ohci *ohci, struct ohci_ed *ed) ohci_remove_norm_ed_from_hw(ohci, ed, HCD_ED_BULK); } +/* + * Remove all the EDs which have a given device address from a list. + * Used when the device is unplugged. + * Returns 1 if anything was changed. + */ +static int ohci_remove_device_list(__u32 *headp, int devnum) +{ + struct ohci_ed *ed; + __u32 *prevp = headp; + int removed = 0; + + while (*prevp != 0) { + ed = bus_to_virt(le32_to_cpup(prevp)); + if ((le32_to_cpup(&ed->status) & OHCI_ED_FA) == devnum) { + /* set the controller to skip this one + and remove it from the list */ + ed->status |= cpu_to_le32(OHCI_ED_SKIP); + *prevp = ed->next_ed; + removed = 1; + } else { + prevp = &ed->next_ed; + } + } + wmb(); + + return removed; +} + +/* + * Remove all the EDs for a given device from all lists. + */ +void ohci_remove_device(struct ohci *ohci, int devnum) +{ + unsigned long flags; + __u32 head; + struct ohci_regs *regs = ohci->regs; + struct ohci_device *root_hub=usb_to_ohci(ohci->bus->root_hub); + + spin_lock_irqsave(&ohci_edtd_lock, flags); + + /* Control list */ + head = cpu_to_le32(readl(®s->ed_controlhead)); + if (ohci_remove_device_list(&head, devnum)) + writel(le32_to_cpup(&head), ®s->ed_controlhead); + + /* Bulk list */ + head = cpu_to_le32(readl(®s->ed_bulkhead)); + if (ohci_remove_device_list(&head, devnum)) + writel(le32_to_cpup(&head), ®s->ed_bulkhead); + + /* Interrupt/iso list */ + head = cpu_to_le32(virt_to_bus(&root_hub->ed[ED_INT_32])); + ohci_remove_device_list(&head, devnum); + + /* + * Wait until the start of the next frame to ensure + * that the HC has seen any changes. + */ + ohci_wait_sof(ohci->regs); + + spin_unlock_irqrestore(&ohci_edtd_lock, flags); +} /* * Remove a TD from the given EDs TD list. @@ -494,7 +561,7 @@ static struct ohci_td *ohci_get_free_td(struct ohci_device *dev) } } - printk("usb-ohci: unable to allocate a TD\n"); + printk(KERN_ERR "usb-ohci: unable to allocate a TD\n"); return NULL; } /* ohci_get_free_td() */ @@ -521,7 +588,7 @@ static struct ohci_ed *ohci_get_free_ed(struct ohci_device *dev) } } - printk("usb-ohci: unable to allocate an ED\n"); + printk(KERN_ERR "usb-ohci: unable to allocate an ED\n"); return NULL; } /* ohci_get_free_ed() */ @@ -594,12 +661,12 @@ struct ohci_ed *ohci_fill_ed(struct ohci_device *dev, struct ohci_ed *ed, struct ohci_td *dummy_td; if (ed_head_td(ed) != ed_tail_td(ed)) - printk("Reusing a non-empty ED %p!\n", ed); + printk(KERN_ERR "Reusing a non-empty ED %p!\n", ed); if (!ed->tail_td) { dummy_td = ohci_get_free_td(dev); if (dummy_td == NULL) { - printk("Error allocating dummy TD for ED %p\n", ed); + printk(KERN_ERR "Error allocating dummy TD for ED %p\n", ed); return NULL; /* no dummy available! */ } make_dumb_td(dummy_td); /* flag it as a dummy */ @@ -607,7 +674,7 @@ struct ohci_ed *ohci_fill_ed(struct ohci_device *dev, struct ohci_ed *ed, } else { dummy_td = bus_to_virt(ed_tail_td(ed)); if (!td_dummy(*dummy_td)) - printk("ED %p's dummy %p is screwy\n", ed, dummy_td); + printk(KERN_ERR "ED %p's dummy %p is screwy\n", ed, dummy_td); } /* set the head TD to the dummy and clear the Carry & Halted bits */ @@ -650,13 +717,13 @@ static int ohci_request_irq(struct usb_device *usb, unsigned int pipe, /* Get an ED and TD */ interrupt_ed = ohci_get_free_ed(dev); if (!interrupt_ed) { - printk("Out of EDs on device %p in ohci_request_irq\n", dev); + printk(KERN_ERR "Out of EDs on device %p in ohci_request_irq\n", dev); return -1; } td = ohci_get_free_td(dev); if (!td) { - printk("Out of TDs in ohci_request_irq\n"); + printk(KERN_ERR "Out of TDs in ohci_request_irq\n"); ohci_free_ed(interrupt_ed); return -1; } @@ -710,7 +777,7 @@ static DECLARE_WAIT_QUEUE_HEAD(control_wakeup); * * This function is called from the interrupt handler. */ -static int ohci_control_completed(int stats, void *buffer, void *dev_id) +static int ohci_control_completed(int stats, void *buffer, int len, void *dev_id) { /* pass the TDs completion status back to control_msg */ if (dev_id) { @@ -759,14 +826,14 @@ static int ohci_control_msg(struct usb_device *usb, unsigned int pipe, printk(KERN_DEBUG "ohci_control_msg %p (ohci_dev: %p) pipe %x, cmd %p, data %p, len %d\n", usb, dev, pipe, cmd, data, len); #endif if (!control_ed) { - printk("usb-ohci: couldn't get ED for dev %p\n", dev); + printk(KERN_ERR "usb-ohci: couldn't get ED for dev %p\n", dev); return -1; } /* get a TD to send this control message with */ setup_td = ohci_get_free_td(dev); if (!setup_td) { - printk("usb-ohci: couldn't get TD for dev %p [cntl setup]\n", dev); + printk(KERN_ERR "usb-ohci: couldn't get TD for dev %p [cntl setup]\n", dev); ohci_free_ed(control_ed); return -1; } @@ -799,7 +866,7 @@ static int ohci_control_msg(struct usb_device *usb, unsigned int pipe, /* allocate the next TD */ data_td = ohci_get_free_td(dev); if (!data_td) { - printk("usb-ohci: couldn't get TD for dev %p [cntl data]\n", dev); + printk(KERN_ERR "usb-ohci: couldn't get TD for dev %p [cntl data]\n", dev); ohci_free_td(setup_td); ohci_free_ed(control_ed); return -1; @@ -833,7 +900,7 @@ static int ohci_control_msg(struct usb_device *usb, unsigned int pipe, status_td = ohci_get_free_td(dev); /* TODO check for NULL */ if (!status_td) { - printk("usb-ohci: couldn't get TD for dev %p [cntl status]\n", dev); + printk(KERN_ERR "usb-ohci: couldn't get TD for dev %p [cntl status]\n", dev); ohci_free_td(setup_td); ohci_free_td(data_td); ohci_free_ed(control_ed); @@ -918,15 +985,17 @@ static int ohci_control_msg(struct usb_device *usb, unsigned int pipe, #ifdef OHCI_DEBUG if (completion_status != 0) { - printk(KERN_ERR "ohci_control_msg: %s on cmd %x %x %x %x %x\n", - cc_names[completion_status & 0xf], cmd->requesttype, - cmd->request, cmd->value, cmd->index, cmd->length); + char *what = (completion_status < 0)? "timed out": + cc_names[completion_status & 0xf]; + printk(KERN_ERR "ohci_control_msg: %s on pipe %x cmd %x %x %x %x %x\n", + what, pipe, cmd->requesttype, cmd->request, + cmd->value, cmd->index, cmd->length); } else if (!usb_pipeout(pipe)) { unsigned char *q = data; int i; - printk(KERN_DEBUG "ctrl msg %x %x %x %x %x returned:", + printk(KERN_DEBUG "ctrl msg %x %x %x %x %x on pipe %x returned:", cmd->requesttype, cmd->request, cmd->value, cmd->index, - cmd->length); + cmd->length, pipe); for (i = 0; i < len; ++i) { if (i % 16 == 0) printk("\n" KERN_DEBUG); @@ -1001,8 +1070,12 @@ static struct usb_device *ohci_usb_allocate(struct usb_device *parent) */ static int ohci_usb_deallocate(struct usb_device *usb_dev) { - kfree(usb_to_ohci(usb_dev)); - kfree(usb_dev); + struct ohci_device *dev = usb_to_ohci(usb_dev); + + ohci_remove_device(dev->ohci, usb_dev->devnum); + + /* kfree(usb_to_ohci(usb_dev)); */ + /* kfree(usb_dev); */ return 0; } @@ -1042,13 +1115,13 @@ static int reset_hc(struct ohci *ohci) while ((readl(&ohci->regs->cmdstatus) & OHCI_CMDSTAT_HCR) != 0) { if (!--timeout) { - printk("usb-ohci: USB HC reset timed out!\n"); + printk(KERN_ERR "usb-ohci: USB HC reset timed out!\n"); return -1; } udelay(1); } - printk(KERN_INFO "usb-ohci: HC %p reset.\n", ohci); + printk(KERN_DEBUG "usb-ohci: HC %p reset.\n", ohci); return 0; } /* reset_hc() */ @@ -1080,7 +1153,8 @@ static int start_hc(struct ohci *ohci) * XXX Should fminterval also be set here? * The spec suggests 0x2edf [11,999]. (FIXME: make this a constant) */ - fminterval |= (0x2edf << 16); + /* fminterval |= (0x2edf << 16); */ + fminterval = (10240 << 16) | 11999; writel(fminterval, &ohci->regs->fminterval); /* Start periodic transfers at 90% of fminterval (fmremaining * counts down; this will put them in the first 10% of the @@ -1122,7 +1196,7 @@ static int start_hc(struct ohci *ohci) /* Turn on power to the root hub ports (thanks Roman!) */ writel( OHCI_ROOT_LPSC, &ohci->regs->roothub.status ); - printk("usb-ohci: host controller operational\n"); + printk(KERN_INFO "usb-ohci: host controller operational\n"); return ret; } /* start_hc() */ @@ -1137,7 +1211,7 @@ static void ohci_reset_port(struct ohci *ohci, unsigned int port) /* Don't allow overflows. */ if (port >= MAX_ROOT_PORTS) { - printk("usb-ohci: bad port #%d in ohci_reset_port\n", port); + printk(KERN_ERR "usb-ohci: bad port #%d in ohci_reset_port\n", port); port = MAX_ROOT_PORTS-1; } @@ -1152,7 +1226,7 @@ static void ohci_reset_port(struct ohci *ohci, unsigned int port) status = readl(&ohci->regs->roothub.portstatus[port]); if (status & PORT_PRS) { /* reset failed, try harder? */ - printk("usb-ohci: port %d reset failed, retrying\n", port); + printk(KERN_ERR "usb-ohci: port %d reset failed, retrying\n", port); writel(PORT_PRS, &ohci->regs->roothub.portstatus[port]); wait_ms(50); } @@ -1340,7 +1414,7 @@ static void ohci_reap_donelist(struct ohci *ohci) int cc = OHCI_TD_CC_GET(le32_to_cpup(&td->info)); if (td_dummy(*td)) - printk("yikes! reaping a dummy TD\n"); + printk(KERN_ERR "yikes! reaping a dummy TD\n"); /* FIXME: munge td->info into a future standard status format */ @@ -1382,7 +1456,7 @@ static void ohci_reap_donelist(struct ohci *ohci) /* Check if TD should be re-queued */ if ((td->completed != NULL) && - (td->completed(cc, td->data, td->dev_id))) { + (td->completed(cc, td->data, -1 /* XXX */, td->dev_id))) { /* Mark the TD as active again: * Set the not accessed condition code * Reset the Error count @@ -1598,14 +1672,14 @@ static struct ohci *alloc_ohci(void* mem_base) /* Get the number of ports on the root hub */ usb->maxchild = readl(&ohci->regs->roothub.a) & 0xff; if (usb->maxchild > MAX_ROOT_PORTS) { - printk("usb-ohci: Limited to %d ports\n", MAX_ROOT_PORTS); + printk(KERN_INFO "usb-ohci: Limited to %d ports\n", MAX_ROOT_PORTS); usb->maxchild = MAX_ROOT_PORTS; } if (usb->maxchild < 1) { - printk("usb-ohci: Less than one root hub port? Impossible!\n"); + printk(KERN_ERR "usb-ohci: Less than one root hub port? Impossible!\n"); usb->maxchild = 1; } - printk("usb-ohci: %d root hub ports found\n", usb->maxchild); + printk(KERN_DEBUG "usb-ohci: %d root hub ports found\n", usb->maxchild); /* * Initialize the ED polling "tree" (for simplicity's sake in @@ -1650,14 +1724,13 @@ static struct ohci *alloc_ohci(void* mem_base) writel(0, &ohci->regs->ed_bulkhead); #ifdef OHCI_DEBUG - printk(KERN_INFO "alloc_ohci(): controller\n"); + printk(KERN_DEBUG "alloc_ohci(): controller\n"); show_ohci_status(ohci); #endif #if 0 printk(KERN_DEBUG "leaving alloc_ohci %p\n", ohci); #endif -printk("alloc_ohci done\n"); return ohci; } /* alloc_ohci() */ @@ -1722,7 +1795,7 @@ static int ohci_control_thread(void * __ohci) * This thread doesn't need any user-level access, * so get rid of all of our resources.. */ - printk(KERN_INFO "ohci-control thread code for 0x%p code at 0x%p\n", __ohci, &ohci_control_thread); + printk(KERN_DEBUG "ohci-control thread code for 0x%p code at 0x%p\n", __ohci, &ohci_control_thread); exit_mm(current); exit_files(current); exit_fs(current); @@ -1735,7 +1808,7 @@ static int ohci_control_thread(void * __ohci) * Damn the torpedoes, full speed ahead */ if (start_hc(ohci) < 0) { - printk("usb-ohci: failed to start the controller\n"); + printk(KERN_ERR "usb-ohci: failed to start the controller\n"); release_ohci(ohci); usb_deregister_bus(ohci->bus); printk(KERN_INFO "leaving ohci_control_thread %p\n", __ohci); @@ -1756,7 +1829,7 @@ static int ohci_control_thread(void * __ohci) writel(OHCI_INTR_RHSC, &ohci->regs->intrenable); #endif - printk(KERN_INFO "ohci-control thread sleeping\n"); + printk(KERN_DEBUG "ohci-control thread sleeping\n"); interruptible_sleep_on(&ohci_configure); #ifdef CONFIG_APM if (apm_resume) { @@ -1796,7 +1869,7 @@ static int ohci_control_thread(void * __ohci) reset_hc(ohci); release_ohci(ohci); usb_deregister_bus(ohci->bus); - printk(KERN_INFO "ohci-control thread for 0x%p exiting\n", __ohci); + printk(KERN_DEBUG "ohci-control thread for 0x%p exiting\n", __ohci); return 0; } /* ohci_control_thread() */ @@ -1891,7 +1964,7 @@ static int found_ohci(int irq, void* mem_base) ohci->irq = irq; #ifdef OHCI_DEBUG - printk(KERN_INFO "usb-ohci: forking ohci-control thread for 0x%p\n", ohci); + printk(KERN_DEBUG "usb-ohci: forking ohci-control thread for 0x%p\n", ohci); #endif /* fork off the handler */ @@ -1903,7 +1976,7 @@ static int found_ohci(int irq, void* mem_base) retval = pid; } else { - printk("usb-ohci: Couldn't allocate interrupt %d\n", irq); + printk(KERN_ERR "usb-ohci: Couldn't allocate interrupt %d\n", irq); } release_ohci(ohci); @@ -1931,7 +2004,7 @@ static int init_ohci(struct pci_dev *dev) /* no interrupt won't work... */ if (dev->irq == 0) { - printk("usb-ohci: no irq assigned? check your BIOS settings.\n"); + printk(KERN_ERR "usb-ohci: no irq assigned? check your BIOS settings.\n"); return -ENODEV; } @@ -1944,14 +2017,14 @@ static int init_ohci(struct pci_dev *dev) mem_base = (unsigned long) ioremap_nocache(mem_base, 4096); if (!mem_base) { - printk("Error mapping OHCI memory\n"); + printk(KERN_ERR "Error mapping OHCI memory\n"); return -EFAULT; } MOD_INC_USE_COUNT; #ifdef OHCI_DEBUG - printk("usb-ohci: Warning! Gobs of debugging output has been enabled.\n"); - printk(" Check your kern.debug logs for the bulk of it.\n"); + printk(KERN_INFO "usb-ohci: Warning! Gobs of debugging output has been enabled.\n"); + printk(KERN_INFO " Check your kern.debug logs for the bulk of it.\n"); #endif if (found_ohci(dev->irq, (void *) mem_base) < 0) { @@ -1978,11 +2051,11 @@ int ohci_init(void) /*u8 type;*/ if (sizeof(struct ohci_device) > 4096) { - printk("usb-ohci: struct ohci_device to large\n"); + printk(KERN_ERR "usb-ohci: struct ohci_device to large\n"); return -ENODEV; } - printk("OHCI USB Driver loading\n"); + printk(KERN_INFO "OHCI USB Driver loading\n"); retval = -ENODEV; for (;;) { @@ -2022,7 +2095,7 @@ void cleanup_module(void){ # ifdef CONFIG_APM apm_unregister_callback(&handle_apm_event); # endif - printk("usb-ohci: module unloaded\n"); + printk(KERN_ERR "usb-ohci: module unloaded\n"); } int init_module(void){ diff --git a/drivers/usb/uhci-debug.c b/drivers/usb/uhci-debug.c index 7c577a58f..32549763e 100644 --- a/drivers/usb/uhci-debug.c +++ b/drivers/usb/uhci-debug.c @@ -131,7 +131,7 @@ void show_queue(struct uhci_qh *qh) #if 0 printk(" link = %p, element = %p\n", qh->link, qh->element); #endif - if(!qh->element) { + if(!(qh->element & ~0xF)) { printk(" td 0 = NULL\n"); return; } diff --git a/drivers/usb/uhci.c b/drivers/usb/uhci.c index 73aab5fa1..c03ce5adf 100644 --- a/drivers/usb/uhci.c +++ b/drivers/usb/uhci.c @@ -126,7 +126,7 @@ static int uhci_td_result(struct uhci_device *dev, struct uhci_td *td, unsigned tmp = td->first; printk("uhci_td_result() failed with status %x\n", status); - show_status(dev->uhci); + //show_status(dev->uhci); do { show_td(tmp); if ((tmp->link & 1) || (tmp->link & 2)) @@ -422,7 +422,7 @@ static int uhci_remove_irq(struct usb_device *usb_dev, unsigned int pipe, usb_de /* notify removal */ - td->completed(USB_ST_REMOVED, NULL, td->dev_id); + td->completed(USB_ST_REMOVED, NULL, 0, td->dev_id); /* this is DANGEROUS - not sure whether this is right */ @@ -645,7 +645,7 @@ void uhci_delete_isochronous(struct usb_device *usb_dev, void *_isodesc) */ static DECLARE_WAIT_QUEUE_HEAD(control_wakeup); -static int uhci_control_completed(int status, void *buffer, void *dev_id) +static int uhci_control_completed(int status, void *buffer, int len, void *dev_id) { wake_up(&control_wakeup); return 0; /* Don't re-instate */ @@ -692,7 +692,7 @@ static int uhci_run_control(struct uhci_device *dev, struct uhci_td *first, stru // show_status(dev->uhci); // show_queues(dev->uhci); - schedule_timeout(HZ/10); + schedule_timeout(HZ*5); // control should be empty here... // show_status(dev->uhci); @@ -736,8 +736,7 @@ static int uhci_run_control(struct uhci_device *dev, struct uhci_td *first, stru * information, that's just ridiculously high. Most * control messages have just a few bytes of data. */ -static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, - devrequest *cmd, void *data, int len) +static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, void *cmd, void *data, int len) { struct uhci_device *dev = usb_to_uhci(usb_dev); struct uhci_td *first, *td, *prevtd; @@ -805,17 +804,18 @@ static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, } /* - * Build the final TD for control status + * Build the final TD for control status */ destination ^= (0xE1 ^ 0x69); /* OUT -> IN */ destination |= 1 << 19; /* End in Data1 */ - td->link = 1; /* Terminate */ - td->status = status | (1 << 24); /* IOC */ + td->backptr = &prevtd->link; + td->status = (status /* & ~(3 << 27) */) | (1 << 24); /* no limit on final packet */ td->info = destination | (0x7ff << 21); /* 0 bytes of data */ td->buffer = 0; td->first = first; - td->backptr = &prevtd->link; + td->link = 1; /* Terminate */ + /* Start it up.. */ ret = uhci_run_control(dev, first, td); @@ -841,7 +841,7 @@ static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, } if (uhci_debug && ret) { - __u8 *p = (__u8 *) cmd; + __u8 *p = cmd; printk("Failed cmd - %02X %02X %02X %02X %02X %02X %02X %02X\n", p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); @@ -860,7 +860,7 @@ static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, */ static DECLARE_WAIT_QUEUE_HEAD(bulk_wakeup); -static int uhci_bulk_completed(int status, void *buffer, void *dev_id) +static int uhci_bulk_completed(int status, void *buffer, int len, void *dev_id) { wake_up(&bulk_wakeup); return 0; /* Don't re-instate */ @@ -908,10 +908,11 @@ static int uhci_run_bulk(struct uhci_device *dev, struct uhci_td *first, struct // show_status(dev->uhci); // show_queues(dev->uhci); - schedule_timeout(HZ/10); + schedule_timeout(HZ*5); // show_status(dev->uhci); // show_queues(dev->uhci); + //show_queue(first->qh); remove_wait_queue(&bulk_wakeup, &wait); /* Clean up in case it failed.. */ @@ -1243,6 +1244,7 @@ static void uhci_interrupt_notify(struct uhci *uhci) { struct list_head *head = &uhci->interrupt_list; struct list_head *tmp; + int status; spin_lock(&irqlist_lock); tmp = head->next; @@ -1252,19 +1254,22 @@ static void uhci_interrupt_notify(struct uhci *uhci) next = tmp->next; - if (!(td->status & (1 << 23))) { /* No longer active? */ + if (!((status = td->status) & (1 << 23)) || /* No longer active? */ + ((td->qh->element & ~15) && + !((status = uhci_link_to_td(td->qh->element)->status) & (1 <<23)) && + (status & 0x760000) /* is in error state (Stall, db, babble, timeout, bitstuff) */)) { /* remove from IRQ list */ __list_del(tmp->prev, next); INIT_LIST_HEAD(tmp); - if (td->completed(uhci_map_status((td->status & 0xff)>> 16, 0), - bus_to_virt(td->buffer), td->dev_id)) { + if (td->completed(uhci_map_status(status, 0), bus_to_virt(td->buffer), -1, td->dev_id)) { list_add(&td->irq_list, &uhci->interrupt_list); if (!(td->status & (1 << 25))) { struct uhci_qh *interrupt_qh = td->qh; usb_dotoggle(td->dev, usb_pipeendpoint(td->info)); - td->info |= 1 << 19; /* toggle between data0 and data1 */ + td->info &= ~(1 << 19); /* clear data toggle */ + td->info |= usb_gettoggle(td->dev, usb_pipeendpoint(td->info)) << 19; /* toggle between data0 and data1 */ td->status = (td->status & 0x2f000000) | (1 << 23) | (1 << 24); /* active */ /* Remove then readd? Is that necessary */ @@ -1283,7 +1288,7 @@ static void uhci_interrupt_notify(struct uhci *uhci) /* If completed wants to not reactivate, then it's */ /* responsible for free'ing the TD's and QH's */ /* or another function (such as run_control) */ - } + } tmp = next; } spin_unlock(&irqlist_lock); @@ -1563,6 +1568,7 @@ static int uhci_control_thread(void * __uhci) { struct uhci *uhci = (struct uhci *)__uhci; struct uhci_device * root_hub =usb_to_uhci(uhci->bus->root_hub); + lock_kernel(); request_region(uhci->io_addr, 32, "usb-uhci"); diff --git a/drivers/usb/usb-core.c b/drivers/usb/usb-core.c index f9f73a051..082549b6e 100644 --- a/drivers/usb/usb-core.c +++ b/drivers/usb/usb-core.c @@ -53,7 +53,7 @@ int usb_init(void) usb_acm_init(); # endif # ifdef CONFIG_USB_PRINTER - usb_print_init(); + usb_printer_init(); # endif # ifdef CONFIG_USB_CPIA usb_cpia_init(); diff --git a/drivers/usb/usb.h b/drivers/usb/usb.h index 63ebeffb9..a6bf78e4a 100644 --- a/drivers/usb/usb.h +++ b/drivers/usb/usb.h @@ -242,10 +242,12 @@ struct usb_driver { * until we come up with a common meaning. * void *buffer - This is a pointer to the data used in this * USB transfer. + * int length - This is the number of bytes transferred in or out + * of the buffer by this transfer. (-1 = unknown/unsupported) * void *dev_id - This is a user defined pointer set when the IRQ * is requested that is passed back. */ -typedef int (*usb_device_irq)(int, void *, void *); +typedef int (*usb_device_irq)(int, void *, int, void *); struct usb_operations { struct usb_device *(*allocate)(struct usb_device *); diff --git a/drivers/usb/usb_scsi.c b/drivers/usb/usb_scsi.c index 655045bea..1a3e16b25 100644 --- a/drivers/usb/usb_scsi.c +++ b/drivers/usb/usb_scsi.c @@ -74,7 +74,9 @@ struct us_data { __u8 ep_int; /* interrupt . */ __u8 subclass; /* as in overview */ __u8 protocol; /* .............. */ + __u8 attention_done; /* force attention on first command */ int (*pop)(Scsi_Cmnd *); /* protocol specific do cmd */ + int (*pop_reset)(struct us_data *); /* ................. device reset */ GUID(guid); /* unique dev id */ struct Scsi_Host *host; /* our dummy host data */ Scsi_Host_Template *htmplt; /* own host template */ @@ -142,6 +144,9 @@ static int us_one_transfer(struct us_data *us, int pipe, char *buf, int length) /* we want to retry if the device reported NAK */ if (result == USB_ST_TIMEOUT) { + if (partial != this_xfer) { + return 0; /* I do not like this */ + } if (!maxtry--) break; this_xfer -= partial; @@ -150,6 +155,11 @@ static int us_one_transfer(struct us_data *us, int pipe, char *buf, int length) /* short data - assume end */ result = USB_ST_DATAUNDERRUN; break; + } else if (result == USB_ST_STALL && us->protocol == US_PR_CB) { + if (!maxtry--) + break; + this_xfer -= partial; + buf += partial; } else break; } while ( this_xfer ); @@ -216,27 +226,57 @@ static unsigned int us_transfer_length(Scsi_Cmnd *srb) } -static int pop_CBI_irq(int state, void *buffer, void *dev_id) +static int pop_CBI_irq(int state, void *buffer, int len, void *dev_id) { struct us_data *us = (struct us_data *)dev_id; if (state != USB_ST_REMOVED) { us->ip_data = *(__u16 *)buffer; - us->ip_wanted = 0; + US_DEBUGP("Interrupt Status %x\n", us->ip_data); } - wake_up(&us->ip_waitq); + if (us->ip_wanted) + wake_up(&us->ip_waitq); + us->ip_wanted = 0; /* we dont want another interrupt */ return 0; } + +static int pop_CB_reset(struct us_data *us) +{ + unsigned char cmd[12]; + devrequest dr; + int result; + + dr.requesttype = USB_TYPE_CLASS | USB_RT_INTERFACE; + dr.request = US_CBI_ADSC; + dr.value = 0; + dr.index = us->pusb_dev->ifnum; + dr.length = 12; + memset(cmd, -1, sizeof(cmd)); + cmd[0] = SEND_DIAGNOSTIC; + cmd[1] = 4; + us->pusb_dev->bus->op->control_msg(us->pusb_dev, + usb_sndctrlpipe(us->pusb_dev,0), + &dr, cmd, 12); + + usb_clear_halt(us->pusb_dev, us->ep_in | 0x80); + usb_clear_halt(us->pusb_dev, us->ep_out); + + /* long wait for reset */ + + schedule_timeout(HZ*5); + return 0; +} + static int pop_CB_command(Scsi_Cmnd *srb) { struct us_data *us = (struct us_data *)srb->host_scribble; devrequest dr; unsigned char cmd[16]; int result; - int retry = 1; + int retry = 5; int done_start = 0; while (retry--) { @@ -279,7 +319,8 @@ static int pop_CB_command(Scsi_Cmnd *srb) result = us->pusb_dev->bus->op->control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), &dr, cmd, us->fixedlength); - if (!done_start && us->subclass == US_SC_UFI && cmd[0] == TEST_UNIT_READY && result) { + if (!done_start && (us->subclass == US_SC_UFI /*|| us->subclass == US_SC_8070*/) + && cmd[0] == TEST_UNIT_READY && result) { /* as per spec try a start command, wait and retry */ done_start++; @@ -302,35 +343,47 @@ static int pop_CB_command(Scsi_Cmnd *srb) return result; } -/* Protocol command handlers */ +/* + * Control/Bulk status handler + */ -static int pop_CBI(Scsi_Cmnd *srb) +static int pop_CB_status(Scsi_Cmnd *srb) { struct us_data *us = (struct us_data *)srb->host_scribble; int result; + __u8 status[2]; + devrequest dr; + int retry = 5; - /* run the command */ - - if ((result = pop_CB_command(srb))) { - US_DEBUGP("CBI command %x\n", result); - if (result == USB_ST_STALL || result == USB_ST_TIMEOUT) - return (DID_OK << 16) | 2; - return DID_ABORT << 16; - } - - /* transfer the data */ - - if (us_transfer_length(srb)) { - result = us_transfer(srb, US_DIRECTION(srb->cmnd[0])); - if (result && result != USB_ST_DATAUNDERRUN) { - US_DEBUGP("CBI transfer %x\n", result); + switch (us->protocol) { + case US_PR_CB: + /* get from control */ + + while (retry--) { + dr.requesttype = 0x80 | USB_TYPE_STANDARD | USB_RT_DEVICE; + dr.request = USB_REQ_GET_STATUS; + dr.index = 0; + dr.value = 0; + dr.length = 2; + result = us->pusb_dev->bus->op->control_msg(us->pusb_dev, + usb_rcvctrlpipe(us->pusb_dev,0), + &dr, status, sizeof(status)); + if (result != USB_ST_TIMEOUT) + break; + } + if (result) { + US_DEBUGP("Bad AP status request %d\n", result); return DID_ABORT << 16; } - } - - /* get status */ + US_DEBUGP("Got AP status %x %x\n", status[0], status[1]); + if (srb->cmnd[0] != REQUEST_SENSE && srb->cmnd[0] != INQUIRY && + ( (status[0] & ~3) || status[1])) + return (DID_OK << 16) | 2; + else + return DID_OK << 16; + break; - if (us->protocol == US_PR_CBI) { + case US_PR_CBI: /* get from interrupt pipe */ /* add interrupt transfer, marked for removal */ @@ -367,12 +420,48 @@ static int pop_CBI(Scsi_Cmnd *srb) return DID_ABORT << 16; } return (DID_OK << 16) + ((us->ip_data & 0x300) ? 2 : 0); - } else { - /* get from where? */ } return DID_ERROR << 16; } +/* Protocol command handlers */ + +static int pop_CBI(Scsi_Cmnd *srb) +{ + struct us_data *us = (struct us_data *)srb->host_scribble; + int result; + + /* run the command */ + + if ((result = pop_CB_command(srb))) { + US_DEBUGP("CBI command %x\n", result); + if (result == USB_ST_STALL || result == USB_ST_TIMEOUT) { + return (DID_OK << 16) | 2; + } + return DID_ABORT << 16; + } + + /* transfer the data */ + + if (us_transfer_length(srb)) { + result = us_transfer(srb, US_DIRECTION(srb->cmnd[0])); + if (result && result != USB_ST_DATAUNDERRUN) { + US_DEBUGP("CBI transfer %x\n", result); + return DID_ABORT << 16; + } else if (result == USB_ST_DATAUNDERRUN) { + return DID_OK << 16; + } + } else { + if (!result) { + return DID_OK << 16; + } + } + + /* get status */ + + return pop_CB_status(srb); +} + static int pop_Bulk_reset(struct us_data *us) { devrequest dr; @@ -380,21 +469,20 @@ static int pop_Bulk_reset(struct us_data *us) dr.requesttype = USB_TYPE_CLASS | USB_RT_INTERFACE; dr.request = US_BULK_RESET; - dr.value = US_BULK_RESET_SOFT; + dr.value = US_BULK_RESET_HARD; dr.index = 0; dr.length = 0; - US_DEBUGP("Bulk soft reset\n"); result = us->pusb_dev->bus->op->control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), &dr, NULL, 0); - if (result) { - US_DEBUGP("Bulk soft reset failed %d\n", result); - dr.value = US_BULK_RESET_HARD; - result = us->pusb_dev->bus->op->control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), &dr, NULL, 0); - if (result) - US_DEBUGP("Bulk hard reset failed %d\n", result); - } + if (result) + US_DEBUGP("Bulk hard reset failed %d\n", result); usb_clear_halt(us->pusb_dev, us->ep_in | 0x80); usb_clear_halt(us->pusb_dev, us->ep_out); + + /* long wait for reset */ + + schedule_timeout(HZ*5); + return result; } /* @@ -453,8 +541,6 @@ static int pop_Bulk(Scsi_Cmnd *srb) stall = 0; do { - //usb_settoggle(us->pusb_dev, us->ep_in, 0); /* AAARgh!! */ - US_DEBUGP("Toggle is %d\n", usb_gettoggle(us->pusb_dev, us->ep_in)); result = us->pusb_dev->bus->op->bulk_msg(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_in), &bcs, US_BULK_CS_WRAP_LEN, &partial); @@ -564,6 +650,9 @@ static int us_queuecommand( Scsi_Cmnd *srb , void (*done)(Scsi_Cmnd *)) struct us_data *us = (struct us_data *)srb->host->hostdata[0]; US_DEBUGP("Command wakeup\n"); + if (us->srb) { + /* busy */ + } srb->host_scribble = (unsigned char *)us; us->srb = srb; srb->scsi_done = done; @@ -581,9 +670,12 @@ static int us_abort( Scsi_Cmnd *srb ) return 0; } -static int us_device_reset( Scsi_Cmnd *srb ) +static int us_bus_reset( Scsi_Cmnd *srb ) { - return 0; + struct us_data *us = (struct us_data *)srb->host->hostdata[0]; + + us->pop_reset(us); + return SUCCESS; } static int us_host_reset( Scsi_Cmnd *srb ) @@ -591,10 +683,6 @@ static int us_host_reset( Scsi_Cmnd *srb ) return 0; } -static int us_bus_reset( Scsi_Cmnd *srb ) -{ - return 0; -} #undef SPRINTF #define SPRINTF(args...) { if (pos < (buffer + length)) pos += sprintf (pos, ## args); } @@ -623,9 +711,9 @@ int usb_scsi_proc_info (char *buffer, char **start, off_t offset, int length, in if (inout) return length; - if (!(vendor = usb_string(us->pusb_dev, us->pusb_dev->descriptor.iManufacturer))) + if (!us->pusb_dev || !(vendor = usb_string(us->pusb_dev, us->pusb_dev->descriptor.iManufacturer))) vendor = "?"; - if (!(product = usb_string(us->pusb_dev, us->pusb_dev->descriptor.iProduct))) + if (!us->pusb_dev || !(product = usb_string(us->pusb_dev, us->pusb_dev->descriptor.iProduct))) product = "?"; switch (us->protocol) { @@ -677,7 +765,7 @@ static Scsi_Host_Template my_host_template = { us_queuecommand, NULL, /* eh_strategy */ us_abort, - us_device_reset, + us_bus_reset, us_bus_reset, us_host_reset, NULL, /* abort */ @@ -695,6 +783,25 @@ static Scsi_Host_Template my_host_template = { TRUE /* emulated */ }; +static unsigned char sense_notready[] = { + 0x70, /* current error */ + 0x00, + 0x02, /* not ready */ + 0x00, + 0x00, + 10, /* additional length */ + 0x00, + 0x00, + 0x00, + 0x00, + 0x04, /* not ready */ + 0x03, /* manual intervention */ + 0x00, + 0x00, + 0x00, + 0x00 +}; + static int usbscsi_control_thread(void * __us) { struct us_data *us = (struct us_data *)__us; @@ -710,7 +817,7 @@ static int usbscsi_control_thread(void * __us) exit_files(current); //exit_fs(current); - sprintf(current->comm, "usbscsi%d", us->host_no); + sprintf(current->comm, "usbscsi%d", us->host_number); unlock_kernel(); @@ -727,18 +834,160 @@ static int usbscsi_control_thread(void * __us) switch (action) { case US_ACT_COMMAND : - if (!us->pusb_dev || us->srb->target || us->srb->lun) { + if (us->srb->target || us->srb->lun) { /* bad device */ US_DEBUGP( "Bad device number (%d/%d) or dev %x\n", us->srb->target, us->srb->lun, (unsigned int)us->pusb_dev); us->srb->result = DID_BAD_TARGET << 16; + } else if (!us->pusb_dev) { + + /* our device has gone - pretend not ready */ + + if (us->srb->cmnd[0] == REQUEST_SENSE) { + memcpy(us->srb->request_buffer, sense_notready, sizeof(sense_notready)); + us->srb->result = DID_OK << 16; + } else { + us->srb->result = (DID_OK << 16) | 2; + } } else { US_DEBUG(us_show_command(us->srb)); + + /* check for variable length - do properly if so */ + if (us->filter && us->filter->command) us->srb->result = us->filter->command(us->fdata, us->srb); - else + else if (us->srb->cmnd[0] == START_STOP && + us->pusb_dev->descriptor.idProduct == 0x0001 && + us->pusb_dev->descriptor.idVendor == 0x04e6) + us->srb->result = DID_OK << 16; + else { + unsigned int savelen = us->srb->request_bufflen; + unsigned int saveallocation; + + switch (us->srb->cmnd[0]) { + case REQUEST_SENSE: + if (us->srb->request_bufflen > 18) + us->srb->request_bufflen = 18; + else + break; + saveallocation = us->srb->cmnd[4]; + us->srb->cmnd[4] = 18; + break; + + case INQUIRY: + if (us->srb->request_bufflen > 36) + us->srb->request_bufflen = 36; + else + break; + saveallocation = us->srb->cmnd[4]; + us->srb->cmnd[4] = 36; + break; + + case MODE_SENSE: + if (us->srb->request_bufflen > 4) + us->srb->request_bufflen = 4; + else + break; + saveallocation = us->srb->cmnd[4]; + us->srb->cmnd[4] = 4; + break; + + case LOG_SENSE: + case MODE_SENSE_10: + if (us->srb->request_bufflen > 8) + us->srb->request_bufflen = 8; + else + break; + saveallocation = (us->srb->cmnd[7] << 8) | us->srb->cmnd[8]; + us->srb->cmnd[7] = 0; + us->srb->cmnd[8] = 8; + break; + + default: + break; + } us->srb->result = us->pop(us->srb); + + if (savelen != us->srb->request_bufflen && + us->srb->result == (DID_OK << 16)) { + unsigned char *p = (unsigned char *)us->srb->request_buffer; + unsigned int length; + + /* set correct length and retry */ + switch (us->srb->cmnd[0]) { + case REQUEST_SENSE: + /* simply return 18 bytes */ + p[7] = 10; + length = us->srb->request_bufflen;; + break; + + case INQUIRY: + length = p[4] + 5 > savelen ? savelen : p[4] + 5; + us->srb->cmnd[4] = length; + break; + + case MODE_SENSE: + length = p[0] + 4 > savelen ? savelen : p[0] + 4; + us->srb->cmnd[4] = 4; + break; + + case LOG_SENSE: + length = ((p[2] << 8) + p[3]) + 4 > savelen ? savelen : ((p[2] << 8) + p[3]) + 4; + us->srb->cmnd[7] = length >> 8; + us->srb->cmnd[8] = length; + break; + + case MODE_SENSE_10: + length = ((p[0] << 8) + p[1]) + 8 > savelen ? savelen : ((p[0] << 8) + p[1]) + 8; + us->srb->cmnd[7] = length >> 8; + us->srb->cmnd[8] = length; + break; + } + + US_DEBUGP("Old/New length = %d/%d\n", savelen, length); + + if (us->srb->request_bufflen != length) { + us->srb->request_bufflen = length; + us->srb->result = us->pop(us->srb); + } + /* reset back to original values */ + + us->srb->request_bufflen = savelen; + switch (us->srb->cmnd[0]) { + case REQUEST_SENSE: + case INQUIRY: + case MODE_SENSE: + us->srb->cmnd[4] = saveallocation; + break; + + case LOG_SENSE: + case MODE_SENSE_10: + us->srb->cmnd[7] = saveallocation >> 8; + us->srb->cmnd[8] = saveallocation; + break; + } + } + /* force attention on first command */ + if (!us->attention_done) { + if (us->srb->cmnd[0] == REQUEST_SENSE) { + if (us->srb->result == (DID_OK << 16)) { + unsigned char *p = (unsigned char *)us->srb->request_buffer; + + us->attention_done = 1; + if ((p[2] & 0x0f) != UNIT_ATTENTION) { + p[2] = UNIT_ATTENTION; + p[12] = 0x29; /* power on, reset or bus-reset */ + p[13] = 0; + } + } + } else if (us->srb->cmnd[0] != INQUIRY && + us->srb->result == (DID_OK << 16)) { + us->srb->result |= 2; /* force check condition */ + } + } + } } us->srb->scsi_done(us->srb); + us->srb = NULL; break; case US_ACT_ABORT : @@ -820,7 +1069,7 @@ static int scsi_probe(struct usb_device *dev) if (dev->descriptor.idVendor == 0x04e6 && dev->descriptor.idProduct == 0x0001) { /* shuttle E-USB */ - protocol = US_PR_ZIP; + protocol = US_PR_CB; subclass = US_SC_8070; /* an assumption */ } else if (dev->descriptor.bDeviceClass != 0 || dev->config->altsetting->interface->bInterfaceClass != 8 || @@ -835,11 +1084,15 @@ static int scsi_probe(struct usb_device *dev) usb_string(dev, dev->descriptor.iSerialNumber) ) { make_guid(guid, dev->descriptor.idVendor, dev->descriptor.idProduct, usb_string(dev, dev->descriptor.iSerialNumber)); - for (ss = us_list; ss; ss = ss->next) { - if (GUID_EQUAL(guid, ss->guid)) { - US_DEBUGP("Found existing GUID " GUID_FORMAT "\n", GUID_ARGS(guid)); - break; - } + } else { + make_guid(guid, dev->descriptor.idVendor, dev->descriptor.idProduct, + "0"); + } + for (ss = us_list; ss; ss = ss->next) { + if (!ss->pusb_dev && GUID_EQUAL(guid, ss->guid)) { + US_DEBUGP("Found existing GUID " GUID_FORMAT "\n", GUID_ARGS(guid)); + flags = ss->flags; + break; } } } @@ -865,6 +1118,7 @@ static int scsi_probe(struct usb_device *dev) ss->subclass = interface->bInterfaceSubClass; ss->protocol = interface->bInterfaceProtocol; } + ss->attention_done = 0; /* set the protocol op */ @@ -873,16 +1127,19 @@ static int scsi_probe(struct usb_device *dev) case US_PR_CB: US_DEBUGPX("Control/Bulk\n"); ss->pop = pop_CBI; + ss->pop_reset = pop_CB_reset; break; case US_PR_CBI: US_DEBUGPX("Control/Bulk/Interrupt\n"); ss->pop = pop_CBI; + ss->pop_reset = pop_CB_reset; break; default: US_DEBUGPX("Bulk\n"); ss->pop = pop_Bulk; + ss->pop_reset = pop_Bulk_reset; break; } @@ -907,6 +1164,7 @@ static int scsi_probe(struct usb_device *dev) /* exit if strange looking */ if (usb_set_configuration(dev, dev->config[0].bConfigurationValue) || + usb_set_interface(dev, interface->bInterfaceNumber, 0) || !ss->ep_in || !ss->ep_out || (ss->protocol == US_PR_CBI && ss->ep_int == 0)) { US_DEBUGP("Problems with device\n"); if (ss->host) { @@ -933,13 +1191,8 @@ static int scsi_probe(struct usb_device *dev) /* make unique id if possible */ - if (dev->descriptor.iSerialNumber && - usb_string(dev, dev->descriptor.iSerialNumber) ) { - make_guid(ss->guid, dev->descriptor.idVendor, dev->descriptor.idProduct, - usb_string(dev, dev->descriptor.iSerialNumber)); - } - US_DEBUGP("New GUID " GUID_FORMAT "\n", GUID_ARGS(guid)); + memcpy(ss->guid, guid, sizeof(guid)); /* set class specific stuff */ @@ -986,9 +1239,30 @@ static int scsi_probe(struct usb_device *dev) (struct us_data *)htmplt->proc_dir = ss; - if (ss->protocol == US_PR_CBI) + + if (dev->descriptor.idVendor == 0x04e6 && + dev->descriptor.idProduct == 0x0001) { + devrequest dr; + __u8 qstat[2]; + + /* shuttle E-USB */ + dr.requesttype = 0xC0; + dr.request = 1; + dr.index = 0; + dr.value = 0; + dr.length = 0; + ss->pusb_dev->bus->op->control_msg(ss->pusb_dev, usb_rcvctrlpipe(dev,0), &dr, qstat, 2); + US_DEBUGP("C0 status %x %x\n", qstat[0], qstat[1]); + init_waitqueue_head(&ss->ip_waitq); + ss->pusb_dev->bus->op->request_irq(ss->pusb_dev, + usb_rcvctrlpipe(ss->pusb_dev, ss->ep_int), + pop_CBI_irq, 0, (void *)ss); + interruptible_sleep_on_timeout(&ss->ip_waitq, HZ*5); + + } else if (ss->protocol == US_PR_CBI) init_waitqueue_head(&ss->ip_waitq); + /* start up our thread */ { diff --git a/drivers/usb/usb_scsi_debug.c b/drivers/usb/usb_scsi_debug.c index 2ca847c08..634f4c0f6 100644 --- a/drivers/usb/usb_scsi_debug.c +++ b/drivers/usb/usb_scsi_debug.c @@ -95,7 +95,7 @@ void us_show_command(Scsi_Cmnd *srb) case READ_ELEMENT_STATUS: what = "READ_ELEMENT_STATUS"; break; case SEND_VOLUME_TAG: what = "SEND_VOLUME_TAG"; break; case WRITE_LONG_2: what = "WRITE_LONG_2"; break; - default: what = "??"; break; + default: break; } printk(KERN_DEBUG USB_SCSI "Command %s (%d bytes)\n", what, srb->cmd_len); printk(KERN_DEBUG USB_SCSI " %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", diff --git a/drivers/video/Config.in b/drivers/video/Config.in index 991bed563..06dacf03f 100644 --- a/drivers/video/Config.in +++ b/drivers/video/Config.in @@ -178,11 +178,10 @@ if [ "$CONFIG_FB" = "y" ]; then "$CONFIG_FB_VESA" = "y" -o "$CONFIG_FB_VIRTUAL" = "y" -o \ "$CONFIG_FB_TCX" = "y" -o "$CONFIG_FB_CGTHREE" = "y" -o \ "$CONFIG_FB_CONTROL" = "y" -o "$CONFIG_FB_CLGEN" = "y" -o \ - "$CONFIG_FB_CONTROL" = "y" -o "$CONFIG_FB_CLGEN" = "y" -o \ "$CONFIG_FB_CGFOURTEEN" = "y" -o "$CONFIG_FB_G364" = "y" -o \ "$CONFIG_FB_VIRGE" = "y" -o "$CONFIG_FB_CYBER" = "y" -o \ "$CONFIG_FB_VALKYRIE" = "y" -o "$CONFIG_FB_PLATINUM" = "y" -o \ - "$CONFIG_FB_IGA" = "y" -o "$CONFIG_FB_MATROX" = "y" -o \ + "$CONFIG_FB_IGA" = "y" -o "$CONFIG_FB_MATROX" = "y" -o \ "$CONFIG_FB_CT65550" = "y" -o "$CONFIG_FB_PM2" = "y" -o \ "$CONFIG_FB_SGIVW" = "y" -o "$CONFIG_FB_CYBER2000" = "y" ]; then define_bool CONFIG_FBCON_CFB8 y @@ -196,7 +195,7 @@ if [ "$CONFIG_FB" = "y" ]; then "$CONFIG_FB_CGFOURTEEN" = "m" -o "$CONFIG_FB_G364" = "m" -o \ "$CONFIG_FB_VIRGE" = "m" -o "$CONFIG_FB_CYBER" = "m" -o \ "$CONFIG_FB_VALKYRIE" = "m" -o "$CONFIG_FB_PLATINUM" = "m" -o \ - "$CONFIG_FB_IGA" = "m" -o "$CONFIG_FB_MATROX" = "m" -o \ + "$CONFIG_FB_IGA" = "m" -o "$CONFIG_FB_MATROX" = "m" -o \ "$CONFIG_FB_CT65550" = "m" -o "$CONFIG_FB_PM2" = "m" -o \ "$CONFIG_FB_SGIVW" = "m" -o "$CONFIG_FB_CYBER2000" = "m" ]; then define_bool CONFIG_FBCON_CFB8 m @@ -220,9 +219,9 @@ if [ "$CONFIG_FB" = "y" ]; then "$CONFIG_FB_Q40" = "m" -o \ "$CONFIG_FB_CONTROL" = "m" -o "$CONFIG_FB_CLGEN" = "m" -o \ "$CONFIG_FB_VIRGE" = "m" -o "$CONFIG_FB_CYBER" = "m" -o \ - "$CONFIG_FB_VALKYRIE" = "m" -o "$CONFIG_FB_PLATINUM" = "m" -o \ + "$CONFIG_FB_VALKYRIE" = "m" -o "$CONFIG_FB_PLATINUM" = "m" -o \ "$CONFIG_FB_CT65550" = "m" -o "$CONFIG_FB_MATROX" = "m" -o \ - "$CONFIG_FB_PM2" = "m" -o "$CONFIG_FB_SGIVW" = "m" -o \ + "$CONFIG_FB_PM2" = "y" -o "$CONFIG_FB_SGIVW" = "m" -o \ "$CONFIG_FB_CYBER2000" = "m" ]; then define_bool CONFIG_FBCON_CFB16 m fi diff --git a/drivers/video/acornfb.c b/drivers/video/acornfb.c index e1bc0857c..42485a999 100644 --- a/drivers/video/acornfb.c +++ b/drivers/video/acornfb.c @@ -254,7 +254,9 @@ acornfb_set_timing(struct fb_var_screeninfo *var) bandwidth = var->pixclock * 8 / var->bits_per_pixel; /* 25.175, 4bpp = 79.444ns per byte, 317.776ns per word: fifo = 2,6 */ - if (bandwidth > 71750) + if (bandwidth > 143500) + vidc_ctl |= VIDC_CTRL_FIFO_3_7; + else if (bandwidth > 71750) vidc_ctl |= VIDC_CTRL_FIFO_2_6; else if (bandwidth > 35875) vidc_ctl |= VIDC_CTRL_FIFO_1_5; diff --git a/drivers/video/cyber2000fb.c b/drivers/video/cyber2000fb.c index c24288124..d342304a3 100644 --- a/drivers/video/cyber2000fb.c +++ b/drivers/video/cyber2000fb.c @@ -121,7 +121,7 @@ static const struct res cyber2000_res[] = { 1600, 1200, { 0xff, 0xc7, 0xc9, 0x9f, 0xcf, 0xa0, 0xfe, 0x10, - 0x00, 0x40, + 0x00, 0x40, 0xcf, 0x89, 0xaf, 0xc8, 0x00, 0xbc, 0xf1, 0xe3 }, 0x1f, @@ -154,54 +154,54 @@ static void cyber2000_init_hw(const struct res *res) debug_printf("init vga hw for %dx%d\n", res->xres, res->yres); cyber2000_outb(0xef, 0x3c2); - cyber2000_crtcw(0x0b, 0x11); - cyber2000_attrw(0x00, 0x11); + cyber2000_crtcw(0x11, 0x0b); + cyber2000_attrw(0x11, 0x00); - cyber2000_seqw(0x01, 0x00); + cyber2000_seqw(0x00, 0x01); cyber2000_seqw(0x01, 0x01); - cyber2000_seqw(0x0f, 0x02); - cyber2000_seqw(0x00, 0x03); - cyber2000_seqw(0x0e, 0x04); + cyber2000_seqw(0x02, 0x0f); cyber2000_seqw(0x03, 0x00); + cyber2000_seqw(0x04, 0x0e); + cyber2000_seqw(0x00, 0x03); for (i = 0; i < sizeof(crtc_idx); i++) - cyber2000_crtcw(res->crtc_regs[i], crtc_idx[i]); + cyber2000_crtcw(crtc_idx[i], res->crtc_regs[i]); for (i = 0x0a; i < 0x10; i++) - cyber2000_crtcw(0, i); + cyber2000_crtcw(i, 0); - cyber2000_crtcw(0xff, 0x18); + cyber2000_crtcw(0x18, 0xff); cyber2000_grphw(0x00, 0x00); - cyber2000_grphw(0x00, 0x01); - cyber2000_grphw(0x00, 0x02); - cyber2000_grphw(0x00, 0x03); - cyber2000_grphw(0x00, 0x04); - cyber2000_grphw(0x60, 0x05); - cyber2000_grphw(0x05, 0x06); - cyber2000_grphw(0x0f, 0x07); - cyber2000_grphw(0xff, 0x08); + cyber2000_grphw(0x01, 0x00); + cyber2000_grphw(0x02, 0x00); + cyber2000_grphw(0x03, 0x00); + cyber2000_grphw(0x04, 0x00); + cyber2000_grphw(0x05, 0x60); + cyber2000_grphw(0x06, 0x05); + cyber2000_grphw(0x07, 0x0f); + cyber2000_grphw(0x08, 0xff); for (i = 0; i < 16; i++) cyber2000_attrw(i, i); - cyber2000_attrw(0x01, 0x10); - cyber2000_attrw(0x00, 0x11); - cyber2000_attrw(0x0f, 0x12); - cyber2000_attrw(0x00, 0x13); - cyber2000_attrw(0x00, 0x14); + cyber2000_attrw(0x10, 0x01); + cyber2000_attrw(0x11, 0x00); + cyber2000_attrw(0x12, 0x0f); + cyber2000_attrw(0x13, 0x00); + cyber2000_attrw(0x14, 0x00); for (i = 0; i < sizeof(igs_regs); i += 2) - cyber2000_grphw(igs_regs[i+1], igs_regs[i]); + cyber2000_grphw(igs_regs[i], igs_regs[i+1]); - cyber2000_grphw(res->crtc_ofl, 0x11); + cyber2000_grphw(0x11, res->crtc_ofl); for (i = 0; i < 4; i += 1) - cyber2000_grphw(res->clk_regs[i], 0xb0 + i); + cyber2000_grphw(0xb0 + i, res->clk_regs[i]); - cyber2000_grphw(0x01, 0x90); - cyber2000_grphw(0x80, 0xb9); - cyber2000_grphw(0x00, 0xb9); + cyber2000_grphw(0x90, 0x01); + cyber2000_grphw(0xb9, 0x80); + cyber2000_grphw(0xb9, 0x00); cyber2000_outb(0x56, 0x3ce); i = cyber2000_inb(0x3cf); @@ -311,6 +311,7 @@ cyber2000_accel_clear(struct vc_data *conp, struct display *p, int sy, int sx, cyber2000_outw(height, 0xbf062); switch (p->var.bits_per_pixel) { + case 15: case 16: bgx = ((u16 *)p->dispsw_data)[bgx]; case 8: @@ -415,14 +416,27 @@ cyber2000_setcolreg(u_int regno, u_int red, u_int green, u_int blue, current_par.palette[regno].blue = blue; switch (fb_display[current_par.currcon].var.bits_per_pixel) { +#ifdef FBCON_HAS_CFB8 case 8: cyber2000_outb(regno, 0x3c8); cyber2000_outb(red, 0x3c9); cyber2000_outb(green, 0x3c9); cyber2000_outb(blue, 0x3c9); break; +#endif #ifdef FBCON_HAS_CFB16 + case 15: + if (regno < 32) { + cyber2000_outb(regno << 3, 0x3c8); + cyber2000_outb(red, 0x3c9); + cyber2000_outb(green, 0x3c9); + cyber2000_outb(blue, 0x3c9); + } + if (regno < 16) + current_par.c_table.cfb16[regno] = regno | regno << 5 | regno << 10; + break; + case 16: if (regno < 64) { /* write green */ @@ -464,36 +478,123 @@ cyber2000_setcolreg(u_int regno, u_int red, u_int green, u_int blue, return 0; } -static int cyber2000fb_set_timing(struct fb_var_screeninfo *var) +static void cyber2000fb_calculate_timing(unsigned char *v, struct fb_var_screeninfo *var) { - int width = var->xres_virtual; - int scr_pitch, fetchrow; - int i; - char b, col; + int Htotal, Hdispend, Hblankstart, Hblankend, Hsyncstart, Hsyncend; + int Vtotal, Vdispend, Vblankstart, Vblankend, Vsyncstart, Vsyncend; +#define BIT(v,b1,m,b2) (((v >> b1) & m) << b2) + + Hdispend = var->xres; + Hsyncstart = var->xres + var->right_margin; + Hsyncend = var->xres + var->right_margin + var->hsync_len; + Htotal = var->xres + var->right_margin + var->hsync_len + var->left_margin; + + Hblankstart = var->xres; + Hblankend = Htotal - 4*8; + + Vdispend = var->yres; + Vsyncstart = var->yres + var->lower_margin; + Vsyncend = var->yres + var->lower_margin + var->vsync_len; + Vtotal = var->yres + var->lower_margin + var->vsync_len + var->upper_margin; + + Vblankstart = var->yres + 7; + Vblankend = Vtotal - 11; + + Hdispend >>= 3; + Hsyncstart >>= 3; + Hsyncend >>= 3; + Htotal >>= 3; + Hblankstart >>= 3; + Hblankend >>= 3; + + Htotal -= 5; + Hdispend -= 1; + Vtotal -= 2; + Vdispend -= 1; + Vblankstart -= 1; + Vblankend -= 1; + + v[0] = Htotal; + v[1] = Hdispend; + v[2] = Hblankstart; + v[3] = BIT(Hblankend, 0, 0x1f, 0) | + BIT(1, 0, 0x01, 7); + v[4] = Hsyncstart; + v[5] = BIT(Hsyncend, 0, 0x1f, 0) | + BIT(Hblankend, 5, 0x01, 7); + + v[6] = Vtotal; + v[7] = BIT(Vtotal, 8, 0x01, 0) | + BIT(Vdispend, 8, 0x01, 1) | + BIT(Vsyncstart, 8, 0x01, 2) | + BIT(Vblankstart,8, 0x01, 3) | + BIT(1, 0, 0x01, 4) | + BIT(Vtotal, 9, 0x01, 5) | + BIT(Vdispend, 9, 0x01, 6) | + BIT(Vsyncstart, 9, 0x01, 7); + v[8] = 0; + v[9] = BIT(0, 0, 0x1f, 0) | + BIT(Vblankstart,9, 0x01, 5) | + BIT(1, 0, 0x01, 6); + v[10] = Vsyncstart; + v[11] = BIT(Vsyncend, 0, 0x0f, 0) | + BIT(1, 0, 0x01, 7); + v[12] = Vdispend; + v[14] = 0; + v[15] = Vblankstart; + v[16] = Vblankend; + v[17] = 0xe3; + + /* overflow - graphics reg 0x11 */ + v[18] = BIT(Vtotal, 10, 0x01, 0) | /* guess */ + BIT(Vdispend, 10, 0x01, 1) | + BIT(Vsyncstart, 10, 0x01, 2) | /* guess */ + BIT(Vblankstart,10, 0x01, 3) | /* guess */ + BIT(Hblankend, 6, 0x01, 4); /* guess */ +} + +static void cyber2000fb_set_timing(struct fb_var_screeninfo *var) +{ + unsigned int width = var->xres_virtual; + unsigned int scr_pitch, fetchrow, i; + char b, graph_r77, crtc[32]; switch (var->bits_per_pixel) { case 8: /* PSEUDOCOLOUR, 256 */ b = 0; - col = 1; - scr_pitch = var->xres_virtual / 8; + graph_r77 = 1; + scr_pitch = width; + break; + + case 15:/* DIRECTCOLOUR, 32k */ + b = 1; + graph_r77 = 6; + scr_pitch = width * 2; break; case 16:/* DIRECTCOLOUR, 64k */ b = 1; - col = 2; - scr_pitch = var->xres_virtual / 8 * 2; + graph_r77 = 2; + scr_pitch = width * 2; break; + case 24:/* TRUECOLOUR, 16m */ b = 2; - col = 4; - scr_pitch = var->xres_virtual / 8 * 3; + graph_r77 = 4; width *= 3; + scr_pitch = width; break; default: - return 1; + return; } + width -= 1; + scr_pitch >>= 3; + fetchrow = scr_pitch + 1; + + cyber2000fb_calculate_timing(crtc, var); + for (i = 0; i < NUM_TOTAL_MODES; i++) if (var->xres == cyber2000_res[i].xres && var->yres == cyber2000_res[i].yres) @@ -502,30 +603,41 @@ static int cyber2000fb_set_timing(struct fb_var_screeninfo *var) if (i < NUM_TOTAL_MODES) cyber2000_init_hw(cyber2000_res + i); - fetchrow = scr_pitch + 1; + crtc[13] = scr_pitch; - debug_printf("Setting regs: pitch=%X, fetchrow=%X, col=%X, b=%X\n", - scr_pitch, fetchrow, col, b); + /* + * reprogram the CRTC with the values we calculated + * above. This should be cleaned up once we're + * confident that we're generating the correct + * values. Disable this if you're having problems, + * and report the values obtained from the kernel + * messages. + */ +#if 1 + cyber2000_crtcw(0x11, 0x0b); + for (i = 0; i < sizeof(crtc_idx); i++) + cyber2000_crtcw(crtc_idx[i], crtc[i]); +#else + cyber2000_crtcw(0x13, crtc[13]); +#endif - cyber2000_outb(0x13, 0x3d4); - cyber2000_outb(scr_pitch, 0x3d5); - cyber2000_outb(0x14, 0x3ce); - cyber2000_outb(fetchrow, 0x3cf); - cyber2000_outb(0x15, 0x3ce); + cyber2000_grphw(0x14, fetchrow); /* FIXME: is this the right way round? */ - cyber2000_outb(((fetchrow >> 4) & 0xf0) | ((scr_pitch >> 8) & 0x0f), 0x3cf); - cyber2000_outb(0x77, 0x3ce); - cyber2000_outb(col, 0x3cf); - - - cyber2000_outb(0x33, 0x3ce); - cyber2000_outb(0x1c, 0x3cf); - - cyber2000_outw(width - 1, 0xbf018); - cyber2000_outw(width - 1, 0xbf218); - cyber2000_outb(b, 0xbf01c); - - return 0; + cyber2000_grphw(0x15, ((fetchrow >> 4) & 0xf0) | ((scr_pitch >> 8) & 0x0f)); + cyber2000_grphw(0x77, graph_r77); + cyber2000_grphw(0x33, 0x1c); + + cyber2000_outw(width, 0xbf018); + cyber2000_outw(width, 0xbf218); + cyber2000_outb(b, 0xbf01c); + +{ int j; + printk(KERN_DEBUG); + for (j = 0; j < 19; j++) printk("%2d ", j); printk("\n"KERN_DEBUG); + for (j = 0; j < 19; j++) printk("%02X ", crtc[j]); printk("\n"KERN_DEBUG); + for (j = 0; j < 18; j++) printk("%02X ", cyber2000_res[i].crtc_regs[j]); + printk("%02X\n", cyber2000_res[i].crtc_ofl); +} } static inline void @@ -536,13 +648,10 @@ cyber2000fb_update_start(struct fb_var_screeninfo *var) base = var->yoffset * var->xres_virtual + var->xoffset; - cyber2000_outb(0x0c, 0x3d4); - cyber2000_outb(base, 0x3d5); - cyber2000_outb(0x0d, 0x3d4); - cyber2000_outb(base >> 8, 0x3d5); + cyber2000_crtcw(0x0c, base); + cyber2000_crtcw(0x0d, base >> 8); /* FIXME: need the upper bits of the start offset */ -/* cyber2000_outb(0x??, 0x3d4); - cyber2000_outb(base >> 16, 0x3d5);*/ +/* cyber2000_crtcw(0x??, base >> 16);*/ #endif } @@ -622,6 +731,7 @@ cyber2000fb_decode_var(struct fb_var_screeninfo *var, int con, int *visual) break; #endif #ifdef FBCON_HAS_CFB16 + case 15: case 16: *visual = FB_VISUAL_DIRECTCOLOR; break; @@ -737,6 +847,10 @@ cyber2000fb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info } display->var = *var; + display->var.activate &= ~FB_ACTIVATE_ALL; + + if (var->activate & FB_ACTIVATE_ALL) + global_disp.var = display->var; display->screen_base = (char *)current_par.screen_base; display->visual = visual; @@ -744,8 +858,6 @@ cyber2000fb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info display->type_aux = 0; display->ypanstep = 0; display->ywrapstep = 0; - display->line_length = - display->next_line = (var->xres_virtual * var->bits_per_pixel) / 8; display->can_soft_blank = 1; display->inverse = 0; @@ -754,18 +866,22 @@ cyber2000fb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info case 8: dispsw = &fbcon_cfb8; display->dispsw_data = NULL; + display->next_line = var->xres_virtual; break; #endif #ifdef FBCON_HAS_CFB16 + case 15: case 16: dispsw = &fbcon_cfb16; display->dispsw_data = current_par.c_table.cfb16; + display->next_line = var->xres_virtual * 2; break; #endif #ifdef FBCON_HAS_CFB24 case 24: dispsw = &fbcon_cfb24; display->dispsw_data = current_par.c_table.cfb24; + display->next_line = var->xres_virtual * 3; break; #endif default: @@ -775,6 +891,8 @@ cyber2000fb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info break; } + display->line_length = display->next_line; + if (display->var.accel_flags & FB_ACCELF_TEXT && dispsw != &fbcon_dummy) display->dispsw = &fbcon_cyber_accel; @@ -818,6 +936,7 @@ static int cyber2000fb_pan_display(struct fb_var_screeninfo *var, int con, return -EINVAL; if (y_bottom > fb_display[con].var.yres_virtual) return -EINVAL; +/*disabled until we can update the start address properly */ return -EINVAL; cyber2000fb_update_start(var); @@ -947,12 +1066,26 @@ cyber2000fb_init_fbinfo(void)) init_var.yres = DEFAULT_YRES; init_var.bits_per_pixel = DEFAULT_BPP; + /* + * These parameters give + * 640x480, hsync 31.5kHz, vsync 60Hz + */ + init_var.left_margin = 56; + init_var.right_margin = 16; + init_var.upper_margin = 34; + init_var.lower_margin = 9; + init_var.hsync_len = 88; + init_var.vsync_len = 2; + init_var.pixclock = 39722; + init_var.red.msb_right = 0; init_var.green.msb_right = 0; init_var.blue.msb_right = 0; switch(init_var.bits_per_pixel) { - case 8: + default: + init_var.bits_per_pixel = 8; + case 8: /* PSEUDOCOLOUR */ init_var.bits_per_pixel = 8; init_var.red.offset = 0; init_var.red.length = 8; @@ -962,7 +1095,17 @@ cyber2000fb_init_fbinfo(void)) init_var.blue.length = 8; break; - case 16: + case 15: /* RGB555 */ + init_var.bits_per_pixel = 15; + init_var.red.offset = 10; + init_var.red.length = 5; + init_var.green.offset = 5; + init_var.green.length = 5; + init_var.blue.offset = 0; + init_var.blue.length = 5; + break; + + case 16: /* RGB565 */ init_var.bits_per_pixel = 16; init_var.red.offset = 11; init_var.red.length = 5; @@ -972,7 +1115,7 @@ cyber2000fb_init_fbinfo(void)) init_var.blue.length = 5; break; - case 24: + case 24: /* RGB888 */ init_var.bits_per_pixel = 24; init_var.red.offset = 16; init_var.red.length = 8; diff --git a/drivers/video/cyber2000fb.h b/drivers/video/cyber2000fb.h index f1e81dfa0..bbbd1edbb 100644 --- a/drivers/video/cyber2000fb.h +++ b/drivers/video/cyber2000fb.h @@ -13,19 +13,19 @@ #define cyber2000_inw(reg) (*(unsigned short *)&CyberRegs[reg]) #define cyber2000_inl(reg) (*(unsigned long *)&CyberRegs[reg]) -static inline void cyber2000_crtcw(int val, int reg) +static inline void cyber2000_crtcw(int reg, int val) { cyber2000_outb(reg, 0x3d4); cyber2000_outb(val, 0x3d5); } -static inline void cyber2000_grphw(int val, int reg) +static inline void cyber2000_grphw(int reg, int val) { cyber2000_outb(reg, 0x3ce); cyber2000_outb(val, 0x3cf); } -static inline void cyber2000_attrw(int val, int reg) +static inline void cyber2000_attrw(int reg, int val) { cyber2000_inb(0x3da); cyber2000_outb(reg, 0x3c0); @@ -33,7 +33,7 @@ static inline void cyber2000_attrw(int val, int reg) cyber2000_outb(val, 0x3c0); } -static inline void cyber2000_seqw(int val, int reg) +static inline void cyber2000_seqw(int reg, int val) { cyber2000_outb(reg, 0x3c4); cyber2000_outb(val, 0x3c5); diff --git a/drivers/video/vgacon.c b/drivers/video/vgacon.c index 8823d2121..95a758dd1 100644 --- a/drivers/video/vgacon.c +++ b/drivers/video/vgacon.c @@ -135,9 +135,17 @@ void no_scroll(char *str, int *ints) */ static inline void write_vga(unsigned char reg, unsigned int val) { -#ifndef SLOW_VGA unsigned int v1, v2; + unsigned long flags; + + /* + * ddprintk might set the console position from interrupt + * handlers, thus the write has to be IRQ-atomic. + */ + save_flags(flags); + cli(); +#ifndef SLOW_VGA v1 = reg + (val & 0xff00); v2 = reg + 1 + ((val << 8) & 0xff00); outw(v1, vga_video_port_reg); @@ -148,6 +156,7 @@ static inline void write_vga(unsigned char reg, unsigned int val) outb_p(reg+1, vga_video_port_reg); outb_p(val & 0xff, vga_video_port_val); #endif + restore_flags(flags); } __initfunc(static const char *vgacon_startup(void)) |