diff options
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)) |