(2006-08-06) rescue-bootcd

This commit is contained in:
2006-08-06 00:00:00 +02:00
parent 2f796b816a
commit decb062d20
21091 changed files with 7076462 additions and 0 deletions

View File

@@ -0,0 +1,89 @@
#
# SCSI driver configuration for Acorn
#
config SCSI_ACORNSCSI_3
tristate "Acorn SCSI card (aka30) support"
depends on ARCH_ACORN && SCSI
help
This enables support for the Acorn SCSI card (aka30). If you have an
Acorn system with one of these, say Y. If unsure, say N.
config SCSI_ACORNSCSI_TAGGED_QUEUE
bool "Support SCSI 2 Tagged queueing"
depends on SCSI_ACORNSCSI_3
help
Say Y here to enable tagged queuing support on the Acorn SCSI card.
This is a feature of SCSI-2 which improves performance: the host
adapter can send several SCSI commands to a device's queue even if
previous commands haven't finished yet. Some SCSI devices don't
implement this properly, so the safe answer is N.
config SCSI_ACORNSCSI_SYNC
bool "Support SCSI 2 Synchronous Transfers"
depends on SCSI_ACORNSCSI_3
help
Say Y here to enable synchronous transfer negotiation with all
targets on the Acorn SCSI card.
In general, this improves performance; however some SCSI devices
don't implement it properly, so the safe answer is N.
config SCSI_ARXESCSI
tristate "ARXE SCSI support"
depends on ARCH_ACORN && SCSI
help
Around 1991, Arxe Systems Limited released a high density floppy
disc interface for the Acorn Archimedes range, to allow the use of
HD discs from the then new A5000 on earlier models. This interface
was either sold on its own or with an integral SCSI controller.
Technical details on this NCR53c94-based device are available at
<http://www.cryton.demon.co.uk/acornbits/scsi_arxe.html>
Say Y here to compile in support for the SCSI controller.
config SCSI_CUMANA_2
tristate "CumanaSCSI II support"
depends on ARCH_ACORN && SCSI
help
This enables support for the Cumana SCSI II card. If you have an
Acorn system with one of these, say Y. If unsure, say N.
config SCSI_EESOXSCSI
tristate "EESOX support"
depends on ARCH_ACORN && SCSI
help
This enables support for the EESOX SCSI card. If you have an Acorn
system with one of these, say Y, otherwise say N.
config SCSI_POWERTECSCSI
tristate "PowerTec support"
depends on ARCH_ACORN && SCSI
help
This enables support for the Powertec SCSI card on Acorn systems. If
you have one of these, say Y. If unsure, say N.
comment "The following drivers are not fully supported"
depends on ARCH_ACORN && EXPERIMENTAL
config SCSI_CUMANA_1
tristate "CumanaSCSI I support (EXPERIMENTAL)"
depends on ARCH_ACORN && EXPERIMENTAL && SCSI
help
This enables support for the Cumana SCSI I card. If you have an
Acorn system with one of these, say Y. If unsure, say N.
config SCSI_ECOSCSI
tristate "EcoScsi support (EXPERIMENTAL)"
depends on ARCH_ACORN && EXPERIMENTAL && (ARCH_ARC || ARCH_A5K) && SCSI
help
This enables support for the EcoSCSI card -- a small card that sits
in the Econet socket. If you have an Acorn system with one of these,
say Y. If unsure, say N.
config SCSI_OAK1
tristate "Oak SCSI support (EXPERIMENTAL)"
depends on ARCH_ACORN && EXPERIMENTAL && SCSI
help
This enables support for the Oak SCSI card. If you have an Acorn
system with one of these, say Y. If unsure, say N.

View File

@@ -0,0 +1,14 @@
#
# Makefile for drivers/scsi/arm
#
acornscsi_mod-objs := acornscsi.o acornscsi-io.o
obj-$(CONFIG_SCSI_ACORNSCSI_3) += acornscsi_mod.o queue.o msgqueue.o
obj-$(CONFIG_SCSI_ARXESCSI) += arxescsi.o fas216.o queue.o msgqueue.o
obj-$(CONFIG_SCSI_CUMANA_1) += cumana_1.o
obj-$(CONFIG_SCSI_CUMANA_2) += cumana_2.o fas216.o queue.o msgqueue.o
obj-$(CONFIG_SCSI_ECOSCSI) += ecoscsi.o
obj-$(CONFIG_SCSI_OAK1) += oak.o
obj-$(CONFIG_SCSI_POWERTECSCSI) += powertec.o fas216.o queue.o msgqueue.o
obj-$(CONFIG_SCSI_EESOXSCSI) += eesox.o fas216.o queue.o msgqueue.o

View File

@@ -0,0 +1,145 @@
/*
* linux/drivers/acorn/scsi/acornscsi-io.S: Acorn SCSI card IO
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/linkage.h>
#include <asm/assembler.h>
#include <asm/hardware.h>
#if (IO_BASE == (PCIO_BASE & 0xff000000))
#define ADDR(off,reg) \
tst off, $0x80000000 ;\
mov reg, $IO_BASE ;\
orreq reg, reg, $(PCIO_BASE & 0x00ff0000)
#else
#define ADDR(off,reg) \
tst off, $0x80000000 ;\
movne reg, $IO_BASE ;\
moveq reg, $(PCIO_BASE & 0xff000000) ;\
orreq reg, reg, $(PCIO_BASE & 0x00ff0000)
#endif
@ Purpose: transfer a block of data from the acorn scsi card to memory
@ Proto : void acornscsi_in(unsigned int addr_start, char *buffer, int length)
@ Returns: nothing
.align
ENTRY(__acornscsi_in)
stmfd sp!, {r4 - r7, lr}
bic r0, r0, #3
mov lr, #0xff
orr lr, lr, #0xff00
acornscsi_in16lp:
subs r2, r2, #16
bmi acornscsi_in8
ldmia r0!, {r3, r4, r5, r6}
and r3, r3, lr
orr r3, r3, r4, lsl #16
and r4, r5, lr
orr r4, r4, r6, lsl #16
ldmia r0!, {r5, r6, r7, ip}
and r5, r5, lr
orr r5, r5, r6, lsl #16
and r6, r7, lr
orr r6, r6, ip, lsl #16
stmia r1!, {r3 - r6}
bne acornscsi_in16lp
LOADREGS(fd, sp!, {r4 - r7, pc})
acornscsi_in8: adds r2, r2, #8
bmi acornscsi_in4
ldmia r0!, {r3, r4, r5, r6}
and r3, r3, lr
orr r3, r3, r4, lsl #16
and r4, r5, lr
orr r4, r4, r6, lsl #16
stmia r1!, {r3 - r4}
LOADREGS(eqfd, sp!, {r4 - r7, pc})
sub r2, r2, #8
acornscsi_in4: adds r2, r2, #4
bmi acornscsi_in2
ldmia r0!, {r3, r4}
and r3, r3, lr
orr r3, r3, r4, lsl #16
str r3, [r1], #4
LOADREGS(eqfd, sp!, {r4 - r7, pc})
sub r2, r2, #4
acornscsi_in2: adds r2, r2, #2
ldr r3, [r0], #4
and r3, r3, lr
strb r3, [r1], #1
mov r3, r3, lsr #8
strplb r3, [r1], #1
LOADREGS(fd, sp!, {r4 - r7, pc})
@ Purpose: transfer a block of data from memory to the acorn scsi card
@ Proto : void acornscsi_in(unsigned int addr_start, char *buffer, int length)
@ Returns: nothing
ENTRY(__acornscsi_out)
stmfd sp!, {r4 - r6, lr}
bic r0, r0, #3
acornscsi_out16lp:
subs r2, r2, #16
bmi acornscsi_out8
ldmia r1!, {r4, r6, ip, lr}
mov r3, r4, lsl #16
orr r3, r3, r3, lsr #16
mov r4, r4, lsr #16
orr r4, r4, r4, lsl #16
mov r5, r6, lsl #16
orr r5, r5, r5, lsr #16
mov r6, r6, lsr #16
orr r6, r6, r6, lsl #16
stmia r0!, {r3, r4, r5, r6}
mov r3, ip, lsl #16
orr r3, r3, r3, lsr #16
mov r4, ip, lsr #16
orr r4, r4, r4, lsl #16
mov ip, lr, lsl #16
orr ip, ip, ip, lsr #16
mov lr, lr, lsr #16
orr lr, lr, lr, lsl #16
stmia r0!, {r3, r4, ip, lr}
bne acornscsi_out16lp
LOADREGS(fd, sp!, {r4 - r6, pc})
acornscsi_out8: adds r2, r2, #8
bmi acornscsi_out4
ldmia r1!, {r4, r6}
mov r3, r4, lsl #16
orr r3, r3, r3, lsr #16
mov r4, r4, lsr #16
orr r4, r4, r4, lsl #16
mov r5, r6, lsl #16
orr r5, r5, r5, lsr #16
mov r6, r6, lsr #16
orr r6, r6, r6, lsl #16
stmia r0!, {r3, r4, r5, r6}
LOADREGS(eqfd, sp!, {r4 - r6, pc})
sub r2, r2, #8
acornscsi_out4: adds r2, r2, #4
bmi acornscsi_out2
ldr r4, [r1], #4
mov r3, r4, lsl #16
orr r3, r3, r3, lsr #16
mov r4, r4, lsr #16
orr r4, r4, r4, lsl #16
stmia r0!, {r3, r4}
LOADREGS(eqfd, sp!, {r4 - r6, pc})
sub r2, r2, #4
acornscsi_out2: adds r2, r2, #2
ldr r3, [r1], #2
strb r3, [r0], #1
mov r3, r3, lsr #8
strplb r3, [r0], #1
LOADREGS(fd, sp!, {r4 - r6, pc})

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,358 @@
/*
* linux/drivers/acorn/scsi/acornscsi.h
*
* Copyright (C) 1997 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Acorn SCSI driver
*/
#ifndef ACORNSCSI_H
#define ACORNSCSI_H
/* SBIC registers */
#define SBIC_OWNID 0
#define OWNID_FS1 (1<<7)
#define OWNID_FS2 (1<<6)
#define OWNID_EHP (1<<4)
#define OWNID_EAF (1<<3)
#define SBIC_CTRL 1
#define CTRL_DMAMODE (1<<7)
#define CTRL_DMADBAMODE (1<<6)
#define CTRL_DMABURST (1<<5)
#define CTRL_DMAPOLLED 0
#define CTRL_HHP (1<<4)
#define CTRL_EDI (1<<3)
#define CTRL_IDI (1<<2)
#define CTRL_HA (1<<1)
#define CTRL_HSP (1<<0)
#define SBIC_TIMEOUT 2
#define SBIC_TOTSECTS 3
#define SBIC_TOTHEADS 4
#define SBIC_TOTCYLH 5
#define SBIC_TOTCYLL 6
#define SBIC_LOGADDRH 7
#define SBIC_LOGADDRM2 8
#define SBIC_LOGADDRM1 9
#define SBIC_LOGADDRL 10
#define SBIC_SECTORNUM 11
#define SBIC_HEADNUM 12
#define SBIC_CYLH 13
#define SBIC_CYLL 14
#define SBIC_TARGETLUN 15
#define TARGETLUN_TLV (1<<7)
#define TARGETLUN_DOK (1<<6)
#define SBIC_CMNDPHASE 16
#define SBIC_SYNCHTRANSFER 17
#define SYNCHTRANSFER_OF0 0x00
#define SYNCHTRANSFER_OF1 0x01
#define SYNCHTRANSFER_OF2 0x02
#define SYNCHTRANSFER_OF3 0x03
#define SYNCHTRANSFER_OF4 0x04
#define SYNCHTRANSFER_OF5 0x05
#define SYNCHTRANSFER_OF6 0x06
#define SYNCHTRANSFER_OF7 0x07
#define SYNCHTRANSFER_OF8 0x08
#define SYNCHTRANSFER_OF9 0x09
#define SYNCHTRANSFER_OF10 0x0A
#define SYNCHTRANSFER_OF11 0x0B
#define SYNCHTRANSFER_OF12 0x0C
#define SYNCHTRANSFER_8DBA 0x00
#define SYNCHTRANSFER_2DBA 0x20
#define SYNCHTRANSFER_3DBA 0x30
#define SYNCHTRANSFER_4DBA 0x40
#define SYNCHTRANSFER_5DBA 0x50
#define SYNCHTRANSFER_6DBA 0x60
#define SYNCHTRANSFER_7DBA 0x70
#define SBIC_TRANSCNTH 18
#define SBIC_TRANSCNTM 19
#define SBIC_TRANSCNTL 20
#define SBIC_DESTID 21
#define DESTID_SCC (1<<7)
#define DESTID_DPD (1<<6)
#define SBIC_SOURCEID 22
#define SOURCEID_ER (1<<7)
#define SOURCEID_ES (1<<6)
#define SOURCEID_DSP (1<<5)
#define SOURCEID_SIV (1<<4)
#define SBIC_SSR 23
#define SBIC_CMND 24
#define CMND_RESET 0x00
#define CMND_ABORT 0x01
#define CMND_ASSERTATN 0x02
#define CMND_NEGATEACK 0x03
#define CMND_DISCONNECT 0x04
#define CMND_RESELECT 0x05
#define CMND_SELWITHATN 0x06
#define CMND_SELECT 0x07
#define CMND_SELECTATNTRANSFER 0x08
#define CMND_SELECTTRANSFER 0x09
#define CMND_RESELECTRXDATA 0x0A
#define CMND_RESELECTTXDATA 0x0B
#define CMND_WAITFORSELRECV 0x0C
#define CMND_SENDSTATCMD 0x0D
#define CMND_SENDDISCONNECT 0x0E
#define CMND_SETIDI 0x0F
#define CMND_RECEIVECMD 0x10
#define CMND_RECEIVEDTA 0x11
#define CMND_RECEIVEMSG 0x12
#define CMND_RECEIVEUSP 0x13
#define CMND_SENDCMD 0x14
#define CMND_SENDDATA 0x15
#define CMND_SENDMSG 0x16
#define CMND_SENDUSP 0x17
#define CMND_TRANSLATEADDR 0x18
#define CMND_XFERINFO 0x20
#define CMND_SBT (1<<7)
#define SBIC_DATA 25
#define SBIC_ASR 26
#define ASR_INT (1<<7)
#define ASR_LCI (1<<6)
#define ASR_BSY (1<<5)
#define ASR_CIP (1<<4)
#define ASR_PE (1<<1)
#define ASR_DBR (1<<0)
/* DMAC registers */
#define DMAC_INIT 0x00
#define INIT_8BIT (1)
#define DMAC_CHANNEL 0x80
#define CHANNEL_0 0x00
#define CHANNEL_1 0x01
#define CHANNEL_2 0x02
#define CHANNEL_3 0x03
#define DMAC_TXCNTLO 0x01
#define DMAC_TXCNTHI 0x81
#define DMAC_TXADRLO 0x02
#define DMAC_TXADRMD 0x82
#define DMAC_TXADRHI 0x03
#define DMAC_DEVCON0 0x04
#define DEVCON0_AKL (1<<7)
#define DEVCON0_RQL (1<<6)
#define DEVCON0_EXW (1<<5)
#define DEVCON0_ROT (1<<4)
#define DEVCON0_CMP (1<<3)
#define DEVCON0_DDMA (1<<2)
#define DEVCON0_AHLD (1<<1)
#define DEVCON0_MTM (1<<0)
#define DMAC_DEVCON1 0x84
#define DEVCON1_WEV (1<<1)
#define DEVCON1_BHLD (1<<0)
#define DMAC_MODECON 0x05
#define MODECON_WOED 0x01
#define MODECON_VERIFY 0x00
#define MODECON_READ 0x04
#define MODECON_WRITE 0x08
#define MODECON_AUTOINIT 0x10
#define MODECON_ADDRDIR 0x20
#define MODECON_DEMAND 0x00
#define MODECON_SINGLE 0x40
#define MODECON_BLOCK 0x80
#define MODECON_CASCADE 0xC0
#define DMAC_STATUS 0x85
#define STATUS_TC0 (1<<0)
#define STATUS_RQ0 (1<<4)
#define DMAC_TEMPLO 0x06
#define DMAC_TEMPHI 0x86
#define DMAC_REQREG 0x07
#define DMAC_MASKREG 0x87
#define MASKREG_M0 0x01
#define MASKREG_M1 0x02
#define MASKREG_M2 0x04
#define MASKREG_M3 0x08
/* miscellaneous internal variables */
#define POD_SPACE(x) ((x) + 0xd0000)
#define MASK_ON (MASKREG_M3|MASKREG_M2|MASKREG_M1|MASKREG_M0)
#define MASK_OFF (MASKREG_M3|MASKREG_M2|MASKREG_M1)
/*
* SCSI driver phases
*/
typedef enum {
PHASE_IDLE, /* we're not planning on doing anything */
PHASE_CONNECTING, /* connecting to a target */
PHASE_CONNECTED, /* connected to a target */
PHASE_MSGOUT, /* message out to device */
PHASE_RECONNECTED, /* reconnected */
PHASE_COMMANDPAUSED, /* command partly sent */
PHASE_COMMAND, /* command all sent */
PHASE_DATAOUT, /* data out to device */
PHASE_DATAIN, /* data in from device */
PHASE_STATUSIN, /* status in from device */
PHASE_MSGIN, /* message in from device */
PHASE_DONE, /* finished */
PHASE_ABORTED, /* aborted */
PHASE_DISCONNECT, /* disconnecting */
} phase_t;
/*
* After interrupt, what to do now
*/
typedef enum {
INTR_IDLE, /* not expecting another IRQ */
INTR_NEXT_COMMAND, /* start next command */
INTR_PROCESSING, /* interrupt routine still processing */
} intr_ret_t;
/*
* DMA direction
*/
typedef enum {
DMA_OUT, /* DMA from memory to chip */
DMA_IN /* DMA from chip to memory */
} dmadir_t;
/*
* Synchronous transfer state
*/
typedef enum { /* Synchronous transfer state */
SYNC_ASYNCHRONOUS, /* don't negociate synchronous transfers*/
SYNC_NEGOCIATE, /* start negociation */
SYNC_SENT_REQUEST, /* sent SDTR message */
SYNC_COMPLETED, /* received SDTR reply */
} syncxfer_t;
/*
* Command type
*/
typedef enum { /* command type */
CMD_READ, /* READ_6, READ_10, READ_12 */
CMD_WRITE, /* WRITE_6, WRITE_10, WRITE_12 */
CMD_MISC, /* Others */
} cmdtype_t;
/*
* Data phase direction
*/
typedef enum { /* Data direction */
DATADIR_IN, /* Data in phase expected */
DATADIR_OUT /* Data out phase expected */
} datadir_t;
#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
*/
typedef struct acornscsi_hostdata {
/* miscellaneous */
struct Scsi_Host *host; /* host */
Scsi_Cmnd *SCpnt; /* currently processing command */
Scsi_Cmnd *origSCpnt; /* original connecting command */
/* driver information */
struct {
unsigned int io_port; /* base address of WD33C93 */
unsigned int irq; /* interrupt */
phase_t phase; /* current phase */
struct {
unsigned char target; /* reconnected target */
unsigned char lun; /* reconnected lun */
unsigned char tag; /* reconnected tag */
} reconnected;
Scsi_Pointer SCp; /* current commands data pointer */
MsgQueue_t msgs;
unsigned short last_message; /* last message to be sent */
unsigned char disconnectable:1; /* this command can be disconnected */
} scsi;
/* statistics information */
struct {
unsigned int queues;
unsigned int removes;
unsigned int fins;
unsigned int reads;
unsigned int writes;
unsigned int miscs;
unsigned int disconnects;
unsigned int aborts;
unsigned int resets;
} stats;
/* queue handling */
struct {
Queue_t issue; /* issue queue */
Queue_t disconnected; /* disconnected command queue */
} queues;
/* per-device info */
struct {
unsigned char sync_xfer; /* synchronous transfer (SBIC value) */
syncxfer_t sync_state; /* sync xfer negociation state */
unsigned char disconnect_ok:1; /* device can disconnect */
} device[8];
unsigned long busyluns[64 / sizeof(unsigned long)];/* array of bits indicating LUNs busy */
/* DMA info */
struct {
unsigned int io_port; /* base address of DMA controller */
unsigned int io_intr_clear; /* address of DMA interrupt clear */
unsigned int free_addr; /* next free address */
unsigned int start_addr; /* start address of current transfer */
dmadir_t direction; /* dma direction */
unsigned int transferred; /* number of bytes transferred */
unsigned int xfer_start; /* scheduled DMA transfer start */
unsigned int xfer_length; /* scheduled DMA transfer length */
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 */
struct {
unsigned int io_intr; /* base address of interrupt id reg */
unsigned int io_page; /* base address of page reg */
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 /* ACORNSCSI_H */

View File

@@ -0,0 +1,395 @@
/*
* linux/arch/arm/drivers/scsi/arxescsi.c
*
* Copyright (C) 1997-2000 Russell King, Stefan Hanske
*
* 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 SH 0.0.2 Changed to support ARXE 16-bit SCSI card
* enabled writing
* 01-01-2000 SH 0.1.0 Added *real* pseudo dma writing
* (arxescsi_pseudo_dma_write)
* 02-04-2000 RMK 0.1.1 Updated for new error handling code.
* 22-10-2000 SH Updated for new registering scheme.
*/
#include <linux/module.h>
#include <linux/blkdev.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 <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <asm/dma.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/ecard.h>
#include "../scsi.h"
#include <scsi/scsi_host.h>
#include "fas216.h"
struct arxescsi_info {
FAS216_Info info;
struct expansion_card *ec;
};
#define DMADATA_OFFSET (0x200)
#define DMASTAT_OFFSET (0x600)
#define DMASTAT_DRQ (1 << 0)
#define CSTATUS_IRQ (1 << 0)
#define VERSION "1.10 (23/01/2003 2.5.57)"
/*
* 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;
}
static void arxescsi_pseudo_dma_write(unsigned char *addr, unsigned char *base)
{
__asm__ __volatile__(
" stmdb sp!, {r0-r12}\n"
" mov r3, %0\n"
" mov r1, %1\n"
" add r2, r1, #512\n"
" mov r4, #256\n"
".loop_1: ldmia r3!, {r6, r8, r10, r12}\n"
" mov r5, r6, lsl #16\n"
" mov r7, r8, lsl #16\n"
".loop_2: ldrb r0, [r1, #1536]\n"
" tst r0, #1\n"
" beq .loop_2\n"
" stmia r2, {r5-r8}\n\t"
" mov r9, r10, lsl #16\n"
" mov r11, r12, lsl #16\n"
".loop_3: ldrb r0, [r1, #1536]\n"
" tst r0, #1\n"
" beq .loop_3\n"
" stmia r2, {r9-r12}\n"
" subs r4, r4, #16\n"
" bne .loop_1\n"
" ldmia sp!, {r0-r12}\n"
:
: "r" (addr), "r" (base));
}
/*
* 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
*/
static void
arxescsi_dma_pseudo(struct Scsi_Host *host, Scsi_Pointer *SCp,
fasdmadir_t direction, int transfer)
{
struct arxescsi_info *info = (struct arxescsi_info *)host->hostdata;
unsigned int length, error = 0;
unsigned char *base = info->info.scsi.io_base;
unsigned char *addr;
length = SCp->this_residual;
addr = SCp->ptr;
if (direction == DMA_OUT) {
unsigned int word;
while (length > 256) {
if (readb(base + 0x80) & STAT_INT) {
error = 1;
break;
}
arxescsi_pseudo_dma_write(addr, base);
addr += 256;
length -= 256;
}
if (!error)
while (length > 0) {
if (readb(base + 0x80) & STAT_INT)
break;
if (!(readb(base + DMASTAT_OFFSET) & DMASTAT_DRQ))
continue;
word = *addr | *(addr + 1) << 8;
writew(word, base + DMADATA_OFFSET);
if (length > 1) {
addr += 2;
length -= 2;
} else {
addr += 1;
length -= 1;
}
}
}
else {
if (transfer && (transfer & 255)) {
while (length >= 256) {
if (readb(base + 0x80) & STAT_INT) {
error = 1;
break;
}
if (!(readb(base + DMASTAT_OFFSET) & DMASTAT_DRQ))
continue;
readsw(base + DMADATA_OFFSET, addr, 256 >> 1);
addr += 256;
length -= 256;
}
}
if (!(error))
while (length > 0) {
unsigned long word;
if (readb(base + 0x80) & STAT_INT)
break;
if (!(readb(base + DMASTAT_OFFSET) & DMASTAT_DRQ))
continue;
word = readw(base + DMADATA_OFFSET);
*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: 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.
*/
static const char *arxescsi_info(struct Scsi_Host *host)
{
struct arxescsi_info *info = (struct arxescsi_info *)host->hostdata;
static char string[150];
sprintf(string, "%s (%s) in slot %d v%s",
host->hostt->name, info->info.scsi.type, info->ec->slot_no,
VERSION);
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.
*/
static int
arxescsi_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset, int length,
int inout)
{
struct arxescsi_info *info;
char *p = buffer;
int pos;
info = (struct arxescsi_info *)host->hostdata;
if (inout == 1)
return -EINVAL;
p += sprintf(p, "ARXE 16-bit SCSI driver v%s\n", VERSION);
p += fas216_print_host(&info->info, p);
p += fas216_print_stats(&info->info, p);
p += fas216_print_devices(&info->info, p);
*start = buffer + offset;
pos = p - buffer - offset;
if (pos > length)
pos = length;
return pos;
}
static Scsi_Host_Template arxescsi_template = {
.proc_info = arxescsi_proc_info,
.name = "ARXE SCSI card",
.info = arxescsi_info,
.queuecommand = fas216_noqueue_command,
.eh_host_reset_handler = fas216_eh_host_reset,
.eh_bus_reset_handler = fas216_eh_bus_reset,
.eh_device_reset_handler = fas216_eh_device_reset,
.eh_abort_handler = fas216_eh_abort,
.can_queue = 0,
.this_id = 7,
.sg_tablesize = SG_ALL,
.cmd_per_lun = 1,
.use_clustering = DISABLE_CLUSTERING,
.proc_name = "arxescsi",
};
static int __devinit
arxescsi_probe(struct expansion_card *ec, const struct ecard_id *id)
{
struct Scsi_Host *host;
struct arxescsi_info *info;
unsigned long resbase, reslen;
unsigned char *base;
int ret;
ret = ecard_request_resources(ec);
if (ret)
goto out;
resbase = ecard_resource_start(ec, ECARD_RES_MEMC);
reslen = ecard_resource_len(ec, ECARD_RES_MEMC);
base = ioremap(resbase, reslen);
if (!base) {
ret = -ENOMEM;
goto out_region;
}
host = scsi_host_alloc(&arxescsi_template, sizeof(struct arxescsi_info));
if (!host) {
ret = -ENOMEM;
goto out_unmap;
}
host->base = (unsigned long)base;
host->irq = NO_IRQ;
host->dma_channel = NO_DMA;
info = (struct arxescsi_info *)host->hostdata;
info->ec = ec;
info->info.scsi.io_base = base + 0x2000;
info->info.scsi.irq = host->irq;
info->info.scsi.io_shift = 5;
info->info.ifcfg.clockrate = 24; /* MHz */
info->info.ifcfg.select_timeout = 255;
info->info.ifcfg.asyncperiod = 200; /* ns */
info->info.ifcfg.sync_max_depth = 0;
info->info.ifcfg.cntl3 = CNTL3_FASTSCSI | CNTL3_FASTCLK;
info->info.ifcfg.disconnect_ok = 0;
info->info.ifcfg.wide_max_size = 0;
info->info.ifcfg.capabilities = FASCAP_PSEUDODMA;
info->info.dma.setup = arxescsi_dma_setup;
info->info.dma.pseudo = arxescsi_dma_pseudo;
info->info.dma.stop = arxescsi_dma_stop;
ec->irqaddr = base;
ec->irqmask = CSTATUS_IRQ;
ret = fas216_init(host);
if (ret)
goto out_unregister;
ret = fas216_add(host, &ec->dev);
if (ret == 0)
goto out;
fas216_release(host);
out_unregister:
scsi_host_put(host);
out_unmap:
iounmap(base);
out_region:
ecard_release_resources(ec);
out:
return ret;
}
static void __devexit arxescsi_remove(struct expansion_card *ec)
{
struct Scsi_Host *host = ecard_get_drvdata(ec);
ecard_set_drvdata(ec, NULL);
fas216_remove(host);
iounmap((void *)host->base);
fas216_release(host);
scsi_host_put(host);
ecard_release_resources(ec);
}
static const struct ecard_id arxescsi_cids[] = {
{ MANU_ARXE, PROD_ARXE_SCSI },
{ 0xffff, 0xffff },
};
static struct ecard_driver arxescsi_driver = {
.probe = arxescsi_probe,
.remove = __devexit_p(arxescsi_remove),
.id_table = arxescsi_cids,
.drv = {
.name = "arxescsi",
},
};
static int __init init_arxe_scsi_driver(void)
{
return ecard_register_driver(&arxescsi_driver);
}
static void __exit exit_arxe_scsi_driver(void)
{
ecard_remove_driver(&arxescsi_driver);
}
module_init(init_arxe_scsi_driver);
module_exit(exit_arxe_scsi_driver);
MODULE_AUTHOR("Stefan Hanske");
MODULE_DESCRIPTION("ARXESCSI driver for Acorn machines");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,357 @@
/*
* Generic Generic NCR5380 driver
*
* Copyright 1995-2002, Russell King
*/
#include <linux/module.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/blkdev.h>
#include <linux/init.h>
#include <asm/ecard.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
#include "../scsi.h"
#include <scsi/scsi_host.h>
#include <scsi/scsicam.h>
#define AUTOSENSE
#define PSEUDO_DMA
#define CUMANASCSI_PUBLIC_RELEASE 1
#define NCR5380_implementation_fields int port, ctrl
#define NCR5380_local_declare() struct Scsi_Host *_instance
#define NCR5380_setup(instance) _instance = instance
#define NCR5380_read(reg) cumanascsi_read(_instance, reg)
#define NCR5380_write(reg, value) cumanascsi_write(_instance, reg, value)
#define NCR5380_intr cumanascsi_intr
#define NCR5380_queue_command cumanascsi_queue_command
#define NCR5380_proc_info cumanascsi_proc_info
#define BOARD_NORMAL 0
#define BOARD_NCR53C400 1
#include "../NCR5380.h"
void cumanascsi_setup(char *str, int *ints)
{
}
const char *cumanascsi_info(struct Scsi_Host *spnt)
{
return "";
}
#ifdef NOT_EFFICIENT
#define CTRL(p,v) outb(*ctrl = (v), (p) - 577)
#define STAT(p) inb((p)+1)
#define IN(p) inb((p))
#define OUT(v,p) outb((v), (p))
#else
#define CTRL(p,v) (p[-2308] = (*ctrl = (v)))
#define STAT(p) (p[4])
#define IN(p) (*(p))
#define IN2(p) ((unsigned short)(*(volatile unsigned long *)(p)))
#define OUT(v,p) (*(p) = (v))
#define OUT2(v,p) (*((volatile unsigned long *)(p)) = (v))
#endif
#define L(v) (((v)<<16)|((v) & 0x0000ffff))
#define H(v) (((v)>>16)|((v) & 0xffff0000))
static inline int
NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *addr, int len)
{
int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl;
int oldctrl = *ctrl;
unsigned long *laddr;
#ifdef NOT_EFFICIENT
int iobase = instance->io_port;
int dma_io = iobase & ~(0x3C0000>>2);
#else
volatile unsigned char *iobase = (unsigned char *)ioaddr(instance->io_port);
volatile unsigned char *dma_io = (unsigned char *)((int)iobase & ~0x3C0000);
#endif
if(!len) return 0;
CTRL(iobase, 0x02);
laddr = (unsigned long *)addr;
while(len >= 32)
{
int status;
unsigned long v;
status = STAT(iobase);
if(status & 0x80)
goto end;
if(!(status & 0x40))
continue;
v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
len -= 32;
if(len == 0)
break;
}
addr = (unsigned char *)laddr;
CTRL(iobase, 0x12);
while(len > 0)
{
int status;
status = STAT(iobase);
if(status & 0x80)
goto end;
if(status & 0x40)
{
OUT(*addr++, dma_io);
if(--len == 0)
break;
}
status = STAT(iobase);
if(status & 0x80)
goto end;
if(status & 0x40)
{
OUT(*addr++, dma_io);
if(--len == 0)
break;
}
}
end:
CTRL(iobase, oldctrl|0x40);
return len;
}
static inline int
NCR5380_pread(struct Scsi_Host *instance, unsigned char *addr, int len)
{
int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl;
int oldctrl = *ctrl;
unsigned long *laddr;
#ifdef NOT_EFFICIENT
int iobase = instance->io_port;
int dma_io = iobase & ~(0x3C0000>>2);
#else
volatile unsigned char *iobase = (unsigned char *)ioaddr(instance->io_port);
volatile unsigned char *dma_io = (unsigned char *)((int)iobase & ~0x3C0000);
#endif
if(!len) return 0;
CTRL(iobase, 0x00);
laddr = (unsigned long *)addr;
while(len >= 32)
{
int status;
status = STAT(iobase);
if(status & 0x80)
goto end;
if(!(status & 0x40))
continue;
*laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
*laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
*laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
*laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
*laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
*laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
*laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
*laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
len -= 32;
if(len == 0)
break;
}
addr = (unsigned char *)laddr;
CTRL(iobase, 0x10);
while(len > 0)
{
int status;
status = STAT(iobase);
if(status & 0x80)
goto end;
if(status & 0x40)
{
*addr++ = IN(dma_io);
if(--len == 0)
break;
}
status = STAT(iobase);
if(status & 0x80)
goto end;
if(status & 0x40)
{
*addr++ = IN(dma_io);
if(--len == 0)
break;
}
}
end:
CTRL(iobase, oldctrl|0x40);
return len;
}
#undef STAT
#undef CTRL
#undef IN
#undef OUT
#define CTRL(p,v) outb(*ctrl = (v), (p) - 577)
static char cumanascsi_read(struct Scsi_Host *instance, int reg)
{
unsigned int iobase = instance->io_port;
int i;
int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl;
CTRL(iobase, 0);
i = inb(iobase + 64 + reg);
CTRL(iobase, 0x40);
return i;
}
static void cumanascsi_write(struct Scsi_Host *instance, int reg, int value)
{
int iobase = instance->io_port;
int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl;
CTRL(iobase, 0);
outb(value, iobase + 64 + reg);
CTRL(iobase, 0x40);
}
#undef CTRL
#include "../NCR5380.c"
static Scsi_Host_Template cumanascsi_template = {
.module = THIS_MODULE,
.name = "Cumana 16-bit SCSI",
.info = cumanascsi_info,
.queuecommand = cumanascsi_queue_command,
.eh_abort_handler = NCR5380_abort,
.eh_device_reset_handler= NCR5380_device_reset,
.eh_bus_reset_handler = NCR5380_bus_reset,
.eh_host_reset_handler = NCR5380_host_reset,
.can_queue = 16,
.this_id = 7,
.sg_tablesize = SG_ALL,
.cmd_per_lun = 2,
.unchecked_isa_dma = 0,
.use_clustering = DISABLE_CLUSTERING,
.proc_name = "CumanaSCSI-1",
};
static int __devinit
cumanascsi1_probe(struct expansion_card *ec, const struct ecard_id *id)
{
struct Scsi_Host *host;
int ret = -ENOMEM;
host = scsi_host_alloc(&cumanascsi_template, sizeof(struct NCR5380_hostdata));
if (!host)
goto out;
host->io_port = ecard_address(ec, ECARD_IOC, ECARD_SLOW) + 0x800;
host->irq = ec->irq;
NCR5380_init(host, 0);
host->n_io_port = 255;
if (!(request_region(host->io_port, host->n_io_port, "CumanaSCSI-1"))) {
ret = -EBUSY;
goto out_free;
}
((struct NCR5380_hostdata *)host->hostdata)->ctrl = 0;
outb(0x00, host->io_port - 577);
ret = request_irq(host->irq, cumanascsi_intr, SA_INTERRUPT,
"CumanaSCSI-1", host);
if (ret) {
printk("scsi%d: IRQ%d not free: %d\n",
host->host_no, host->irq, ret);
goto out_release;
}
printk("scsi%d: at port 0x%08lx irq %d",
host->host_no, host->io_port, host->irq);
printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
host->can_queue, host->cmd_per_lun, CUMANASCSI_PUBLIC_RELEASE);
printk("\nscsi%d:", host->host_no);
NCR5380_print_options(host);
printk("\n");
ret = scsi_add_host(host, &ec->dev);
if (ret)
goto out_free_irq;
scsi_scan_host(host);
goto out;
out_free_irq:
free_irq(host->irq, host);
out_release:
release_region(host->io_port, host->n_io_port);
out_free:
scsi_host_put(host);
out:
return ret;
}
static void __devexit cumanascsi1_remove(struct expansion_card *ec)
{
struct Scsi_Host *host = ecard_get_drvdata(ec);
ecard_set_drvdata(ec, NULL);
scsi_remove_host(host);
free_irq(host->irq, host);
NCR5380_exit(host);
release_region(host->io_port, host->n_io_port);
scsi_host_put(host);
}
static const struct ecard_id cumanascsi1_cids[] = {
{ MANU_CUMANA, PROD_CUMANA_SCSI_1 },
{ 0xffff, 0xffff }
};
static struct ecard_driver cumanascsi1_driver = {
.probe = cumanascsi1_probe,
.remove = __devexit_p(cumanascsi1_remove),
.id_table = cumanascsi1_cids,
.drv = {
.name = "cumanascsi1",
},
};
static int __init cumanascsi_init(void)
{
return ecard_register_driver(&cumanascsi1_driver);
}
static void __exit cumanascsi_exit(void)
{
ecard_remove_driver(&cumanascsi1_driver);
}
module_init(cumanascsi_init);
module_exit(cumanascsi_exit);
MODULE_DESCRIPTION("Cumana SCSI-1 driver for Acorn machines");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,561 @@
/*
* linux/drivers/acorn/scsi/cumana_2.c
*
* Copyright (C) 1997-2002 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Changelog:
* 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.
* 27-06-1998 RMK Changed asm/delay.h to linux/delay.h
* 18-08-1998 RMK 0.0.3 Fixed synchronous transfer depth.
* 02-04-2000 RMK 0.0.4 Updated for new error handling code.
*/
#include <linux/module.h>
#include <linux/blkdev.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/dma-mapping.h>
#include <asm/dma.h>
#include <asm/ecard.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/pgtable.h>
#include "../scsi.h"
#include <scsi/scsi_host.h>
#include "fas216.h"
#include "scsi.h"
#include <scsi/scsicam.h>
#define CUMANASCSI2_STATUS (0x0000)
#define STATUS_INT (1 << 0)
#define STATUS_DRQ (1 << 1)
#define STATUS_LATCHED (1 << 3)
#define CUMANASCSI2_ALATCH (0x0014)
#define ALATCH_ENA_INT (3)
#define ALATCH_DIS_INT (2)
#define ALATCH_ENA_TERM (5)
#define ALATCH_DIS_TERM (4)
#define ALATCH_ENA_BIT32 (11)
#define ALATCH_DIS_BIT32 (10)
#define ALATCH_ENA_DMA (13)
#define ALATCH_DIS_DMA (12)
#define ALATCH_DMA_OUT (15)
#define ALATCH_DMA_IN (14)
#define CUMANASCSI2_PSEUDODMA (0x0200)
#define CUMANASCSI2_FAS216_OFFSET (0x0300)
#define CUMANASCSI2_FAS216_SHIFT 2
/*
* Version
*/
#define VERSION "1.00 (13/11/2002 2.5.47)"
/*
* Use term=0,1,0,0,0 to turn terminators on/off
*/
static int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 };
#define NR_SG 256
struct cumanascsi2_info {
FAS216_Info info;
struct expansion_card *ec;
void *status; /* card status register */
void *alatch; /* Control register */
unsigned int terms; /* Terminator state */
void *dmaarea; /* Pseudo DMA area */
struct scatterlist sg[NR_SG]; /* Scatter DMA list */
};
#define CSTATUS_IRQ (1 << 0)
#define CSTATUS_DRQ (1 << 1)
/* Prototype: void cumanascsi_2_irqenable(ec, irqnr)
* Purpose : Enable interrupts on Cumana SCSI 2 card
* Params : ec - expansion card structure
* : irqnr - interrupt number
*/
static void
cumanascsi_2_irqenable(struct expansion_card *ec, int irqnr)
{
writeb(ALATCH_ENA_INT, ec->irq_data);
}
/* Prototype: void cumanascsi_2_irqdisable(ec, irqnr)
* Purpose : Disable interrupts on Cumana SCSI 2 card
* Params : ec - expansion card structure
* : irqnr - interrupt number
*/
static void
cumanascsi_2_irqdisable(struct expansion_card *ec, int irqnr)
{
writeb(ALATCH_DIS_INT, ec->irq_data);
}
static const expansioncard_ops_t cumanascsi_2_ops = {
.irqenable = cumanascsi_2_irqenable,
.irqdisable = cumanascsi_2_irqdisable,
};
/* Prototype: void cumanascsi_2_terminator_ctl(host, on_off)
* Purpose : Turn the Cumana SCSI 2 terminators on or off
* Params : host - card to turn on/off
* : on_off - !0 to turn on, 0 to turn off
*/
static void
cumanascsi_2_terminator_ctl(struct Scsi_Host *host, int on_off)
{
struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata;
if (on_off) {
info->terms = 1;
writeb(ALATCH_ENA_TERM, info->alatch);
} else {
info->terms = 0;
writeb(ALATCH_DIS_TERM, info->alatch);
}
}
/* Prototype: void cumanascsi_2_intr(irq, *dev_id, *regs)
* Purpose : handle interrupts from Cumana SCSI 2 card
* Params : irq - interrupt number
* dev_id - user-defined (Scsi_Host structure)
* regs - processor registers at interrupt
*/
static irqreturn_t
cumanascsi_2_intr(int irq, void *dev_id, struct pt_regs *regs)
{
struct cumanascsi2_info *info = dev_id;
return fas216_intr(&info->info);
}
/* Prototype: fasdmatype_t cumanascsi_2_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 : type of transfer to be performed
*/
static fasdmatype_t
cumanascsi_2_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp,
fasdmadir_t direction, fasdmatype_t min_type)
{
struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata;
struct device *dev = scsi_get_device(host);
int dmach = host->dma_channel;
writeb(ALATCH_DIS_DMA, info->alatch);
if (dmach != NO_DMA &&
(min_type == fasdma_real_all || SCp->this_residual >= 512)) {
int bufs, map_dir, dma_dir, alatch_dir;
bufs = copy_SCp_to_sg(&info->sg[0], SCp, NR_SG);
if (direction == DMA_OUT)
map_dir = DMA_TO_DEVICE,
dma_dir = DMA_MODE_WRITE,
alatch_dir = ALATCH_DMA_OUT;
else
map_dir = DMA_FROM_DEVICE,
dma_dir = DMA_MODE_READ,
alatch_dir = ALATCH_DMA_IN;
dma_map_sg(dev, info->sg, bufs + 1, map_dir);
disable_dma(dmach);
set_dma_sg(dmach, info->sg, bufs + 1);
writeb(alatch_dir, info->alatch);
set_dma_mode(dmach, dma_dir);
enable_dma(dmach);
writeb(ALATCH_ENA_DMA, info->alatch);
writeb(ALATCH_DIS_BIT32, info->alatch);
return fasdma_real_all;
}
/*
* If we're not doing DMA,
* we'll do pseudo DMA
*/
return fasdma_pio;
}
/*
* Prototype: void cumanascsi_2_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
*/
static void
cumanascsi_2_dma_pseudo(struct Scsi_Host *host, Scsi_Pointer *SCp,
fasdmadir_t direction, int transfer)
{
struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata;
unsigned int length;
unsigned char *addr;
length = SCp->this_residual;
addr = SCp->ptr;
if (direction == DMA_OUT)
#if 0
while (length > 1) {
unsigned long word;
unsigned int status = readb(info->status);
if (status & STATUS_INT)
goto end;
if (!(status & STATUS_DRQ))
continue;
word = *addr | *(addr + 1) << 8;
writew(word, info->dmaarea);
addr += 2;
length -= 2;
}
#else
printk ("PSEUDO_OUT???\n");
#endif
else {
if (transfer && (transfer & 255)) {
while (length >= 256) {
unsigned int status = readb(info->status);
if (status & STATUS_INT)
return;
if (!(status & STATUS_DRQ))
continue;
readsw(info->dmaarea, addr, 256 >> 1);
addr += 256;
length -= 256;
}
}
while (length > 0) {
unsigned long word;
unsigned int status = readb(info->status);
if (status & STATUS_INT)
return;
if (!(status & STATUS_DRQ))
continue;
word = readw(info->dmaarea);
*addr++ = word;
if (--length > 0) {
*addr++ = word >> 8;
length --;
}
}
}
}
/* Prototype: int cumanascsi_2_dma_stop(host, SCpnt)
* Purpose : stops DMA/PIO
* Params : host - host
* SCpnt - command
*/
static void
cumanascsi_2_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp)
{
struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata;
if (host->dma_channel != NO_DMA) {
writeb(ALATCH_DIS_DMA, info->alatch);
disable_dma(host->dma_channel);
}
}
/* Prototype: const char *cumanascsi_2_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 *cumanascsi_2_info(struct Scsi_Host *host)
{
struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata;
static char string[150];
sprintf(string, "%s (%s) in slot %d v%s terminators o%s",
host->hostt->name, info->info.scsi.type, info->ec->slot_no,
VERSION, info->terms ? "n" : "ff");
return string;
}
/* Prototype: int cumanascsi_2_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
* Purpose : Set a driver specific function
* Params : host - host to setup
* : buffer - buffer containing string describing operation
* : length - length of string
* Returns : -EINVAL, or 0
*/
static int
cumanascsi_2_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
{
int ret = length;
if (length >= 11 && strcmp(buffer, "CUMANASCSI2") == 0) {
buffer += 11;
length -= 11;
if (length >= 5 && strncmp(buffer, "term=", 5) == 0) {
if (buffer[5] == '1')
cumanascsi_2_terminator_ctl(host, 1);
else if (buffer[5] == '0')
cumanascsi_2_terminator_ctl(host, 0);
else
ret = -EINVAL;
} else
ret = -EINVAL;
} else
ret = -EINVAL;
return ret;
}
/* Prototype: int cumanascsi_2_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 cumanascsi_2_proc_info (struct Scsi_Host *host, char *buffer, char **start, off_t offset,
int length, int inout)
{
struct cumanascsi2_info *info;
char *p = buffer;
int pos;
if (inout == 1)
return cumanascsi_2_set_proc_info(host, buffer, length);
info = (struct cumanascsi2_info *)host->hostdata;
p += sprintf(p, "Cumana SCSI II driver v%s\n", VERSION);
p += fas216_print_host(&info->info, p);
p += sprintf(p, "Term : o%s\n",
info->terms ? "n" : "ff");
p += fas216_print_stats(&info->info, p);
p += fas216_print_devices(&info->info, p);
*start = buffer + offset;
pos = p - buffer - offset;
if (pos > length)
pos = length;
return pos;
}
static Scsi_Host_Template cumanascsi2_template = {
.module = THIS_MODULE,
.proc_info = cumanascsi_2_proc_info,
.name = "Cumana SCSI II",
.info = cumanascsi_2_info,
.queuecommand = fas216_queue_command,
.eh_host_reset_handler = fas216_eh_host_reset,
.eh_bus_reset_handler = fas216_eh_bus_reset,
.eh_device_reset_handler = fas216_eh_device_reset,
.eh_abort_handler = fas216_eh_abort,
.can_queue = 1,
.this_id = 7,
.sg_tablesize = SG_ALL,
.cmd_per_lun = 1,
.use_clustering = DISABLE_CLUSTERING,
.proc_name = "cumanascsi2",
};
static int __devinit
cumanascsi2_probe(struct expansion_card *ec, const struct ecard_id *id)
{
struct Scsi_Host *host;
struct cumanascsi2_info *info;
unsigned long resbase, reslen;
unsigned char *base;
int ret;
ret = ecard_request_resources(ec);
if (ret)
goto out;
resbase = ecard_resource_start(ec, ECARD_RES_MEMC);
reslen = ecard_resource_len(ec, ECARD_RES_MEMC);
base = ioremap(resbase, reslen);
if (!base) {
ret = -ENOMEM;
goto out_region;
}
host = scsi_host_alloc(&cumanascsi2_template,
sizeof(struct cumanascsi2_info));
if (!host) {
ret = -ENOMEM;
goto out_unmap;
}
host->base = (unsigned long)base;
host->irq = ec->irq;
host->dma_channel = ec->dma;
ecard_set_drvdata(ec, host);
info = (struct cumanascsi2_info *)host->hostdata;
info->ec = ec;
info->dmaarea = base + CUMANASCSI2_PSEUDODMA;
info->status = base + CUMANASCSI2_STATUS;
info->alatch = base + CUMANASCSI2_ALATCH;
ec->irqaddr = info->status;
ec->irqmask = STATUS_INT;
ec->irq_data = base + CUMANASCSI2_ALATCH;
ec->ops = &cumanascsi_2_ops;
cumanascsi_2_terminator_ctl(host, term[ec->slot_no]);
info->info.scsi.io_base = base + CUMANASCSI2_FAS216_OFFSET;
info->info.scsi.io_shift = CUMANASCSI2_FAS216_SHIFT;
info->info.scsi.irq = host->irq;
info->info.ifcfg.clockrate = 40; /* MHz */
info->info.ifcfg.select_timeout = 255;
info->info.ifcfg.asyncperiod = 200; /* ns */
info->info.ifcfg.sync_max_depth = 7;
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.ifcfg.capabilities = FASCAP_PSEUDODMA;
info->info.dma.setup = cumanascsi_2_dma_setup;
info->info.dma.pseudo = cumanascsi_2_dma_pseudo;
info->info.dma.stop = cumanascsi_2_dma_stop;
ret = fas216_init(host);
if (ret)
goto out_free;
ret = request_irq(host->irq, cumanascsi_2_intr,
SA_INTERRUPT, "cumanascsi2", info);
if (ret) {
printk("scsi%d: IRQ%d not free: %d\n",
host->host_no, host->irq, ret);
goto out_release;
}
if (host->dma_channel != NO_DMA) {
if (request_dma(host->dma_channel, "cumanascsi2")) {
printk("scsi%d: DMA%d not free, using PIO\n",
host->host_no, host->dma_channel);
host->dma_channel = NO_DMA;
} else {
set_dma_speed(host->dma_channel, 180);
info->info.ifcfg.capabilities |= FASCAP_DMA;
}
}
ret = fas216_add(host, &ec->dev);
if (ret == 0)
goto out;
if (host->dma_channel != NO_DMA)
free_dma(host->dma_channel);
free_irq(host->irq, host);
out_release:
fas216_release(host);
out_free:
scsi_host_put(host);
out_unmap:
iounmap(base);
out_region:
ecard_release_resources(ec);
out:
return ret;
}
static void __devexit cumanascsi2_remove(struct expansion_card *ec)
{
struct Scsi_Host *host = ecard_get_drvdata(ec);
struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata;
ecard_set_drvdata(ec, NULL);
fas216_remove(host);
if (host->dma_channel != NO_DMA)
free_dma(host->dma_channel);
free_irq(host->irq, info);
iounmap((void *)host->base);
fas216_release(host);
scsi_host_put(host);
ecard_release_resources(ec);
}
static const struct ecard_id cumanascsi2_cids[] = {
{ MANU_CUMANA, PROD_CUMANA_SCSI_2 },
{ 0xffff, 0xffff },
};
static struct ecard_driver cumanascsi2_driver = {
.probe = cumanascsi2_probe,
.remove = __devexit_p(cumanascsi2_remove),
.id_table = cumanascsi2_cids,
.drv = {
.name = "cumanascsi2",
},
};
static int __init cumanascsi2_init(void)
{
return ecard_register_driver(&cumanascsi2_driver);
}
static void __exit cumanascsi2_exit(void)
{
ecard_remove_driver(&cumanascsi2_driver);
}
module_init(cumanascsi2_init);
module_exit(cumanascsi2_exit);
MODULE_AUTHOR("Russell King");
MODULE_DESCRIPTION("Cumana SCSI-2 driver for Acorn machines");
MODULE_PARM(term, "1-8i");
MODULE_PARM_DESC(term, "SCSI bus termination");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,239 @@
#define AUTOSENSE
/* #define PSEUDO_DMA */
/*
* EcoSCSI Generic NCR5380 driver
*
* Copyright 1995, Russell King
*
* ALPHA RELEASE 1.
*
* For more information, please consult
*
* NCR 5380 Family
* SCSI Protocol Controller
* Databook
*
* NCR Microelectronics
* 1635 Aeroplaza Drive
* Colorado Springs, CO 80916
* 1+ (719) 578-3400
* 1+ (800) 334-5454
*/
#include <linux/module.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/blkdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include "../scsi.h"
#include <scsi/scsi_host.h>
#define NCR5380_implementation_fields int port, ctrl
#define NCR5380_local_declare() struct Scsi_Host *_instance
#define NCR5380_setup(instance) _instance = instance
#define NCR5380_read(reg) ecoscsi_read(_instance, reg)
#define NCR5380_write(reg, value) ecoscsi_write(_instance, reg, value)
#define NCR5380_intr ecoscsi_intr
#define NCR5380_queue_command ecoscsi_queue_command
#define NCR5380_proc_info ecoscsi_proc_info
#include "../NCR5380.h"
#define ECOSCSI_PUBLIC_RELEASE 1
static char ecoscsi_read(struct Scsi_Host *instance, int reg)
{
int iobase = instance->io_port;
outb(reg | 8, iobase);
return inb(iobase + 1);
}
static void ecoscsi_write(struct Scsi_Host *instance, int reg, int value)
{
int iobase = instance->io_port;
outb(reg | 8, iobase);
outb(value, iobase + 1);
}
/*
* Function : ecoscsi_setup(char *str, int *ints)
*
* Purpose : LILO command line initialization of the overrides array,
*
* Inputs : str - unused, ints - array of integer parameters with ints[0]
* equal to the number of ints.
*
*/
void ecoscsi_setup(char *str, int *ints)
{
}
const char * ecoscsi_info (struct Scsi_Host *spnt)
{
return "";
}
#if 0
#define STAT(p) inw(p + 144)
static inline int NCR5380_pwrite(struct Scsi_Host *host, unsigned char *addr,
int len)
{
int iobase = host->io_port;
printk("writing %p len %d\n",addr, len);
if(!len) return -1;
while(1)
{
int status;
while(((status = STAT(iobase)) & 0x100)==0);
}
}
static inline int NCR5380_pread(struct Scsi_Host *host, unsigned char *addr,
int len)
{
int iobase = host->io_port;
int iobase2= host->io_port + 0x100;
unsigned char *start = addr;
int s;
printk("reading %p len %d\n",addr, len);
outb(inb(iobase + 128), iobase + 135);
while(len > 0)
{
int status,b,i, timeout;
timeout = 0x07FFFFFF;
while(((status = STAT(iobase)) & 0x100)==0)
{
timeout--;
if(status & 0x200 || !timeout)
{
printk("status = %p\n",status);
outb(0, iobase + 135);
return 1;
}
}
if(len >= 128)
{
for(i=0; i<64; i++)
{
b = inw(iobase + 136);
*addr++ = b;
*addr++ = b>>8;
}
len -= 128;
}
else
{
b = inw(iobase + 136);
*addr ++ = b;
len -= 1;
if(len)
*addr ++ = b>>8;
len -= 1;
}
}
outb(0, iobase + 135);
printk("first bytes = %02X %02X %02X %20X %02X %02X %02X\n",*start, start[1], start[2], start[3], start[4], start[5], start[6]);
return 1;
}
#endif
#undef STAT
#define BOARD_NORMAL 0
#define BOARD_NCR53C400 1
#include "../NCR5380.c"
static Scsi_Host_Template ecoscsi_template = {
.module = THIS_MODULE,
.name = "Serial Port EcoSCSI NCR5380",
.proc_name = "ecoscsi",
.info = ecoscsi_info,
.queuecommand = ecoscsi_queue_command,
.eh_abort_handler = NCR5380_abort,
.eh_device_reset_handler= NCR5380_device_reset,
.eh_bus_reset_handler = NCR5380_bus_reset,
.eh_host_reset_handler = NCR5380_host_reset,
.can_queue = 16,
.this_id = 7,
.sg_tablesize = SG_ALL,
.cmd_per_lun = 2,
.use_clustering = DISABLE_CLUSTERING
};
static struct Scsi_Host *host;
static int __init ecoscsi_init(void)
{
host = scsi_host_alloc(tpnt, sizeof(struct NCR5380_hostdata));
if (!host)
return 0;
host->io_port = 0x80ce8000;
host->n_io_port = 144;
host->irq = IRQ_NONE;
if (!(request_region(host->io_port, host->n_io_port, "ecoscsi")) )
goto unregister_scsi;
ecoscsi_write(host, MODE_REG, 0x20); /* Is it really SCSI? */
if (ecoscsi_read(host, MODE_REG) != 0x20) /* Write to a reg. */
goto release_reg;
ecoscsi_write(host, MODE_REG, 0x00 ); /* it back. */
if (ecoscsi_read(host, MODE_REG) != 0x00)
goto release_reg;
NCR5380_init(host, 0);
printk("scsi%d: at port 0x%08lx irqs disabled", host->host_no, host->io_port);
printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
host->can_queue, host->cmd_per_lun, ECOSCSI_PUBLIC_RELEASE);
printk("\nscsi%d:", host->host_no);
NCR5380_print_options(host);
printk("\n");
scsi_add_host(host, NULL); /* XXX handle failure */
scsi_scan_host(host);
return 0;
release_reg:
release_region(host->io_port, host->n_io_port);
unregister_scsi:
scsi_host_put(host);
return -ENODEV;
}
static void __exit ecoscsi_exit(void)
{
scsi_remove_host(host);
if (shpnt->irq != IRQ_NONE)
free_irq(shpnt->irq, NULL);
NCR5380_exit(host);
if (shpnt->io_port)
release_region(shpnt->io_port, shpnt->n_io_port);
scsi_host_put(host);
return 0;
}
module_init(ecoscsi_init);
module_exit(ecoscsi_exit);
MODULE_AUTHOR("Russell King");
MODULE_DESCRIPTION("Econet-SCSI driver for Acorn machines");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,682 @@
/*
* linux/drivers/acorn/scsi/eesox.c
*
* Copyright (C) 1997-2003 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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:
* 01-10-1997 RMK Created, READONLY version
* 15-02-1998 RMK READ/WRITE version
* added DMA support and hardware definitions
* 14-03-1998 RMK Updated DMA support
* Added terminator control
* 15-04-1998 RMK Only do PIO if FAS216 will allow it.
* 27-06-1998 RMK Changed asm/delay.h to linux/delay.h
* 02-04-2000 RMK 0.0.3 Fixed NO_IRQ/NO_DMA problem, updated for new
* error handling code.
*/
#include <linux/module.h>
#include <linux/blkdev.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/dma-mapping.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/dma.h>
#include <asm/ecard.h>
#include <asm/pgtable.h>
#include "../scsi.h"
#include <scsi/scsi_host.h>
#include "fas216.h"
#include "scsi.h"
#include <scsi/scsicam.h>
#define EESOX_FAS216_OFFSET 0x3000
#define EESOX_FAS216_SHIFT 5
#define EESOX_DMASTAT 0x2800
#define EESOX_STAT_INTR 0x01
#define EESOX_STAT_DMA 0x02
#define EESOX_CONTROL 0x2800
#define EESOX_INTR_ENABLE 0x04
#define EESOX_TERM_ENABLE 0x02
#define EESOX_RESET 0x01
#define EESOX_DMADATA 0x3800
#define VERSION "1.10 (17/01/2003 2.5.59)"
/*
* Use term=0,1,0,0,0 to turn terminators on/off
*/
static int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 };
#define NR_SG 256
struct eesoxscsi_info {
FAS216_Info info;
struct expansion_card *ec;
void *ctl_port;
unsigned int control;
struct scatterlist sg[NR_SG]; /* Scatter DMA list */
};
/* Prototype: void eesoxscsi_irqenable(ec, irqnr)
* Purpose : Enable interrupts on EESOX SCSI card
* Params : ec - expansion card structure
* : irqnr - interrupt number
*/
static void
eesoxscsi_irqenable(struct expansion_card *ec, int irqnr)
{
struct eesoxscsi_info *info = (struct eesoxscsi_info *)ec->irq_data;
info->control |= EESOX_INTR_ENABLE;
writeb(info->control, info->ctl_port);
}
/* Prototype: void eesoxscsi_irqdisable(ec, irqnr)
* Purpose : Disable interrupts on EESOX SCSI card
* Params : ec - expansion card structure
* : irqnr - interrupt number
*/
static void
eesoxscsi_irqdisable(struct expansion_card *ec, int irqnr)
{
struct eesoxscsi_info *info = (struct eesoxscsi_info *)ec->irq_data;
info->control &= ~EESOX_INTR_ENABLE;
writeb(info->control, info->ctl_port);
}
static const expansioncard_ops_t eesoxscsi_ops = {
.irqenable = eesoxscsi_irqenable,
.irqdisable = eesoxscsi_irqdisable,
};
/* Prototype: void eesoxscsi_terminator_ctl(*host, on_off)
* Purpose : Turn the EESOX SCSI terminators on or off
* Params : host - card to turn on/off
* : on_off - !0 to turn on, 0 to turn off
*/
static void
eesoxscsi_terminator_ctl(struct Scsi_Host *host, int on_off)
{
struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata;
unsigned long flags;
spin_lock_irqsave(host->host_lock, flags);
if (on_off)
info->control |= EESOX_TERM_ENABLE;
else
info->control &= ~EESOX_TERM_ENABLE;
writeb(info->control, info->ctl_port);
spin_unlock_irqrestore(host->host_lock, flags);
}
/* Prototype: void eesoxscsi_intr(irq, *dev_id, *regs)
* Purpose : handle interrupts from EESOX SCSI card
* Params : irq - interrupt number
* dev_id - user-defined (Scsi_Host structure)
* regs - processor registers at interrupt
*/
static irqreturn_t
eesoxscsi_intr(int irq, void *dev_id, struct pt_regs *regs)
{
struct eesoxscsi_info *info = dev_id;
return fas216_intr(&info->info);
}
/* Prototype: fasdmatype_t eesoxscsi_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 : type of transfer to be performed
*/
static fasdmatype_t
eesoxscsi_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp,
fasdmadir_t direction, fasdmatype_t min_type)
{
struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata;
struct device *dev = scsi_get_device(host);
int dmach = host->dma_channel;
if (dmach != NO_DMA &&
(min_type == fasdma_real_all || SCp->this_residual >= 512)) {
int bufs, map_dir, dma_dir;
bufs = copy_SCp_to_sg(&info->sg[0], SCp, NR_SG);
if (direction == DMA_OUT)
map_dir = DMA_TO_DEVICE,
dma_dir = DMA_MODE_WRITE;
else
map_dir = DMA_FROM_DEVICE,
dma_dir = DMA_MODE_READ;
dma_map_sg(dev, info->sg, bufs + 1, map_dir);
disable_dma(dmach);
set_dma_sg(dmach, info->sg, bufs + 1);
set_dma_mode(dmach, dma_dir);
enable_dma(dmach);
return fasdma_real_all;
}
/*
* We don't do DMA, we only do slow PIO
*
* Some day, we will do Pseudo DMA
*/
return fasdma_pseudo;
}
static void eesoxscsi_buffer_in(void *buf, int length, void *base)
{
const void *reg_fas = base + EESOX_FAS216_OFFSET;
const void *reg_dmastat = base + EESOX_DMASTAT;
const void *reg_dmadata = base + EESOX_DMADATA;
const register unsigned long mask = 0xffff;
do {
unsigned int status;
/*
* Interrupt request?
*/
status = readb(reg_fas + (REG_STAT << EESOX_FAS216_SHIFT));
if (status & STAT_INT)
break;
/*
* DMA request active?
*/
status = readb(reg_dmastat);
if (!(status & EESOX_STAT_DMA))
continue;
/*
* Get number of bytes in FIFO
*/
status = readb(reg_fas + (REG_CFIS << EESOX_FAS216_SHIFT)) & CFIS_CF;
if (status > 16)
status = 16;
if (status > length)
status = length;
/*
* Align buffer.
*/
if (((u32)buf) & 2 && status >= 2) {
*(u16 *)buf = readl(reg_dmadata);
buf += 2;
status -= 2;
length -= 2;
}
if (status >= 8) {
unsigned long l1, l2;
l1 = readl(reg_dmadata) & mask;
l1 |= readl(reg_dmadata) << 16;
l2 = readl(reg_dmadata) & mask;
l2 |= readl(reg_dmadata) << 16;
*(u32 *)buf = l1;
buf += 4;
*(u32 *)buf = l2;
buf += 4;
length -= 8;
continue;
}
if (status >= 4) {
unsigned long l1;
l1 = readl(reg_dmadata) & mask;
l1 |= readl(reg_dmadata) << 16;
*(u32 *)buf = l1;
buf += 4;
length -= 4;
continue;
}
if (status >= 2) {
*(u16 *)buf = readl(reg_dmadata);
buf += 2;
length -= 2;
}
} while (length);
}
static void eesoxscsi_buffer_out(void *buf, int length, void *base)
{
const void *reg_fas = base + EESOX_FAS216_OFFSET;
const void *reg_dmastat = base + EESOX_DMASTAT;
const void *reg_dmadata = base + EESOX_DMADATA;
do {
unsigned int status;
/*
* Interrupt request?
*/
status = readb(reg_fas + (REG_STAT << EESOX_FAS216_SHIFT));
if (status & STAT_INT)
break;
/*
* DMA request active?
*/
status = readb(reg_dmastat);
if (!(status & EESOX_STAT_DMA))
continue;
/*
* Get number of bytes in FIFO
*/
status = readb(reg_fas + (REG_CFIS << EESOX_FAS216_SHIFT)) & CFIS_CF;
if (status > 16)
status = 16;
status = 16 - status;
if (status > length)
status = length;
status &= ~1;
/*
* Align buffer.
*/
if (((u32)buf) & 2 && status >= 2) {
writel(*(u16 *)buf << 16, reg_dmadata);
buf += 2;
status -= 2;
length -= 2;
}
if (status >= 8) {
unsigned long l1, l2;
l1 = *(u32 *)buf;
buf += 4;
l2 = *(u32 *)buf;
buf += 4;
writel(l1 << 16, reg_dmadata);
writel(l1, reg_dmadata);
writel(l2 << 16, reg_dmadata);
writel(l2, reg_dmadata);
length -= 8;
continue;
}
if (status >= 4) {
unsigned long l1;
l1 = *(u32 *)buf;
buf += 4;
writel(l1 << 16, reg_dmadata);
writel(l1, reg_dmadata);
length -= 4;
continue;
}
if (status >= 2) {
writel(*(u16 *)buf << 16, reg_dmadata);
buf += 2;
length -= 2;
}
} while (length);
}
static void
eesoxscsi_dma_pseudo(struct Scsi_Host *host, Scsi_Pointer *SCp,
fasdmadir_t dir, int transfer_size)
{
void *base = (void *)host->base;
if (dir == DMA_IN) {
eesoxscsi_buffer_in(SCp->ptr, SCp->this_residual, base);
} else {
eesoxscsi_buffer_out(SCp->ptr, SCp->this_residual, base);
}
}
/* Prototype: int eesoxscsi_dma_stop(host, SCpnt)
* Purpose : stops DMA/PIO
* Params : host - host
* SCpnt - command
*/
static void
eesoxscsi_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp)
{
if (host->dma_channel != NO_DMA)
disable_dma(host->dma_channel);
}
/* Prototype: const char *eesoxscsi_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 *eesoxscsi_info(struct Scsi_Host *host)
{
struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata;
static char string[150];
sprintf(string, "%s (%s) in slot %d v%s terminators o%s",
host->hostt->name, info->info.scsi.type, info->ec->slot_no,
VERSION, info->control & EESOX_TERM_ENABLE ? "n" : "ff");
return string;
}
/* Prototype: int eesoxscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
* Purpose : Set a driver specific function
* Params : host - host to setup
* : buffer - buffer containing string describing operation
* : length - length of string
* Returns : -EINVAL, or 0
*/
static int
eesoxscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
{
int ret = length;
if (length >= 9 && strncmp(buffer, "EESOXSCSI", 9) == 0) {
buffer += 9;
length -= 9;
if (length >= 5 && strncmp(buffer, "term=", 5) == 0) {
if (buffer[5] == '1')
eesoxscsi_terminator_ctl(host, 1);
else if (buffer[5] == '0')
eesoxscsi_terminator_ctl(host, 0);
else
ret = -EINVAL;
} else
ret = -EINVAL;
} else
ret = -EINVAL;
return ret;
}
/* Prototype: int eesoxscsi_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 eesoxscsi_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset,
int length, int inout)
{
struct eesoxscsi_info *info;
char *p = buffer;
int pos;
if (inout == 1)
return eesoxscsi_set_proc_info(host, buffer, length);
info = (struct eesoxscsi_info *)host->hostdata;
p += sprintf(p, "EESOX SCSI driver v%s\n", VERSION);
p += fas216_print_host(&info->info, p);
p += sprintf(p, "Term : o%s\n",
info->control & EESOX_TERM_ENABLE ? "n" : "ff");
p += fas216_print_stats(&info->info, p);
p += fas216_print_devices(&info->info, p);
*start = buffer + offset;
pos = p - buffer - offset;
if (pos > length)
pos = length;
return pos;
}
static ssize_t eesoxscsi_show_term(struct device *dev, char *buf)
{
struct expansion_card *ec = ECARD_DEV(dev);
struct Scsi_Host *host = ecard_get_drvdata(ec);
struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata;
return sprintf(buf, "%d\n", info->control & EESOX_TERM_ENABLE ? 1 : 0);
}
static ssize_t eesoxscsi_store_term(struct device *dev, const char *buf, size_t len)
{
struct expansion_card *ec = ECARD_DEV(dev);
struct Scsi_Host *host = ecard_get_drvdata(ec);
struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata;
unsigned long flags;
if (len > 1) {
spin_lock_irqsave(host->host_lock, flags);
if (buf[0] != '0') {
info->control |= EESOX_TERM_ENABLE;
} else {
info->control &= ~EESOX_TERM_ENABLE;
}
writeb(info->control, info->ctl_port);
spin_unlock_irqrestore(host->host_lock, flags);
}
return len;
}
static DEVICE_ATTR(bus_term, S_IRUGO | S_IWUSR,
eesoxscsi_show_term, eesoxscsi_store_term);
static Scsi_Host_Template eesox_template = {
.module = THIS_MODULE,
.proc_info = eesoxscsi_proc_info,
.name = "EESOX SCSI",
.info = eesoxscsi_info,
.queuecommand = fas216_queue_command,
.eh_host_reset_handler = fas216_eh_host_reset,
.eh_bus_reset_handler = fas216_eh_bus_reset,
.eh_device_reset_handler = fas216_eh_device_reset,
.eh_abort_handler = fas216_eh_abort,
.can_queue = 1,
.this_id = 7,
.sg_tablesize = SG_ALL,
.cmd_per_lun = 1,
.use_clustering = DISABLE_CLUSTERING,
.proc_name = "eesox",
};
static int __devinit
eesoxscsi_probe(struct expansion_card *ec, const struct ecard_id *id)
{
struct Scsi_Host *host;
struct eesoxscsi_info *info;
unsigned long resbase, reslen;
unsigned char *base;
int ret;
ret = ecard_request_resources(ec);
if (ret)
goto out;
resbase = ecard_resource_start(ec, ECARD_RES_IOCFAST);
reslen = ecard_resource_len(ec, ECARD_RES_IOCFAST);
base = ioremap(resbase, reslen);
if (!base) {
ret = -ENOMEM;
goto out_region;
}
host = scsi_host_alloc(&eesox_template,
sizeof(struct eesoxscsi_info));
if (!host) {
ret = -ENOMEM;
goto out_unmap;
}
host->base = (unsigned long)base;
host->irq = ec->irq;
host->dma_channel = ec->dma;
ecard_set_drvdata(ec, host);
info = (struct eesoxscsi_info *)host->hostdata;
info->ec = ec;
info->ctl_port = base + EESOX_CONTROL;
info->control = term[ec->slot_no] ? EESOX_TERM_ENABLE : 0;
writeb(info->control, info->ctl_port);
ec->irqaddr = base + EESOX_DMASTAT;
ec->irqmask = EESOX_STAT_INTR;
ec->irq_data = info;
ec->ops = &eesoxscsi_ops;
info->info.scsi.io_base = base + EESOX_FAS216_OFFSET;
info->info.scsi.io_shift = EESOX_FAS216_SHIFT;
info->info.scsi.irq = host->irq;
info->info.ifcfg.clockrate = 40; /* MHz */
info->info.ifcfg.select_timeout = 255;
info->info.ifcfg.asyncperiod = 200; /* ns */
info->info.ifcfg.sync_max_depth = 7;
info->info.ifcfg.cntl3 = CNTL3_FASTSCSI | CNTL3_FASTCLK;
info->info.ifcfg.disconnect_ok = 1;
info->info.ifcfg.wide_max_size = 0;
info->info.ifcfg.capabilities = FASCAP_PSEUDODMA;
info->info.dma.setup = eesoxscsi_dma_setup;
info->info.dma.pseudo = eesoxscsi_dma_pseudo;
info->info.dma.stop = eesoxscsi_dma_stop;
device_create_file(&ec->dev, &dev_attr_bus_term);
ret = fas216_init(host);
if (ret)
goto out_free;
ret = request_irq(host->irq, eesoxscsi_intr, 0, "eesoxscsi", info);
if (ret) {
printk("scsi%d: IRQ%d not free: %d\n",
host->host_no, host->irq, ret);
goto out_remove;
}
if (host->dma_channel != NO_DMA) {
if (request_dma(host->dma_channel, "eesox")) {
printk("scsi%d: DMA%d not free, DMA disabled\n",
host->host_no, host->dma_channel);
host->dma_channel = NO_DMA;
} else {
set_dma_speed(host->dma_channel, 180);
info->info.ifcfg.capabilities |= FASCAP_DMA;
info->info.ifcfg.cntl3 |= CNTL3_BS8;
}
}
ret = fas216_add(host, &ec->dev);
if (ret == 0)
goto out;
if (host->dma_channel != NO_DMA)
free_dma(host->dma_channel);
free_irq(host->irq, host);
out_remove:
fas216_remove(host);
out_free:
device_remove_file(&ec->dev, &dev_attr_bus_term);
scsi_host_put(host);
out_unmap:
iounmap(base);
out_region:
ecard_release_resources(ec);
out:
return ret;
}
static void __devexit eesoxscsi_remove(struct expansion_card *ec)
{
struct Scsi_Host *host = ecard_get_drvdata(ec);
struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata;
ecard_set_drvdata(ec, NULL);
fas216_remove(host);
if (host->dma_channel != NO_DMA)
free_dma(host->dma_channel);
free_irq(host->irq, info);
device_remove_file(&ec->dev, &dev_attr_bus_term);
iounmap((void *)host->base);
fas216_release(host);
scsi_host_put(host);
ecard_release_resources(ec);
}
static const struct ecard_id eesoxscsi_cids[] = {
{ MANU_EESOX, PROD_EESOX_SCSI2 },
{ 0xffff, 0xffff },
};
static struct ecard_driver eesoxscsi_driver = {
.probe = eesoxscsi_probe,
.remove = __devexit_p(eesoxscsi_remove),
.id_table = eesoxscsi_cids,
.drv = {
.name = "eesoxscsi",
},
};
static int __init eesox_init(void)
{
return ecard_register_driver(&eesoxscsi_driver);
}
static void __exit eesox_exit(void)
{
ecard_remove_driver(&eesoxscsi_driver);
}
module_init(eesox_init);
module_exit(eesox_exit);
MODULE_AUTHOR("Russell King");
MODULE_DESCRIPTION("EESOX 'Fast' SCSI driver for Acorn machines");
MODULE_PARM(term, "1-8i");
MODULE_PARM_DESC(term, "SCSI bus termination");
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,394 @@
/*
* linux/drivers/acorn/scsi/fas216.h
*
* Copyright (C) 1997-2000 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* FAS216 generic driver
*/
#ifndef FAS216_H
#define FAS216_H
#ifndef NO_IRQ
#define NO_IRQ 255
#endif
#include "queue.h"
#include "msgqueue.h"
/* FAS register definitions */
/* transfer count low */
#define REG_CTCL (0)
#define REG_STCL (0)
/* transfer count medium */
#define REG_CTCM (1)
#define REG_STCM (1)
/* fifo data */
#define REG_FF (2)
/* command */
#define REG_CMD (3)
#define CMD_NOP 0x00
#define CMD_FLUSHFIFO 0x01
#define CMD_RESETCHIP 0x02
#define CMD_RESETSCSI 0x03
#define CMD_TRANSFERINFO 0x10
#define CMD_INITCMDCOMPLETE 0x11
#define CMD_MSGACCEPTED 0x12
#define CMD_PADBYTES 0x18
#define CMD_SETATN 0x1a
#define CMD_RSETATN 0x1b
#define CMD_SELECTWOATN 0x41
#define CMD_SELECTATN 0x42
#define CMD_SELECTATNSTOP 0x43
#define CMD_ENABLESEL 0x44
#define CMD_DISABLESEL 0x45
#define CMD_SELECTATN3 0x46
#define CMD_RESEL3 0x47
#define CMD_WITHDMA 0x80
/* status register (read) */
#define REG_STAT (4)
#define STAT_IO (1 << 0) /* IO phase */
#define STAT_CD (1 << 1) /* CD phase */
#define STAT_MSG (1 << 2) /* MSG phase */
#define STAT_TRANSFERDONE (1 << 3) /* Transfer completed */
#define STAT_TRANSFERCNTZ (1 << 4) /* Transfer counter is zero */
#define STAT_PARITYERROR (1 << 5) /* Parity error */
#define STAT_REALBAD (1 << 6) /* Something bad */
#define STAT_INT (1 << 7) /* Interrupt */
#define STAT_BUSMASK (STAT_MSG|STAT_CD|STAT_IO)
#define STAT_DATAOUT (0) /* Data out */
#define STAT_DATAIN (STAT_IO) /* Data in */
#define STAT_COMMAND (STAT_CD) /* Command out */
#define STAT_STATUS (STAT_CD|STAT_IO) /* Status In */
#define STAT_MESGOUT (STAT_MSG|STAT_CD) /* Message out */
#define STAT_MESGIN (STAT_MSG|STAT_CD|STAT_IO) /* Message In */
/* bus ID for select / reselect */
#define REG_SDID (4)
#define BUSID(target) ((target) & 7)
/* Interrupt status register (read) */
#define REG_INST (5)
#define INST_SELWOATN (1 << 0) /* Select w/o ATN */
#define INST_SELATN (1 << 1) /* Select w/ATN */
#define INST_RESELECTED (1 << 2) /* Reselected */
#define INST_FUNCDONE (1 << 3) /* Function done */
#define INST_BUSSERVICE (1 << 4) /* Bus service */
#define INST_DISCONNECT (1 << 5) /* Disconnect */
#define INST_ILLEGALCMD (1 << 6) /* Illegal command */
#define INST_BUSRESET (1 << 7) /* SCSI Bus reset */
/* Timeout register (write) */
#define REG_STIM (5)
/* Sequence step register (read) */
#define REG_IS (6)
#define IS_BITS 0x07
#define IS_SELARB 0x00 /* Select & Arb ok */
#define IS_MSGBYTESENT 0x01 /* One byte message sent*/
#define IS_NOTCOMMAND 0x02 /* Not in command state */
#define IS_EARLYPHASE 0x03 /* Early phase change */
#define IS_COMPLETE 0x04 /* Command ok */
#define IS_SOF 0x08 /* Sync off flag */
/* Transfer period step (write) */
#define REG_STP (6)
/* Synchronous Offset (write) */
#define REG_SOF (7)
/* Fifo state register (read) */
#define REG_CFIS (7)
#define CFIS_CF 0x1f /* Num bytes in FIFO */
#define CFIS_IS 0xe0 /* Step */
/* config register 1 */
#define REG_CNTL1 (8)
#define CNTL1_CID (7 << 0) /* Chip ID */
#define CNTL1_STE (1 << 3) /* Self test enable */
#define CNTL1_PERE (1 << 4) /* Parity enable reporting en. */
#define CNTL1_PTE (1 << 5) /* Parity test enable */
#define CNTL1_DISR (1 << 6) /* Disable Irq on SCSI reset */
#define CNTL1_ETM (1 << 7) /* Extended Timing Mode */
/* Clock conversion factor (read) */
#define REG_CLKF (9)
#define CLKF_F37MHZ 0x00 /* 35.01 - 40 MHz */
#define CLKF_F10MHZ 0x02 /* 10 MHz */
#define CLKF_F12MHZ 0x03 /* 10.01 - 15 MHz */
#define CLKF_F17MHZ 0x04 /* 15.01 - 20 MHz */
#define CLKF_F22MHZ 0x05 /* 20.01 - 25 MHz */
#define CLKF_F27MHZ 0x06 /* 25.01 - 30 MHz */
#define CLKF_F32MHZ 0x07 /* 30.01 - 35 MHz */
/* Chip test register (write) */
#define REG_FTM (10)
#define TEST_FTM 0x01 /* Force target mode */
#define TEST_FIM 0x02 /* Force initiator mode */
#define TEST_FHI 0x04 /* Force high impedance mode */
/* Configuration register 2 (read/write) */
#define REG_CNTL2 (11)
#define CNTL2_PGDP (1 << 0) /* Pass Th/Generate Data Parity */
#define CNTL2_PGRP (1 << 1) /* Pass Th/Generate Reg Parity */
#define CNTL2_ACDPE (1 << 2) /* Abort on Cmd/Data Parity Err */
#define CNTL2_S2FE (1 << 3) /* SCSI2 Features Enable */
#define CNTL2_TSDR (1 << 4) /* Tristate DREQ */
#define CNTL2_SBO (1 << 5) /* Select Byte Order */
#define CNTL2_ENF (1 << 6) /* Enable features */
#define CNTL2_DAE (1 << 7) /* Data Alignment Enable */
/* Configuration register 3 (read/write) */
#define REG_CNTL3 (12)
#define CNTL3_BS8 (1 << 0) /* Burst size 8 */
#define CNTL3_MDM (1 << 1) /* Modify DMA mode */
#define CNTL3_LBTM (1 << 2) /* Last Byte Transfer mode */
#define CNTL3_FASTCLK (1 << 3) /* Fast SCSI clocking */
#define CNTL3_FASTSCSI (1 << 4) /* Fast SCSI */
#define CNTL3_G2CB (1 << 5) /* Group2 SCSI support */
#define CNTL3_QTAG (1 << 6) /* Enable 3 byte msgs */
#define CNTL3_ADIDCHK (1 << 7) /* Additional ID check */
/* High transfer count (read/write) */
#define REG_CTCH (14)
#define REG_STCH (14)
/* ID register (read only) */
#define REG_ID (14)
/* Data alignment */
#define REG_DAL (15)
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_DATAOUT, /* data out to device */
PHASE_DATAIN, /* data in from device */
PHASE_MSGIN, /* message in from device */
PHASE_MSGIN_DISCONNECT, /* disconnecting from bus */
PHASE_MSGOUT, /* after message out phase */
PHASE_MSGOUT_EXPECT, /* expecting message out */
PHASE_STATUS, /* status from device */
PHASE_DONE /* Command complete */
} phase_t;
typedef enum {
DMA_OUT, /* DMA from memory to chip */
DMA_IN /* DMA from chip to memory */
} fasdmadir_t;
typedef enum {
fasdma_none, /* No dma */
fasdma_pio, /* PIO mode */
fasdma_pseudo, /* Pseudo DMA */
fasdma_real_block, /* Real DMA, on block by block basis */
fasdma_real_all /* Real DMA, on request by request */
} fasdmatype_t;
typedef enum {
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
#define FASCAP_DMA (1 << 0)
#define FASCAP_PSEUDODMA (1 << 1)
typedef struct {
unsigned long magic_start;
spinlock_t host_lock;
struct Scsi_Host *host; /* host */
Scsi_Cmnd *SCpnt; /* currently processing command */
Scsi_Cmnd *origSCpnt; /* original connecting command */
Scsi_Cmnd *reqSCpnt; /* request sense command */
Scsi_Cmnd *rstSCpnt; /* reset command */
Scsi_Cmnd *pending_SCpnt[8]; /* per-device pending commands */
int next_pending; /* next pending device */
/*
* Error recovery
*/
wait_queue_head_t eh_wait;
struct timer_list eh_timer;
unsigned int rst_dev_status;
unsigned int rst_bus_status;
/* driver information */
struct {
phase_t phase; /* current phase */
void *io_base; /* iomem base of FAS216 */
unsigned int io_port; /* base address of FAS216 */
unsigned int io_shift; /* shift to adjust reg offsets by */
unsigned char cfg[4]; /* configuration registers */
const char *type; /* chip type */
unsigned int irq; /* interrupt */
Scsi_Pointer SCp; /* current commands data pointer */
MsgQueue_t msgs; /* message queue for connected device */
unsigned int async_stp; /* Async transfer STP value */
unsigned char msgin_fifo; /* bytes in fifo at time of message in */
unsigned char message[256]; /* last message received from device */
unsigned char disconnectable:1; /* this command can be disconnected */
unsigned char aborting:1; /* aborting command */
} scsi;
/* statistics information */
struct {
unsigned int queues;
unsigned int removes;
unsigned int fins;
unsigned int reads;
unsigned int writes;
unsigned int miscs;
unsigned int disconnects;
unsigned int aborts;
unsigned int bus_resets;
unsigned int host_resets;
} stats;
/* configuration information */
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 capabilities; /* driver capabilities */
unsigned int disconnect_ok:1; /* Disconnects allowed? */
} ifcfg;
/* queue handling */
struct {
Queue_t issue; /* issue queue */
Queue_t disconnected; /* disconnected command queue */
} queues;
/* per-device info */
struct fas216_device {
unsigned char disconnect_ok:1; /* device can disconnect */
unsigned char parity_enabled:1; /* parity checking enabled */
unsigned char parity_check:1; /* need to check parity checking */
unsigned char period; /* sync xfer period in (*4ns) */
unsigned char stp; /* synchronous transfer period */
unsigned char sof; /* synchronous offset register */
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 long busyluns[64/sizeof(unsigned long)];/* array of bits indicating LUNs busy */
/* dma */
struct {
fasdmatype_t transfer_type; /* current type of DMA transfer */
fasdmatype_t (*setup) (struct Scsi_Host *host, Scsi_Pointer *SCp, fasdmadir_t direction, fasdmatype_t min_dma);
void (*pseudo)(struct Scsi_Host *host, Scsi_Pointer *SCp, fasdmadir_t direction, int transfer);
void (*stop) (struct Scsi_Host *host, Scsi_Pointer *SCp);
} dma;
/* miscellaneous */
int internal_done; /* flag to indicate request done */
unsigned long magic_end;
} FAS216_Info;
/* Function: int fas216_init (struct Scsi_Host *instance)
* Purpose : initialise FAS/NCR/AMD SCSI structures.
* Params : instance - a driver-specific filled-out structure
* Returns : 0 on success
*/
extern int fas216_init (struct Scsi_Host *instance);
/* Function: int fas216_add (struct Scsi_Host *instance, struct device *dev)
* Purpose : initialise FAS/NCR/AMD SCSI ic.
* Params : instance - a driver-specific filled-out structure
* Returns : 0 on success
*/
extern int fas216_add (struct Scsi_Host *instance, struct device *dev);
/* Function: int fas216_queue_command (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
* Purpose : queue a command for adapter to process.
* Params : SCpnt - Command to queue
* done - done function to call once command is complete
* Returns : 0 - success, else error
*/
extern int fas216_queue_command (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
/* Function: int fas216_noqueue_command (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
* Purpose : queue a command for adapter to process, and process it to completion.
* Params : SCpnt - Command to queue
* done - done function to call once command is complete
* Returns : 0 - success, else error
*/
extern int fas216_noqueue_command (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
/* Function: irqreturn_t fas216_intr (FAS216_Info *info)
* Purpose : handle interrupts from the interface to progress a command
* Params : info - interface to service
*/
extern irqreturn_t fas216_intr (FAS216_Info *info);
extern void fas216_remove (struct Scsi_Host *instance);
/* Function: void fas216_release (struct Scsi_Host *instance)
* Purpose : release all resources and put everything to bed for FAS/NCR/AMD SCSI ic.
* Params : instance - a driver-specific filled-out structure
* Returns : 0 on success
*/
extern void fas216_release (struct Scsi_Host *instance);
extern int fas216_print_host(FAS216_Info *info, char *buffer);
extern int fas216_print_stats(FAS216_Info *info, char *buffer);
extern int fas216_print_devices(FAS216_Info *info, char *buffer);
/* Function: int fas216_eh_abort(Scsi_Cmnd *SCpnt)
* Purpose : abort this command
* Params : SCpnt - command to abort
* Returns : FAILED if unable to abort
*/
extern int fas216_eh_abort(Scsi_Cmnd *SCpnt);
/* Function: int fas216_eh_device_reset(Scsi_Cmnd *SCpnt)
* Purpose : Reset the device associated with this command
* Params : SCpnt - command specifing device to reset
* Returns : FAILED if unable to reset
*/
extern int fas216_eh_device_reset(Scsi_Cmnd *SCpnt);
/* Function: int fas216_eh_bus_reset(Scsi_Cmnd *SCpnt)
* Purpose : Reset the complete bus associated with this command
* Params : SCpnt - command specifing bus to reset
* Returns : FAILED if unable to reset
*/
extern int fas216_eh_bus_reset(Scsi_Cmnd *SCpnt);
/* Function: int fas216_eh_host_reset(Scsi_Cmnd *SCpnt)
* Purpose : Reset the host associated with this command
* Params : SCpnt - command specifing host to reset
* Returns : FAILED if unable to reset
*/
extern int fas216_eh_host_reset(Scsi_Cmnd *SCpnt);
#endif /* FAS216_H */

View File

@@ -0,0 +1,171 @@
/*
* linux/drivers/acorn/scsi/msgqueue.c
*
* Copyright (C) 1997-1998 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* message queue handling
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/stddef.h>
#include <linux/init.h>
#include "msgqueue.h"
/*
* Function: struct msgqueue_entry *mqe_alloc(MsgQueue_t *msgq)
* Purpose : Allocate a message queue entry
* Params : msgq - message queue to claim entry for
* Returns : message queue entry or NULL.
*/
static struct msgqueue_entry *mqe_alloc(MsgQueue_t *msgq)
{
struct msgqueue_entry *mq;
if ((mq = msgq->free) != NULL)
msgq->free = mq->next;
return mq;
}
/*
* Function: void mqe_free(MsgQueue_t *msgq, struct msgqueue_entry *mq)
* Purpose : free a message queue entry
* Params : msgq - message queue to free entry from
* mq - message queue entry to free
*/
static void mqe_free(MsgQueue_t *msgq, struct msgqueue_entry *mq)
{
if (mq) {
mq->next = msgq->free;
msgq->free = mq;
}
}
/*
* Function: void msgqueue_initialise(MsgQueue_t *msgq)
* Purpose : initialise a message queue
* Params : msgq - queue to initialise
*/
void msgqueue_initialise(MsgQueue_t *msgq)
{
int i;
msgq->qe = NULL;
msgq->free = &msgq->entries[0];
for (i = 0; i < NR_MESSAGES; i++)
msgq->entries[i].next = &msgq->entries[i + 1];
msgq->entries[NR_MESSAGES - 1].next = NULL;
}
/*
* Function: void msgqueue_free(MsgQueue_t *msgq)
* Purpose : free a queue
* Params : msgq - queue to free
*/
void msgqueue_free(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
*/
int msgqueue_msglength(MsgQueue_t *msgq)
{
struct msgqueue_entry *mq = msgq->qe;
int length = 0;
for (mq = msgq->qe; mq; mq = mq->next)
length += mq->msg.length;
return length;
}
/*
* Function: struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno)
* Purpose : return a message
* Params : msgq - queue to obtain message from
* : msgno - message number
* Returns : pointer to message string, or NULL
*/
struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno)
{
struct msgqueue_entry *mq;
for (mq = msgq->qe; mq && msgno; mq = mq->next, msgno--);
return mq ? &mq->msg : NULL;
}
/*
* 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
*/
int msgqueue_addmsg(MsgQueue_t *msgq, int length, ...)
{
struct msgqueue_entry *mq = mqe_alloc(msgq);
va_list ap;
if (mq) {
struct msgqueue_entry **mqp;
int i;
va_start(ap, length);
for (i = 0; i < length; i++)
mq->msg.msg[i] = va_arg(ap, unsigned int);
va_end(ap);
mq->msg.length = length;
mq->msg.fifo = 0;
mq->next = NULL;
mqp = &msgq->qe;
while (*mqp)
mqp = &(*mqp)->next;
*mqp = mq;
}
return mq != NULL;
}
/*
* Function: void msgqueue_flush(MsgQueue_t *msgq)
* Purpose : flush all messages from message queue
* Params : msgq - queue to flush
*/
void msgqueue_flush(MsgQueue_t *msgq)
{
struct msgqueue_entry *mq, *mqnext;
for (mq = msgq->qe; mq; mq = mqnext) {
mqnext = mq->next;
mqe_free(msgq, mq);
}
msgq->qe = NULL;
}
EXPORT_SYMBOL(msgqueue_initialise);
EXPORT_SYMBOL(msgqueue_free);
EXPORT_SYMBOL(msgqueue_msglength);
EXPORT_SYMBOL(msgqueue_getmsg);
EXPORT_SYMBOL(msgqueue_addmsg);
EXPORT_SYMBOL(msgqueue_flush);
MODULE_AUTHOR("Russell King");
MODULE_DESCRIPTION("SCSI message queue handling");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,82 @@
/*
* linux/drivers/acorn/scsi/msgqueue.h
*
* Copyright (C) 1997 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* message queue handling
*/
#ifndef MSGQUEUE_H
#define MSGQUEUE_H
struct message {
char msg[8];
int length;
int fifo;
};
struct msgqueue_entry {
struct message msg;
struct msgqueue_entry *next;
};
#define NR_MESSAGES 4
typedef struct {
struct msgqueue_entry *qe;
struct msgqueue_entry *free;
struct msgqueue_entry entries[NR_MESSAGES];
} MsgQueue_t;
/*
* Function: void msgqueue_initialise(MsgQueue_t *msgq)
* Purpose : initialise a message queue
* Params : msgq - queue to initialise
*/
extern void msgqueue_initialise(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);
/*
* 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);
/*
* Function: struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno)
* Purpose : return a message & its length
* Params : msgq - queue to obtain message from
* : msgno - message number
* Returns : pointer to message string, or NULL
*/
extern struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno);
/*
* 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, ...);
/*
* 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);
#endif

View File

@@ -0,0 +1,217 @@
/*
* Oak Generic NCR5380 driver
*
* Copyright 1995-2002, Russell King
*/
#include <linux/module.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/blkdev.h>
#include <linux/init.h>
#include <asm/ecard.h>
#include <asm/io.h>
#include <asm/system.h>
#include "../scsi.h"
#include <scsi/scsi_host.h>
#define AUTOSENSE
/*#define PSEUDO_DMA*/
#define OAKSCSI_PUBLIC_RELEASE 1
#define NCR5380_read(reg) oakscsi_read(_instance, reg)
#define NCR5380_write(reg, value) oakscsi_write(_instance, reg, value)
#define NCR5380_intr oakscsi_intr
#define NCR5380_queue_command oakscsi_queue_command
#define NCR5380_proc_info oakscsi_proc_info
#define NCR5380_implementation_fields int port, ctrl
#define NCR5380_local_declare() struct Scsi_Host *_instance
#define NCR5380_setup(instance) _instance = instance
#define BOARD_NORMAL 0
#define BOARD_NCR53C400 1
#include "../NCR5380.h"
#undef START_DMA_INITIATOR_RECEIVE_REG
#define START_DMA_INITIATOR_RECEIVE_REG (7 + 128)
const char * oakscsi_info (struct Scsi_Host *spnt)
{
return "";
}
#define STAT(p) inw(p + 144)
extern void inswb(int from, void *to, int len);
static inline int NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *addr,
int len)
{
int iobase = instance->io_port;
printk("writing %p len %d\n",addr, len);
if(!len) return -1;
while(1)
{
int status;
while(((status = STAT(iobase)) & 0x100)==0);
}
}
static inline int NCR5380_pread(struct Scsi_Host *instance, unsigned char *addr,
int len)
{
int iobase = instance->io_port;
printk("reading %p len %d\n", addr, len);
while(len > 0)
{
int status, timeout;
unsigned long b;
timeout = 0x01FFFFFF;
while(((status = STAT(iobase)) & 0x100)==0)
{
timeout--;
if(status & 0x200 || !timeout)
{
printk("status = %08X\n",status);
return 1;
}
}
if(len >= 128)
{
inswb(iobase + 136, addr, 128);
addr += 128;
len -= 128;
}
else
{
b = (unsigned long) inw(iobase + 136);
*addr ++ = b;
len -= 1;
if(len)
*addr ++ = b>>8;
len -= 1;
}
}
return 0;
}
#define oakscsi_read(instance,reg) (inb((instance)->io_port + (reg)))
#define oakscsi_write(instance,reg,val) (outb((val), (instance)->io_port + (reg)))
#undef STAT
#include "../NCR5380.c"
static Scsi_Host_Template oakscsi_template = {
.module = THIS_MODULE,
.proc_info = oakscsi_proc_info,
.name = "Oak 16-bit SCSI",
.info = oakscsi_info,
.queuecommand = oakscsi_queue_command,
.eh_abort_handler = NCR5380_abort,
.eh_device_reset_handler= NCR5380_device_reset,
.eh_bus_reset_handler = NCR5380_bus_reset,
.eh_host_reset_handler = NCR5380_host_reset,
.can_queue = 16,
.this_id = 7,
.sg_tablesize = SG_ALL,
.cmd_per_lun = 2,
.use_clustering = DISABLE_CLUSTERING,
.proc_name = "oakscsi",
};
static int __devinit
oakscsi_probe(struct expansion_card *ec, const struct ecard_id *id)
{
struct Scsi_Host *host;
int ret = -ENOMEM;
host = scsi_host_alloc(&oakscsi_template, sizeof(struct NCR5380_hostdata));
if (!host)
goto out;
host->io_port = ecard_address(ec, ECARD_MEMC, 0);
host->irq = IRQ_NONE;
host->n_io_port = 255;
ret = -EBUSY;
if (!request_region (host->io_port, host->n_io_port, "Oak SCSI"))
goto unreg;
NCR5380_init(host, 0);
printk("scsi%d: at port 0x%08lx irqs disabled",
host->host_no, host->io_port);
printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
host->can_queue, host->cmd_per_lun, OAKSCSI_PUBLIC_RELEASE);
printk("\nscsi%d:", host->host_no);
NCR5380_print_options(host);
printk("\n");
ret = scsi_add_host(host, &ec->dev);
if (ret)
goto out_release;
scsi_scan_host(host);
goto out;
out_release:
release_region(host->io_port, host->n_io_port);
unreg:
scsi_host_put(host);
out:
return ret;
}
static void __devexit oakscsi_remove(struct expansion_card *ec)
{
struct Scsi_Host *host = ecard_get_drvdata(ec);
ecard_set_drvdata(ec, NULL);
scsi_remove_host(host);
NCR5380_exit(host);
release_region(host->io_port, host->n_io_port);
scsi_host_put(host);
}
static const struct ecard_id oakscsi_cids[] = {
{ MANU_OAK, PROD_OAK_SCSI },
{ 0xffff, 0xffff }
};
static struct ecard_driver oakscsi_driver = {
.probe = oakscsi_probe,
.remove = __devexit_p(oakscsi_remove),
.id_table = oakscsi_cids,
.drv = {
.name = "oakscsi",
},
};
static int __init oakscsi_init(void)
{
return ecard_register_driver(&oakscsi_driver);
}
static void __exit oakscsi_exit(void)
{
ecard_remove_driver(&oakscsi_driver);
}
module_init(oakscsi_init);
module_exit(oakscsi_exit);
MODULE_AUTHOR("Russell King");
MODULE_DESCRIPTION("Oak SCSI driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,472 @@
/*
* linux/drivers/acorn/scsi/powertec.c
*
* Copyright (C) 1997-2003 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/blkdev.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/dma-mapping.h>
#include <asm/dma.h>
#include <asm/ecard.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/pgtable.h>
#include "../scsi.h"
#include <scsi/scsi_host.h>
#include "fas216.h"
#include "scsi.h"
#include <scsi/scsicam.h>
#define POWERTEC_FAS216_OFFSET 0x3000
#define POWERTEC_FAS216_SHIFT 6
#define POWERTEC_INTR_STATUS 0x2000
#define POWERTEC_INTR_BIT 0x80
#define POWERTEC_RESET_CONTROL 0x1018
#define POWERTEC_RESET_BIT 1
#define POWERTEC_TERM_CONTROL 0x2018
#define POWERTEC_TERM_ENABLE 1
#define POWERTEC_INTR_CONTROL 0x101c
#define POWERTEC_INTR_ENABLE 1
#define POWERTEC_INTR_DISABLE 0
#define VERSION "1.10 (19/01/2003 2.5.59)"
/*
* Use term=0,1,0,0,0 to turn terminators on/off.
* One entry per slot.
*/
static int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 };
#define NR_SG 256
struct powertec_info {
FAS216_Info info;
struct expansion_card *ec;
void *term_port;
unsigned int term_ctl;
struct scatterlist sg[NR_SG];
};
/* Prototype: void powertecscsi_irqenable(ec, irqnr)
* Purpose : Enable interrupts on Powertec SCSI card
* Params : ec - expansion card structure
* : irqnr - interrupt number
*/
static void
powertecscsi_irqenable(struct expansion_card *ec, int irqnr)
{
writeb(POWERTEC_INTR_ENABLE, ec->irq_data);
}
/* Prototype: void powertecscsi_irqdisable(ec, irqnr)
* Purpose : Disable interrupts on Powertec SCSI card
* Params : ec - expansion card structure
* : irqnr - interrupt number
*/
static void
powertecscsi_irqdisable(struct expansion_card *ec, int irqnr)
{
writeb(POWERTEC_INTR_DISABLE, ec->irq_data);
}
static const expansioncard_ops_t powertecscsi_ops = {
.irqenable = powertecscsi_irqenable,
.irqdisable = powertecscsi_irqdisable,
};
/* Prototype: void powertecscsi_terminator_ctl(host, on_off)
* Purpose : Turn the Powertec SCSI terminators on or off
* Params : host - card to turn on/off
* : on_off - !0 to turn on, 0 to turn off
*/
static void
powertecscsi_terminator_ctl(struct Scsi_Host *host, int on_off)
{
struct powertec_info *info = (struct powertec_info *)host->hostdata;
info->term_ctl = on_off ? POWERTEC_TERM_ENABLE : 0;
writeb(info->term_ctl, info->term_port);
}
/* Prototype: void powertecscsi_intr(irq, *dev_id, *regs)
* Purpose : handle interrupts from Powertec SCSI card
* Params : irq - interrupt number
* dev_id - user-defined (Scsi_Host structure)
* regs - processor registers at interrupt
*/
static irqreturn_t
powertecscsi_intr(int irq, void *dev_id, struct pt_regs *regs)
{
struct powertec_info *info = dev_id;
return fas216_intr(&info->info);
}
/* Prototype: fasdmatype_t powertecscsi_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 : type of transfer to be performed
*/
static fasdmatype_t
powertecscsi_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp,
fasdmadir_t direction, fasdmatype_t min_type)
{
struct powertec_info *info = (struct powertec_info *)host->hostdata;
struct device *dev = scsi_get_device(host);
int dmach = host->dma_channel;
if (info->info.ifcfg.capabilities & FASCAP_DMA &&
min_type == fasdma_real_all) {
int bufs, map_dir, dma_dir;
bufs = copy_SCp_to_sg(&info->sg[0], SCp, NR_SG);
if (direction == DMA_OUT)
map_dir = DMA_TO_DEVICE,
dma_dir = DMA_MODE_WRITE;
else
map_dir = DMA_FROM_DEVICE,
dma_dir = DMA_MODE_READ;
dma_map_sg(dev, info->sg, bufs + 1, map_dir);
disable_dma(dmach);
set_dma_sg(dmach, info->sg, bufs + 1);
set_dma_mode(dmach, dma_dir);
enable_dma(dmach);
return fasdma_real_all;
}
/*
* If we're not doing DMA,
* we'll do slow PIO
*/
return fasdma_pio;
}
/* Prototype: int powertecscsi_dma_stop(host, SCpnt)
* Purpose : stops DMA/PIO
* Params : host - host
* SCpnt - command
*/
static void
powertecscsi_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp)
{
if (host->dma_channel != NO_DMA)
disable_dma(host->dma_channel);
}
/* Prototype: const char *powertecscsi_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 *powertecscsi_info(struct Scsi_Host *host)
{
struct powertec_info *info = (struct powertec_info *)host->hostdata;
static char string[150];
sprintf(string, "%s (%s) in slot %d v%s terminators o%s",
host->hostt->name, info->info.scsi.type, info->ec->slot_no,
VERSION, info->term_ctl ? "n" : "ff");
return string;
}
/* Prototype: int powertecscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
* Purpose : Set a driver specific function
* Params : host - host to setup
* : buffer - buffer containing string describing operation
* : length - length of string
* Returns : -EINVAL, or 0
*/
static int
powertecscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
{
int ret = length;
if (length >= 12 && strncmp(buffer, "POWERTECSCSI", 12) == 0) {
buffer += 12;
length -= 12;
if (length >= 5 && strncmp(buffer, "term=", 5) == 0) {
if (buffer[5] == '1')
powertecscsi_terminator_ctl(host, 1);
else if (buffer[5] == '0')
powertecscsi_terminator_ctl(host, 0);
else
ret = -EINVAL;
} else
ret = -EINVAL;
} else
ret = -EINVAL;
return ret;
}
/* Prototype: int powertecscsi_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
* inout - 0 for reading, 1 for writing.
* Returns : length of data written to buffer.
*/
int powertecscsi_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset,
int length, int inout)
{
struct powertec_info *info;
char *p = buffer;
int pos;
if (inout == 1)
return powertecscsi_set_proc_info(host, buffer, length);
info = (struct powertec_info *)host->hostdata;
p += sprintf(p, "PowerTec SCSI driver v%s\n", VERSION);
p += fas216_print_host(&info->info, p);
p += sprintf(p, "Term : o%s\n",
info->term_ctl ? "n" : "ff");
p += fas216_print_stats(&info->info, p);
p += fas216_print_devices(&info->info, p);
*start = buffer + offset;
pos = p - buffer - offset;
if (pos > length)
pos = length;
return pos;
}
static ssize_t powertecscsi_show_term(struct device *dev, char *buf)
{
struct expansion_card *ec = ECARD_DEV(dev);
struct Scsi_Host *host = ecard_get_drvdata(ec);
struct powertec_info *info = (struct powertec_info *)host->hostdata;
return sprintf(buf, "%d\n", info->term_ctl ? 1 : 0);
}
static ssize_t
powertecscsi_store_term(struct device *dev, const char *buf, size_t len)
{
struct expansion_card *ec = ECARD_DEV(dev);
struct Scsi_Host *host = ecard_get_drvdata(ec);
if (len > 1)
powertecscsi_terminator_ctl(host, buf[0] != '0');
return len;
}
static DEVICE_ATTR(bus_term, S_IRUGO | S_IWUSR,
powertecscsi_show_term, powertecscsi_store_term);
static Scsi_Host_Template powertecscsi_template = {
.module = THIS_MODULE,
.proc_info = powertecscsi_proc_info,
.name = "PowerTec SCSI",
.info = powertecscsi_info,
.queuecommand = fas216_queue_command,
.eh_host_reset_handler = fas216_eh_host_reset,
.eh_bus_reset_handler = fas216_eh_bus_reset,
.eh_device_reset_handler = fas216_eh_device_reset,
.eh_abort_handler = fas216_eh_abort,
.can_queue = 8,
.this_id = 7,
.sg_tablesize = SG_ALL,
.cmd_per_lun = 2,
.use_clustering = ENABLE_CLUSTERING,
.proc_name = "powertec",
};
static int __devinit
powertecscsi_probe(struct expansion_card *ec, const struct ecard_id *id)
{
struct Scsi_Host *host;
struct powertec_info *info;
unsigned long resbase, reslen;
unsigned char *base;
int ret;
ret = ecard_request_resources(ec);
if (ret)
goto out;
resbase = ecard_resource_start(ec, ECARD_RES_IOCFAST);
reslen = ecard_resource_len(ec, ECARD_RES_IOCFAST);
base = ioremap(resbase, reslen);
if (!base) {
ret = -ENOMEM;
goto out_region;
}
host = scsi_host_alloc(&powertecscsi_template,
sizeof (struct powertec_info));
if (!host) {
ret = -ENOMEM;
goto out_unmap;
}
host->base = (unsigned long)base;
host->irq = ec->irq;
host->dma_channel = ec->dma;
ec->irqaddr = base + POWERTEC_INTR_STATUS;
ec->irqmask = POWERTEC_INTR_BIT;
ec->irq_data = base + POWERTEC_INTR_CONTROL;
ec->ops = &powertecscsi_ops;
ecard_set_drvdata(ec, host);
info = (struct powertec_info *)host->hostdata;
info->term_port = base + POWERTEC_TERM_CONTROL;
powertecscsi_terminator_ctl(host, term[ec->slot_no]);
device_create_file(&ec->dev, &dev_attr_bus_term);
info->info.scsi.io_base = base + POWERTEC_FAS216_OFFSET;
info->info.scsi.io_shift = POWERTEC_FAS216_SHIFT;
info->info.scsi.irq = host->irq;
info->info.ifcfg.clockrate = 40; /* MHz */
info->info.ifcfg.select_timeout = 255;
info->info.ifcfg.asyncperiod = 200; /* ns */
info->info.ifcfg.sync_max_depth = 7;
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.ifcfg.capabilities = 0;
info->info.dma.setup = powertecscsi_dma_setup;
info->info.dma.pseudo = NULL;
info->info.dma.stop = powertecscsi_dma_stop;
ret = fas216_init(host);
if (ret)
goto out_free;
ret = request_irq(host->irq, powertecscsi_intr,
SA_INTERRUPT, "powertec", info);
if (ret) {
printk("scsi%d: IRQ%d not free: %d\n",
host->host_no, host->irq, ret);
goto out_release;
}
if (host->dma_channel != NO_DMA) {
if (request_dma(host->dma_channel, "powertec")) {
printk("scsi%d: DMA%d not free, using PIO\n",
host->host_no, host->dma_channel);
host->dma_channel = NO_DMA;
} else {
set_dma_speed(host->dma_channel, 180);
info->info.ifcfg.capabilities |= FASCAP_DMA;
}
}
ret = fas216_add(host, &ec->dev);
if (ret == 0)
goto out;
if (host->dma_channel != NO_DMA)
free_dma(host->dma_channel);
free_irq(host->irq, host);
out_release:
fas216_release(host);
out_free:
device_remove_file(&ec->dev, &dev_attr_bus_term);
scsi_host_put(host);
out_unmap:
iounmap(base);
out_region:
ecard_release_resources(ec);
out:
return ret;
}
static void __devexit powertecscsi_remove(struct expansion_card *ec)
{
struct Scsi_Host *host = ecard_get_drvdata(ec);
struct powertecscsi_info *info = (struct powertecscsi_info *)host->hostdata;
ecard_set_drvdata(ec, NULL);
fas216_remove(host);
device_remove_file(&ec->dev, &dev_attr_bus_term);
if (host->dma_channel != NO_DMA)
free_dma(host->dma_channel);
free_irq(host->irq, info);
iounmap((void *)host->base);
fas216_release(host);
scsi_host_put(host);
ecard_release_resources(ec);
}
static const struct ecard_id powertecscsi_cids[] = {
{ MANU_ALSYSTEMS, PROD_ALSYS_SCSIATAPI },
{ 0xffff, 0xffff },
};
static struct ecard_driver powertecscsi_driver = {
.probe = powertecscsi_probe,
.remove = __devexit_p(powertecscsi_remove),
.id_table = powertecscsi_cids,
.drv = {
.name = "powertecscsi",
},
};
static int __init powertecscsi_init(void)
{
return ecard_register_driver(&powertecscsi_driver);
}
static void __exit powertecscsi_exit(void)
{
ecard_remove_driver(&powertecscsi_driver);
}
module_init(powertecscsi_init);
module_exit(powertecscsi_exit);
MODULE_AUTHOR("Russell King");
MODULE_DESCRIPTION("Powertec SCSI driver");
MODULE_PARM(term, "1-8i");
MODULE_PARM_DESC(term, "SCSI bus termination");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,319 @@
/*
* linux/drivers/acorn/scsi/queue.c: queue handling primitives
*
* Copyright (C) 1997-2000 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Changelog:
* 15-Sep-1997 RMK Created.
* 11-Oct-1997 RMK Corrected problem with queue_remove_exclude
* not updating internal linked list properly
* (was causing commands to go missing).
* 30-Aug-2000 RMK Use Linux list handling and spinlocks
*/
#include <linux/module.h>
#include <linux/blkdev.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/init.h>
#include "../scsi.h"
#define DEBUG
typedef struct queue_entry {
struct list_head list;
Scsi_Cmnd *SCpnt;
#ifdef DEBUG
unsigned long magic;
#endif
} QE_t;
#ifdef DEBUG
#define QUEUE_MAGIC_FREE 0xf7e1c9a3
#define QUEUE_MAGIC_USED 0xf7e1cc33
#define SET_MAGIC(q,m) ((q)->magic = (m))
#define BAD_MAGIC(q,m) ((q)->magic != (m))
#else
#define SET_MAGIC(q,m) do { } while (0)
#define BAD_MAGIC(q,m) (0)
#endif
#include "queue.h"
#define NR_QE 32
/*
* Function: void queue_initialise (Queue_t *queue)
* Purpose : initialise a queue
* Params : queue - queue to initialise
*/
int queue_initialise (Queue_t *queue)
{
unsigned int nqueues = NR_QE;
QE_t *q;
spin_lock_init(&queue->queue_lock);
INIT_LIST_HEAD(&queue->head);
INIT_LIST_HEAD(&queue->free);
/*
* If life was easier, then SCpnt would have a
* host-available list head, and we wouldn't
* need to keep free lists or allocate this
* memory.
*/
queue->alloc = q = kmalloc(sizeof(QE_t) * nqueues, GFP_KERNEL);
if (q) {
for (; nqueues; q++, nqueues--) {
SET_MAGIC(q, QUEUE_MAGIC_FREE);
q->SCpnt = NULL;
list_add(&q->list, &queue->free);
}
}
return queue->alloc != NULL;
}
/*
* Function: void queue_free (Queue_t *queue)
* Purpose : free a queue
* Params : queue - queue to free
*/
void queue_free (Queue_t *queue)
{
if (!list_empty(&queue->head))
printk(KERN_WARNING "freeing non-empty queue %p\n", queue);
if (queue->alloc)
kfree(queue->alloc);
}
/*
* Function: int queue_add_cmd(Queue_t *queue, Scsi_Cmnd *SCpnt, int head)
* Purpose : Add a new command onto a queue, adding REQUEST_SENSE to head.
* Params : queue - destination queue
* SCpnt - command to add
* head - add command to head of queue
* Returns : 0 on error, !0 on success
*/
int __queue_add(Queue_t *queue, Scsi_Cmnd *SCpnt, int head)
{
unsigned long flags;
struct list_head *l;
QE_t *q;
int ret = 0;
spin_lock_irqsave(&queue->queue_lock, flags);
if (list_empty(&queue->free))
goto empty;
l = queue->free.next;
list_del(l);
q = list_entry(l, QE_t, list);
if (BAD_MAGIC(q, QUEUE_MAGIC_FREE))
BUG();
SET_MAGIC(q, QUEUE_MAGIC_USED);
q->SCpnt = SCpnt;
if (head)
list_add(l, &queue->head);
else
list_add_tail(l, &queue->head);
ret = 1;
empty:
spin_unlock_irqrestore(&queue->queue_lock, flags);
return ret;
}
static Scsi_Cmnd *__queue_remove(Queue_t *queue, struct list_head *ent)
{
QE_t *q;
/*
* Move the entry from the "used" list onto the "free" list
*/
list_del(ent);
q = list_entry(ent, QE_t, list);
if (BAD_MAGIC(q, QUEUE_MAGIC_USED))
BUG();
SET_MAGIC(q, QUEUE_MAGIC_FREE);
list_add(ent, &queue->free);
return q->SCpnt;
}
/*
* Function: Scsi_Cmnd *queue_remove_exclude (queue, exclude)
* Purpose : remove a SCSI command from a queue
* Params : queue - queue to remove command from
* exclude - bit array of target&lun which is busy
* Returns : Scsi_Cmnd if successful (and a reference), or NULL if no command available
*/
Scsi_Cmnd *queue_remove_exclude(Queue_t *queue, unsigned long *exclude)
{
unsigned long flags;
struct list_head *l;
Scsi_Cmnd *SCpnt = NULL;
spin_lock_irqsave(&queue->queue_lock, flags);
list_for_each(l, &queue->head) {
QE_t *q = list_entry(l, QE_t, list);
if (!test_bit(q->SCpnt->device->id * 8 + q->SCpnt->device->lun, exclude)) {
SCpnt = __queue_remove(queue, l);
break;
}
}
spin_unlock_irqrestore(&queue->queue_lock, flags);
return SCpnt;
}
/*
* Function: Scsi_Cmnd *queue_remove (queue)
* Purpose : removes first SCSI command from a queue
* Params : queue - queue to remove command from
* Returns : Scsi_Cmnd if successful (and a reference), or NULL if no command available
*/
Scsi_Cmnd *queue_remove(Queue_t *queue)
{
unsigned long flags;
Scsi_Cmnd *SCpnt = NULL;
spin_lock_irqsave(&queue->queue_lock, flags);
if (!list_empty(&queue->head))
SCpnt = __queue_remove(queue, queue->head.next);
spin_unlock_irqrestore(&queue->queue_lock, flags);
return SCpnt;
}
/*
* Function: Scsi_Cmnd *queue_remove_tgtluntag (queue, target, lun, tag)
* Purpose : remove a SCSI command from the queue for a specified target/lun/tag
* Params : queue - queue to remove command from
* target - target that we want
* lun - lun on device
* tag - tag on device
* Returns : Scsi_Cmnd if successful, or NULL if no command satisfies requirements
*/
Scsi_Cmnd *queue_remove_tgtluntag (Queue_t *queue, int target, int lun, int tag)
{
unsigned long flags;
struct list_head *l;
Scsi_Cmnd *SCpnt = NULL;
spin_lock_irqsave(&queue->queue_lock, flags);
list_for_each(l, &queue->head) {
QE_t *q = list_entry(l, QE_t, list);
if (q->SCpnt->device->id == target && q->SCpnt->device->lun == lun &&
q->SCpnt->tag == tag) {
SCpnt = __queue_remove(queue, l);
break;
}
}
spin_unlock_irqrestore(&queue->queue_lock, flags);
return SCpnt;
}
/*
* Function: queue_remove_all_target(queue, target)
* Purpose : remove all SCSI commands from the queue for a specified target
* Params : queue - queue to remove command from
* target - target device id
* Returns : nothing
*/
void queue_remove_all_target(Queue_t *queue, int target)
{
unsigned long flags;
struct list_head *l;
spin_lock_irqsave(&queue->queue_lock, flags);
list_for_each(l, &queue->head) {
QE_t *q = list_entry(l, QE_t, list);
if (q->SCpnt->device->id == target)
__queue_remove(queue, l);
}
spin_unlock_irqrestore(&queue->queue_lock, flags);
}
/*
* Function: int queue_probetgtlun (queue, target, lun)
* Purpose : check to see if we have a command in the queue for the specified
* target/lun.
* Params : queue - queue to look in
* target - target we want to probe
* lun - lun on target
* Returns : 0 if not found, != 0 if found
*/
int queue_probetgtlun (Queue_t *queue, int target, int lun)
{
unsigned long flags;
struct list_head *l;
int found = 0;
spin_lock_irqsave(&queue->queue_lock, flags);
list_for_each(l, &queue->head) {
QE_t *q = list_entry(l, QE_t, list);
if (q->SCpnt->device->id == target && q->SCpnt->device->lun == lun) {
found = 1;
break;
}
}
spin_unlock_irqrestore(&queue->queue_lock, flags);
return found;
}
/*
* Function: int queue_remove_cmd(Queue_t *queue, Scsi_Cmnd *SCpnt)
* Purpose : remove a specific command from the queues
* Params : queue - queue to look in
* SCpnt - command to find
* Returns : 0 if not found
*/
int queue_remove_cmd(Queue_t *queue, Scsi_Cmnd *SCpnt)
{
unsigned long flags;
struct list_head *l;
int found = 0;
spin_lock_irqsave(&queue->queue_lock, flags);
list_for_each(l, &queue->head) {
QE_t *q = list_entry(l, QE_t, list);
if (q->SCpnt == SCpnt) {
__queue_remove(queue, l);
found = 1;
break;
}
}
spin_unlock_irqrestore(&queue->queue_lock, flags);
return found;
}
EXPORT_SYMBOL(queue_initialise);
EXPORT_SYMBOL(queue_free);
EXPORT_SYMBOL(__queue_add);
EXPORT_SYMBOL(queue_remove);
EXPORT_SYMBOL(queue_remove_exclude);
EXPORT_SYMBOL(queue_remove_tgtluntag);
EXPORT_SYMBOL(queue_remove_cmd);
EXPORT_SYMBOL(queue_remove_all_target);
EXPORT_SYMBOL(queue_probetgtlun);
MODULE_AUTHOR("Russell King");
MODULE_DESCRIPTION("SCSI command queueing");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,105 @@
/*
* linux/drivers/acorn/scsi/queue.h: queue handling
*
* Copyright (C) 1997 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef QUEUE_H
#define QUEUE_H
typedef struct {
struct list_head head;
struct list_head free;
spinlock_t queue_lock;
void *alloc; /* start of allocated mem */
} Queue_t;
/*
* Function: void queue_initialise (Queue_t *queue)
* Purpose : initialise a queue
* Params : queue - queue to initialise
*/
extern int queue_initialise (Queue_t *queue);
/*
* Function: void queue_free (Queue_t *queue)
* Purpose : free a queue
* Params : queue - queue to free
*/
extern void queue_free (Queue_t *queue);
/*
* Function: Scsi_Cmnd *queue_remove (queue)
* Purpose : removes first SCSI command from a queue
* Params : queue - queue to remove command from
* Returns : Scsi_Cmnd if successful (and a reference), or NULL if no command available
*/
extern Scsi_Cmnd *queue_remove (Queue_t *queue);
/*
* Function: Scsi_Cmnd *queue_remove_exclude_ref (queue, exclude)
* Purpose : remove a SCSI command from a queue
* Params : queue - queue to remove command from
* exclude - array of busy LUNs
* Returns : Scsi_Cmnd if successful (and a reference), or NULL if no command available
*/
extern Scsi_Cmnd *queue_remove_exclude (Queue_t *queue, unsigned long *exclude);
#define queue_add_cmd_ordered(queue,SCpnt) \
__queue_add(queue,SCpnt,(SCpnt)->cmnd[0] == REQUEST_SENSE)
#define queue_add_cmd_tail(queue,SCpnt) \
__queue_add(queue,SCpnt,0)
/*
* Function: int __queue_add(Queue_t *queue, Scsi_Cmnd *SCpnt, int head)
* Purpose : Add a new command onto a queue
* Params : queue - destination queue
* SCpnt - command to add
* head - add command to head of queue
* Returns : 0 on error, !0 on success
*/
extern int __queue_add(Queue_t *queue, Scsi_Cmnd *SCpnt, int head);
/*
* Function: Scsi_Cmnd *queue_remove_tgtluntag (queue, target, lun, tag)
* Purpose : remove a SCSI command from the queue for a specified target/lun/tag
* Params : queue - queue to remove command from
* target - target that we want
* lun - lun on device
* tag - tag on device
* Returns : Scsi_Cmnd if successful, or NULL if no command satisfies requirements
*/
extern Scsi_Cmnd *queue_remove_tgtluntag (Queue_t *queue, int target, int lun, int tag);
/*
* Function: queue_remove_all_target(queue, target)
* Purpose : remove all SCSI commands from the queue for a specified target
* Params : queue - queue to remove command from
* target - target device id
* Returns : nothing
*/
extern void queue_remove_all_target(Queue_t *queue, int target);
/*
* Function: int queue_probetgtlun (queue, target, lun)
* Purpose : check to see if we have a command in the queue for the specified
* target/lun.
* Params : queue - queue to look in
* target - target we want to probe
* lun - lun on target
* Returns : 0 if not found, != 0 if found
*/
extern int queue_probetgtlun (Queue_t *queue, int target, int lun);
/*
* Function: int queue_remove_cmd (Queue_t *queue, Scsi_Cmnd *SCpnt)
* Purpose : remove a specific command from the queues
* Params : queue - queue to look in
* SCpnt - command to find
* Returns : 0 if not found
*/
int queue_remove_cmd(Queue_t *queue, Scsi_Cmnd *SCpnt);
#endif /* QUEUE_H */

View File

@@ -0,0 +1,115 @@
/*
* linux/drivers/acorn/scsi/scsi.h
*
* Copyright (C) 2002 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Commonly used scsi driver functions.
*/
#define BELT_AND_BRACES
/*
* The scatter-gather list handling. This contains all
* the yucky stuff that needs to be fixed properly.
*/
static inline int copy_SCp_to_sg(struct scatterlist *sg, Scsi_Pointer *SCp, int max)
{
int bufs = SCp->buffers_residual;
BUG_ON(bufs + 1 > max);
sg->page = virt_to_page(SCp->ptr);
sg->offset = offset_in_page(SCp->ptr);
sg->length = SCp->this_residual;
if (bufs)
memcpy(sg + 1, SCp->buffer + 1,
sizeof(struct scatterlist) * bufs);
return bufs + 1;
}
static inline int next_SCp(Scsi_Pointer *SCp)
{
int ret = SCp->buffers_residual;
if (ret) {
SCp->buffer++;
SCp->buffers_residual--;
SCp->ptr = (char *)
(page_address(SCp->buffer->page) +
SCp->buffer->offset);
SCp->this_residual = SCp->buffer->length;
} else {
SCp->ptr = NULL;
SCp->this_residual = 0;
}
return ret;
}
static inline unsigned char get_next_SCp_byte(Scsi_Pointer *SCp)
{
char c = *SCp->ptr;
SCp->ptr += 1;
SCp->this_residual -= 1;
return c;
}
static inline void put_next_SCp_byte(Scsi_Pointer *SCp, unsigned char c)
{
*SCp->ptr = c;
SCp->ptr += 1;
SCp->this_residual -= 1;
}
static inline void init_SCp(Scsi_Cmnd *SCpnt)
{
memset(&SCpnt->SCp, 0, sizeof(struct scsi_pointer));
if (SCpnt->use_sg) {
unsigned long len = 0;
int buf;
SCpnt->SCp.buffer = (struct scatterlist *) SCpnt->buffer;
SCpnt->SCp.buffers_residual = SCpnt->use_sg - 1;
SCpnt->SCp.ptr = (char *)
(page_address(SCpnt->SCp.buffer->page) +
SCpnt->SCp.buffer->offset);
SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length;
#ifdef BELT_AND_BRACES
/*
* Calculate correct buffer length. Some commands
* come in with the wrong request_bufflen.
*/
for (buf = 0; buf <= SCpnt->SCp.buffers_residual; buf++)
len += SCpnt->SCp.buffer[buf].length;
if (SCpnt->request_bufflen != len)
printk(KERN_WARNING "scsi%d.%c: bad request buffer "
"length %d, should be %ld\n", SCpnt->device->host->host_no,
'0' + SCpnt->device->id, SCpnt->request_bufflen, len);
SCpnt->request_bufflen = len;
#endif
} else {
SCpnt->SCp.ptr = (unsigned char *)SCpnt->request_buffer;
SCpnt->SCp.this_residual = SCpnt->request_bufflen;
}
/*
* If the upper SCSI layers pass a buffer, but zero length,
* we aren't interested in the buffer pointer.
*/
if (SCpnt->SCp.this_residual == 0 && SCpnt->SCp.ptr) {
#if 0 //def BELT_AND_BRACES
printk(KERN_WARNING "scsi%d.%c: zero length buffer passed for "
"command ", SCpnt->host->host_no, '0' + SCpnt->target);
print_command(SCpnt->cmnd);
#endif
SCpnt->SCp.ptr = NULL;
}
}