Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

84
drivers/scsi/arm/Kconfig Normal file
View file

@ -0,0 +1,84 @@
#
# SCSI driver configuration for Acorn
#
config SCSI_ACORNSCSI_3
tristate "Acorn SCSI card (aka30) support"
depends on ARCH_ACORN && SCSI
select SCSI_SPI_ATTRS
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
config SCSI_CUMANA_1
tristate "CumanaSCSI I support"
depends on ARCH_ACORN && SCSI
select SCSI_SPI_ATTRS
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_OAK1
tristate "Oak SCSI support"
depends on ARCH_ACORN && SCSI
select SCSI_SPI_ATTRS
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.

13
drivers/scsi/arm/Makefile Normal file
View file

@ -0,0 +1,13 @@
#
# 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_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,138 @@
/*
* 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 <mach/hardware.h>
#if defined(__APCS_32__)
#define LOADREGS(t,r,l...) ldm##t r, l
#elif defined(__APCS_26__)
#define LOADREGS(t,r,l...) ldm##t r, l##^
#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})

3014
drivers/scsi/arm/acornscsi.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,353 @@
/*
* 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 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 negotiate synchronous transfers*/
SYNC_NEGOCIATE, /* start negotiation */
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 */
struct scsi_cmnd *SCpnt; /* currently processing command */
struct scsi_cmnd *origSCpnt; /* original connecting command */
void __iomem *base; /* memc base address */
void __iomem *fast; /* fast ioc base address */
/* driver information */
struct {
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;
struct 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 negotiation 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 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 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 */

358
drivers/scsi/arm/arxescsi.c Normal file
View file

@ -0,0 +1,358 @@
/*
* linux/drivers/scsi/arm/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/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/ecard.h>
#include "../scsi.h"
#include <scsi/scsi_host.h>
#include "fas216.h"
struct arxescsi_info {
FAS216_Info info;
struct expansion_card *ec;
void __iomem *base;
};
#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, struct 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, void __iomem *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, struct scsi_pointer *SCp,
fasdmadir_t direction, int transfer)
{
struct arxescsi_info *info = (struct arxescsi_info *)host->hostdata;
unsigned int length, error = 0;
void __iomem *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, struct 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;
}
static int
arxescsi_show_info(struct seq_file *m, struct Scsi_Host *host)
{
struct arxescsi_info *info;
info = (struct arxescsi_info *)host->hostdata;
seq_printf(m, "ARXE 16-bit SCSI driver v%s\n", VERSION);
fas216_print_host(&info->info, m);
fas216_print_stats(&info->info, m);
fas216_print_devices(&info->info, m);
return 0;
}
static struct scsi_host_template arxescsi_template = {
.show_info = arxescsi_show_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 arxescsi_probe(struct expansion_card *ec, const struct ecard_id *id)
{
struct Scsi_Host *host;
struct arxescsi_info *info;
void __iomem *base;
int ret;
ret = ecard_request_resources(ec);
if (ret)
goto out;
base = ecardm_iomap(ec, ECARD_RES_MEMC, 0, 0);
if (!base) {
ret = -ENOMEM;
goto out_region;
}
host = scsi_host_alloc(&arxescsi_template, sizeof(struct arxescsi_info));
if (!host) {
ret = -ENOMEM;
goto out_region;
}
info = (struct arxescsi_info *)host->hostdata;
info->ec = ec;
info->base = base;
info->info.scsi.io_base = base + 0x2000;
info->info.scsi.irq = 0;
info->info.scsi.dma = NO_DMA;
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_region:
ecard_release_resources(ec);
out:
return ret;
}
static void arxescsi_remove(struct expansion_card *ec)
{
struct Scsi_Host *host = ecard_get_drvdata(ec);
ecard_set_drvdata(ec, NULL);
fas216_remove(host);
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 = 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");

340
drivers/scsi/arm/cumana_1.c Normal file
View file

@ -0,0 +1,340 @@
/*
* Generic Generic NCR5380 driver
*
* Copyright 1995-2002, Russell King
*/
#include <linux/module.h>
#include <linux/signal.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 "../scsi.h"
#include <scsi/scsi_host.h>
#include <scsi/scsicam.h>
#define AUTOSENSE
#define PSEUDO_DMA
#define CUMANASCSI_PUBLIC_RELEASE 1
#define priv(host) ((struct NCR5380_hostdata *)(host)->hostdata)
#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_implementation_fields \
unsigned ctrl; \
void __iomem *base; \
void __iomem *dma
#include "../NCR5380.h"
void cumanascsi_setup(char *str, int *ints)
{
}
const char *cumanascsi_info(struct Scsi_Host *spnt)
{
return "";
}
#define CTRL 0x16fc
#define STAT 0x2004
#define L(v) (((v)<<16)|((v) & 0x0000ffff))
#define H(v) (((v)>>16)|((v) & 0xffff0000))
static inline int
NCR5380_pwrite(struct Scsi_Host *host, unsigned char *addr, int len)
{
unsigned long *laddr;
void __iomem *dma = priv(host)->dma + 0x2000;
if(!len) return 0;
writeb(0x02, priv(host)->base + CTRL);
laddr = (unsigned long *)addr;
while(len >= 32)
{
unsigned int status;
unsigned long v;
status = readb(priv(host)->base + STAT);
if(status & 0x80)
goto end;
if(!(status & 0x40))
continue;
v=*laddr++; writew(L(v), dma); writew(H(v), dma);
v=*laddr++; writew(L(v), dma); writew(H(v), dma);
v=*laddr++; writew(L(v), dma); writew(H(v), dma);
v=*laddr++; writew(L(v), dma); writew(H(v), dma);
v=*laddr++; writew(L(v), dma); writew(H(v), dma);
v=*laddr++; writew(L(v), dma); writew(H(v), dma);
v=*laddr++; writew(L(v), dma); writew(H(v), dma);
v=*laddr++; writew(L(v), dma); writew(H(v), dma);
len -= 32;
if(len == 0)
break;
}
addr = (unsigned char *)laddr;
writeb(0x12, priv(host)->base + CTRL);
while(len > 0)
{
unsigned int status;
status = readb(priv(host)->base + STAT);
if(status & 0x80)
goto end;
if(status & 0x40)
{
writeb(*addr++, dma);
if(--len == 0)
break;
}
status = readb(priv(host)->base + STAT);
if(status & 0x80)
goto end;
if(status & 0x40)
{
writeb(*addr++, dma);
if(--len == 0)
break;
}
}
end:
writeb(priv(host)->ctrl | 0x40, priv(host)->base + CTRL);
return len;
}
static inline int
NCR5380_pread(struct Scsi_Host *host, unsigned char *addr, int len)
{
unsigned long *laddr;
void __iomem *dma = priv(host)->dma + 0x2000;
if(!len) return 0;
writeb(0x00, priv(host)->base + CTRL);
laddr = (unsigned long *)addr;
while(len >= 32)
{
unsigned int status;
status = readb(priv(host)->base + STAT);
if(status & 0x80)
goto end;
if(!(status & 0x40))
continue;
*laddr++ = readw(dma) | (readw(dma) << 16);
*laddr++ = readw(dma) | (readw(dma) << 16);
*laddr++ = readw(dma) | (readw(dma) << 16);
*laddr++ = readw(dma) | (readw(dma) << 16);
*laddr++ = readw(dma) | (readw(dma) << 16);
*laddr++ = readw(dma) | (readw(dma) << 16);
*laddr++ = readw(dma) | (readw(dma) << 16);
*laddr++ = readw(dma) | (readw(dma) << 16);
len -= 32;
if(len == 0)
break;
}
addr = (unsigned char *)laddr;
writeb(0x10, priv(host)->base + CTRL);
while(len > 0)
{
unsigned int status;
status = readb(priv(host)->base + STAT);
if(status & 0x80)
goto end;
if(status & 0x40)
{
*addr++ = readb(dma);
if(--len == 0)
break;
}
status = readb(priv(host)->base + STAT);
if(status & 0x80)
goto end;
if(status & 0x40)
{
*addr++ = readb(dma);
if(--len == 0)
break;
}
}
end:
writeb(priv(host)->ctrl | 0x40, priv(host)->base + CTRL);
return len;
}
static unsigned char cumanascsi_read(struct Scsi_Host *host, unsigned int reg)
{
void __iomem *base = priv(host)->base;
unsigned char val;
writeb(0, base + CTRL);
val = readb(base + 0x2100 + (reg << 2));
priv(host)->ctrl = 0x40;
writeb(0x40, base + CTRL);
return val;
}
static void cumanascsi_write(struct Scsi_Host *host, unsigned int reg, unsigned int value)
{
void __iomem *base = priv(host)->base;
writeb(0, base + CTRL);
writeb(value, base + 0x2100 + (reg << 2));
priv(host)->ctrl = 0x40;
writeb(0x40, base + CTRL);
}
#include "../NCR5380.c"
static struct 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_bus_reset_handler = NCR5380_bus_reset,
.can_queue = 16,
.this_id = 7,
.sg_tablesize = SG_ALL,
.cmd_per_lun = 2,
.use_clustering = DISABLE_CLUSTERING,
.proc_name = "CumanaSCSI-1",
};
static int cumanascsi1_probe(struct expansion_card *ec,
const struct ecard_id *id)
{
struct Scsi_Host *host;
int ret;
ret = ecard_request_resources(ec);
if (ret)
goto out;
host = scsi_host_alloc(&cumanascsi_template, sizeof(struct NCR5380_hostdata));
if (!host) {
ret = -ENOMEM;
goto out_release;
}
priv(host)->base = ioremap(ecard_resource_start(ec, ECARD_RES_IOCSLOW),
ecard_resource_len(ec, ECARD_RES_IOCSLOW));
priv(host)->dma = ioremap(ecard_resource_start(ec, ECARD_RES_MEMC),
ecard_resource_len(ec, ECARD_RES_MEMC));
if (!priv(host)->base || !priv(host)->dma) {
ret = -ENOMEM;
goto out_unmap;
}
host->irq = ec->irq;
NCR5380_init(host, 0);
priv(host)->ctrl = 0;
writeb(0, priv(host)->base + CTRL);
host->n_io_port = 255;
if (!(request_region(host->io_port, host->n_io_port, "CumanaSCSI-1"))) {
ret = -EBUSY;
goto out_unmap;
}
ret = request_irq(host->irq, cumanascsi_intr, 0,
"CumanaSCSI-1", host);
if (ret) {
printk("scsi%d: IRQ%d not free: %d\n",
host->host_no, host->irq, ret);
goto out_unmap;
}
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_unmap:
iounmap(priv(host)->base);
iounmap(priv(host)->dma);
scsi_host_put(host);
out_release:
ecard_release_resources(ec);
out:
return ret;
}
static void 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);
iounmap(priv(host)->base);
iounmap(priv(host)->dma);
scsi_host_put(host);
ecard_release_resources(ec);
}
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 = 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");

521
drivers/scsi/arm/cumana_2.c Normal file
View file

@ -0,0 +1,521 @@
/*
* linux/drivers/acorn/scsi/cumana_2.c
*
* Copyright (C) 1997-2005 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/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/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 __iomem *base;
unsigned int terms; /* Terminator state */
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)
{
struct cumanascsi2_info *info = ec->irq_data;
writeb(ALATCH_ENA_INT, info->base + CUMANASCSI2_ALATCH);
}
/* 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)
{
struct cumanascsi2_info *info = ec->irq_data;
writeb(ALATCH_DIS_INT, info->base + CUMANASCSI2_ALATCH);
}
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->base + CUMANASCSI2_ALATCH);
} else {
info->terms = 0;
writeb(ALATCH_DIS_TERM, info->base + CUMANASCSI2_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)
*/
static irqreturn_t
cumanascsi_2_intr(int irq, void *dev_id)
{
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, struct 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 = info->info.scsi.dma;
writeb(ALATCH_DIS_DMA, info->base + CUMANASCSI2_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, map_dir);
disable_dma(dmach);
set_dma_sg(dmach, info->sg, bufs);
writeb(alatch_dir, info->base + CUMANASCSI2_ALATCH);
set_dma_mode(dmach, dma_dir);
enable_dma(dmach);
writeb(ALATCH_ENA_DMA, info->base + CUMANASCSI2_ALATCH);
writeb(ALATCH_DIS_BIT32, info->base + CUMANASCSI2_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, struct 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->base + CUMANASCSI2_STATUS);
if (status & STATUS_INT)
goto end;
if (!(status & STATUS_DRQ))
continue;
word = *addr | *(addr + 1) << 8;
writew(word, info->base + CUMANASCSI2_PSEUDODMA);
addr += 2;
length -= 2;
}
#else
printk ("PSEUDO_OUT???\n");
#endif
else {
if (transfer && (transfer & 255)) {
while (length >= 256) {
unsigned int status = readb(info->base + CUMANASCSI2_STATUS);
if (status & STATUS_INT)
return;
if (!(status & STATUS_DRQ))
continue;
readsw(info->base + CUMANASCSI2_PSEUDODMA,
addr, 256 >> 1);
addr += 256;
length -= 256;
}
}
while (length > 0) {
unsigned long word;
unsigned int status = readb(info->base + CUMANASCSI2_STATUS);
if (status & STATUS_INT)
return;
if (!(status & STATUS_DRQ))
continue;
word = readw(info->base + CUMANASCSI2_PSEUDODMA);
*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, struct scsi_pointer *SCp)
{
struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata;
if (info->info.scsi.dma != NO_DMA) {
writeb(ALATCH_DIS_DMA, info->base + CUMANASCSI2_ALATCH);
disable_dma(info->info.scsi.dma);
}
}
/* 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 && strncmp(buffer, "CUMANASCSI2", 11) == 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;
}
static int cumanascsi_2_show_info(struct seq_file *m, struct Scsi_Host *host)
{
struct cumanascsi2_info *info;
info = (struct cumanascsi2_info *)host->hostdata;
seq_printf(m, "Cumana SCSI II driver v%s\n", VERSION);
fas216_print_host(&info->info, m);
seq_printf(m, "Term : o%s\n",
info->terms ? "n" : "ff");
fas216_print_stats(&info->info, m);
fas216_print_devices(&info->info, m);
return 0;
}
static struct scsi_host_template cumanascsi2_template = {
.module = THIS_MODULE,
.show_info = cumanascsi_2_show_info,
.write_info = cumanascsi_2_set_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 = SCSI_MAX_SG_CHAIN_SEGMENTS,
.dma_boundary = IOMD_DMA_BOUNDARY,
.cmd_per_lun = 1,
.use_clustering = DISABLE_CLUSTERING,
.proc_name = "cumanascsi2",
};
static int cumanascsi2_probe(struct expansion_card *ec,
const struct ecard_id *id)
{
struct Scsi_Host *host;
struct cumanascsi2_info *info;
void __iomem *base;
int ret;
ret = ecard_request_resources(ec);
if (ret)
goto out;
base = ecardm_iomap(ec, ECARD_RES_MEMC, 0, 0);
if (!base) {
ret = -ENOMEM;
goto out_region;
}
host = scsi_host_alloc(&cumanascsi2_template,
sizeof(struct cumanascsi2_info));
if (!host) {
ret = -ENOMEM;
goto out_region;
}
ecard_set_drvdata(ec, host);
info = (struct cumanascsi2_info *)host->hostdata;
info->ec = ec;
info->base = base;
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 = ec->irq;
info->info.scsi.dma = ec->dma;
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;
ec->irqaddr = info->base + CUMANASCSI2_STATUS;
ec->irqmask = STATUS_INT;
ecard_setirq(ec, &cumanascsi_2_ops, info);
ret = fas216_init(host);
if (ret)
goto out_free;
ret = request_irq(ec->irq, cumanascsi_2_intr,
0, "cumanascsi2", info);
if (ret) {
printk("scsi%d: IRQ%d not free: %d\n",
host->host_no, ec->irq, ret);
goto out_release;
}
if (info->info.scsi.dma != NO_DMA) {
if (request_dma(info->info.scsi.dma, "cumanascsi2")) {
printk("scsi%d: DMA%d not free, using PIO\n",
host->host_no, info->info.scsi.dma);
info->info.scsi.dma = NO_DMA;
} else {
set_dma_speed(info->info.scsi.dma, 180);
info->info.ifcfg.capabilities |= FASCAP_DMA;
}
}
ret = fas216_add(host, &ec->dev);
if (ret == 0)
goto out;
if (info->info.scsi.dma != NO_DMA)
free_dma(info->info.scsi.dma);
free_irq(ec->irq, host);
out_release:
fas216_release(host);
out_free:
scsi_host_put(host);
out_region:
ecard_release_resources(ec);
out:
return ret;
}
static void 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 (info->info.scsi.dma != NO_DMA)
free_dma(info->info.scsi.dma);
free_irq(ec->irq, info);
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 = 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_param_array(term, int, NULL, 0);
MODULE_PARM_DESC(term, "SCSI bus termination");
MODULE_LICENSE("GPL");

645
drivers/scsi/arm/eesox.c Normal file
View file

@ -0,0 +1,645 @@
/*
* linux/drivers/acorn/scsi/eesox.c
*
* Copyright (C) 1997-2005 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/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/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 __iomem *base;
void __iomem *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)
*/
static irqreturn_t
eesoxscsi_intr(int irq, void *dev_id)
{
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, struct 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 = info->info.scsi.dma;
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, map_dir);
disable_dma(dmach);
set_dma_sg(dmach, info->sg, bufs);
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 __iomem *base)
{
const void __iomem *reg_fas = base + EESOX_FAS216_OFFSET;
const void __iomem *reg_dmastat = base + EESOX_DMASTAT;
const void __iomem *reg_dmadata = base + EESOX_DMADATA;
register const 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 __iomem *base)
{
const void __iomem *reg_fas = base + EESOX_FAS216_OFFSET;
const void __iomem *reg_dmastat = base + EESOX_DMASTAT;
void __iomem *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, struct scsi_pointer *SCp,
fasdmadir_t dir, int transfer_size)
{
struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata;
if (dir == DMA_IN) {
eesoxscsi_buffer_in(SCp->ptr, SCp->this_residual, info->base);
} else {
eesoxscsi_buffer_out(SCp->ptr, SCp->this_residual, info->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, struct scsi_pointer *SCp)
{
struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata;
if (info->info.scsi.dma != NO_DMA)
disable_dma(info->info.scsi.dma);
}
/* 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;
}
static int eesoxscsi_show_info(struct seq_file *m, struct Scsi_Host *host)
{
struct eesoxscsi_info *info;
info = (struct eesoxscsi_info *)host->hostdata;
seq_printf(m, "EESOX SCSI driver v%s\n", VERSION);
fas216_print_host(&info->info, m);
seq_printf(m, "Term : o%s\n",
info->control & EESOX_TERM_ENABLE ? "n" : "ff");
fas216_print_stats(&info->info, m);
fas216_print_devices(&info->info, m);
return 0;
}
static ssize_t eesoxscsi_show_term(struct device *dev, struct device_attribute *attr, 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, struct device_attribute *attr, 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 struct scsi_host_template eesox_template = {
.module = THIS_MODULE,
.show_info = eesoxscsi_show_info,
.write_info = eesoxscsi_set_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 = SCSI_MAX_SG_CHAIN_SEGMENTS,
.dma_boundary = IOMD_DMA_BOUNDARY,
.cmd_per_lun = 1,
.use_clustering = DISABLE_CLUSTERING,
.proc_name = "eesox",
};
static int eesoxscsi_probe(struct expansion_card *ec, const struct ecard_id *id)
{
struct Scsi_Host *host;
struct eesoxscsi_info *info;
void __iomem *base;
int ret;
ret = ecard_request_resources(ec);
if (ret)
goto out;
base = ecardm_iomap(ec, ECARD_RES_IOCFAST, 0, 0);
if (!base) {
ret = -ENOMEM;
goto out_region;
}
host = scsi_host_alloc(&eesox_template,
sizeof(struct eesoxscsi_info));
if (!host) {
ret = -ENOMEM;
goto out_region;
}
ecard_set_drvdata(ec, host);
info = (struct eesoxscsi_info *)host->hostdata;
info->ec = ec;
info->base = base;
info->ctl_port = base + EESOX_CONTROL;
info->control = term[ec->slot_no] ? EESOX_TERM_ENABLE : 0;
writeb(info->control, info->ctl_port);
info->info.scsi.io_base = base + EESOX_FAS216_OFFSET;
info->info.scsi.io_shift = EESOX_FAS216_SHIFT;
info->info.scsi.irq = ec->irq;
info->info.scsi.dma = ec->dma;
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;
ec->irqaddr = base + EESOX_DMASTAT;
ec->irqmask = EESOX_STAT_INTR;
ecard_setirq(ec, &eesoxscsi_ops, info);
device_create_file(&ec->dev, &dev_attr_bus_term);
ret = fas216_init(host);
if (ret)
goto out_free;
ret = request_irq(ec->irq, eesoxscsi_intr, 0, "eesoxscsi", info);
if (ret) {
printk("scsi%d: IRQ%d not free: %d\n",
host->host_no, ec->irq, ret);
goto out_remove;
}
if (info->info.scsi.dma != NO_DMA) {
if (request_dma(info->info.scsi.dma, "eesox")) {
printk("scsi%d: DMA%d not free, DMA disabled\n",
host->host_no, info->info.scsi.dma);
info->info.scsi.dma = NO_DMA;
} else {
set_dma_speed(info->info.scsi.dma, 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 (info->info.scsi.dma != NO_DMA)
free_dma(info->info.scsi.dma);
free_irq(ec->irq, host);
out_remove:
fas216_remove(host);
out_free:
device_remove_file(&ec->dev, &dev_attr_bus_term);
scsi_host_put(host);
out_region:
ecard_release_resources(ec);
out:
return ret;
}
static void 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 (info->info.scsi.dma != NO_DMA)
free_dma(info->info.scsi.dma);
free_irq(ec->irq, info);
device_remove_file(&ec->dev, &dev_attr_bus_term);
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 = 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_param_array(term, int, NULL, 0);
MODULE_PARM_DESC(term, "SCSI bus termination");
MODULE_LICENSE("GPL");

3041
drivers/scsi/arm/fas216.c Normal file

File diff suppressed because it is too large Load diff

393
drivers/scsi/arm/fas216.h Normal file
View file

@ -0,0 +1,393 @@
/*
* 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
#include <scsi/scsi_eh.h>
#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, /* Negotiate with device */
neg_inprogress, /* Negotiation sent */
neg_complete, /* Negotiation complete */
neg_targcomplete, /* Target completed negotiation */
neg_invalid /* Negotiation 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 */
struct scsi_cmnd *SCpnt; /* currently processing command */
struct scsi_cmnd *origSCpnt; /* original connecting command */
struct scsi_cmnd *reqSCpnt; /* request sense command */
struct scsi_cmnd *rstSCpnt; /* reset command */
struct 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 __iomem *io_base; /* iomem base 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 */
int dma; /* dma channel */
struct 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, struct scsi_pointer *SCp, fasdmadir_t direction, fasdmatype_t min_dma);
void (*pseudo)(struct Scsi_Host *host, struct scsi_pointer *SCp, fasdmadir_t direction, int transfer);
void (*stop) (struct Scsi_Host *host, struct scsi_pointer *SCp);
} dma;
/* miscellaneous */
int internal_done; /* flag to indicate request done */
struct scsi_eh_save ses; /* holds request sense restore info */
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(struct Scsi_Host *h, struct scsi_cmnd *SCpnt)
* Purpose : queue a command for adapter to process.
* Params : h - host adapter
* : SCpnt - Command to queue
* Returns : 0 - success, else error
*/
extern int fas216_queue_command(struct Scsi_Host *h, struct scsi_cmnd *SCpnt);
/* Function: int fas216_noqueue_command(struct Scsi_Host *h, struct scsi_cmnd *SCpnt)
* Purpose : queue a command for adapter to process, and process it to completion.
* Params : h - host adapter
* : SCpnt - Command to queue
* Returns : 0 - success, else error
*/
extern int fas216_noqueue_command(struct Scsi_Host *, struct 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 void fas216_print_host(FAS216_Info *info, struct seq_file *m);
extern void fas216_print_stats(FAS216_Info *info, struct seq_file *m);
extern void fas216_print_devices(FAS216_Info *info, struct seq_file *m);
/* Function: int fas216_eh_abort(struct scsi_cmnd *SCpnt)
* Purpose : abort this command
* Params : SCpnt - command to abort
* Returns : FAILED if unable to abort
*/
extern int fas216_eh_abort(struct scsi_cmnd *SCpnt);
/* Function: int fas216_eh_device_reset(struct 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(struct scsi_cmnd *SCpnt);
/* Function: int fas216_eh_bus_reset(struct 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(struct scsi_cmnd *SCpnt);
/* Function: int fas216_eh_host_reset(struct 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(struct scsi_cmnd *SCpnt);
#endif /* FAS216_H */

171
drivers/scsi/arm/msgqueue.c Normal file
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

226
drivers/scsi/arm/oak.c Normal file
View file

@ -0,0 +1,226 @@
/*
* Oak Generic NCR5380 driver
*
* Copyright 1995-2002, Russell King
*/
#include <linux/module.h>
#include <linux/signal.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 "../scsi.h"
#include <scsi/scsi_host.h>
#define AUTOSENSE
/*#define PSEUDO_DMA*/
#define OAKSCSI_PUBLIC_RELEASE 1
#define DONT_USE_INTR
#define priv(host) ((struct NCR5380_hostdata *)(host)->hostdata)
#define NCR5380_local_declare() void __iomem *_base
#define NCR5380_setup(host) _base = priv(host)->base
#define NCR5380_read(reg) readb(_base + ((reg) << 2))
#define NCR5380_write(reg, value) writeb(value, _base + ((reg) << 2))
#define NCR5380_intr oakscsi_intr
#define NCR5380_queue_command oakscsi_queue_command
#define NCR5380_show_info oakscsi_show_info
#define NCR5380_write_info oakscsi_write_info
#define NCR5380_implementation_fields \
void __iomem *base
#include "../NCR5380.h"
#undef START_DMA_INITIATOR_RECEIVE_REG
#define START_DMA_INITIATOR_RECEIVE_REG (128 + 7)
const char * oakscsi_info (struct Scsi_Host *spnt)
{
return "";
}
#define STAT ((128 + 16) << 2)
#define DATA ((128 + 8) << 2)
static inline int NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *addr,
int len)
{
void __iomem *base = priv(instance)->base;
printk("writing %p len %d\n",addr, len);
if(!len) return -1;
while(1)
{
int status;
while (((status = readw(base + STAT)) & 0x100)==0);
}
}
static inline int NCR5380_pread(struct Scsi_Host *instance, unsigned char *addr,
int len)
{
void __iomem *base = priv(instance)->base;
printk("reading %p len %d\n", addr, len);
while(len > 0)
{
unsigned int status, timeout;
unsigned long b;
timeout = 0x01FFFFFF;
while (((status = readw(base + STAT)) & 0x100)==0)
{
timeout--;
if(status & 0x200 || !timeout)
{
printk("status = %08X\n", status);
return 1;
}
}
if(len >= 128)
{
readsw(base + DATA, addr, 128);
addr += 128;
len -= 128;
}
else
{
b = (unsigned long) readw(base + DATA);
*addr ++ = b;
len -= 1;
if(len)
*addr ++ = b>>8;
len -= 1;
}
}
return 0;
}
#undef STAT
#undef DATA
#include "../NCR5380.c"
static struct scsi_host_template oakscsi_template = {
.module = THIS_MODULE,
.show_info = oakscsi_show_info,
.write_info = oakscsi_write_info,
.name = "Oak 16-bit SCSI",
.info = oakscsi_info,
.queuecommand = oakscsi_queue_command,
.eh_abort_handler = NCR5380_abort,
.eh_bus_reset_handler = NCR5380_bus_reset,
.can_queue = 16,
.this_id = 7,
.sg_tablesize = SG_ALL,
.cmd_per_lun = 2,
.use_clustering = DISABLE_CLUSTERING,
.proc_name = "oakscsi",
};
static int oakscsi_probe(struct expansion_card *ec, const struct ecard_id *id)
{
struct Scsi_Host *host;
int ret = -ENOMEM;
ret = ecard_request_resources(ec);
if (ret)
goto out;
host = scsi_host_alloc(&oakscsi_template, sizeof(struct NCR5380_hostdata));
if (!host) {
ret = -ENOMEM;
goto release;
}
priv(host)->base = ioremap(ecard_resource_start(ec, ECARD_RES_MEMC),
ecard_resource_len(ec, ECARD_RES_MEMC));
if (!priv(host)->base) {
ret = -ENOMEM;
goto unreg;
}
host->irq = IRQ_NONE;
host->n_io_port = 255;
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_unmap;
scsi_scan_host(host);
goto out;
out_unmap:
iounmap(priv(host)->base);
unreg:
scsi_host_put(host);
release:
ecard_release_resources(ec);
out:
return ret;
}
static void 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);
iounmap(priv(host)->base);
scsi_host_put(host);
ecard_release_resources(ec);
}
static const struct ecard_id oakscsi_cids[] = {
{ MANU_OAK, PROD_OAK_SCSI },
{ 0xffff, 0xffff }
};
static struct ecard_driver oakscsi_driver = {
.probe = oakscsi_probe,
.remove = 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");

451
drivers/scsi/arm/powertec.c Normal file
View file

@ -0,0 +1,451 @@
/*
* linux/drivers/acorn/scsi/powertec.c
*
* Copyright (C) 1997-2005 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/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/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 __iomem *base;
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)
{
struct powertec_info *info = ec->irq_data;
writeb(POWERTEC_INTR_ENABLE, info->base + POWERTEC_INTR_CONTROL);
}
/* 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)
{
struct powertec_info *info = ec->irq_data;
writeb(POWERTEC_INTR_DISABLE, info->base + POWERTEC_INTR_CONTROL);
}
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->base + POWERTEC_TERM_CONTROL);
}
/* 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)
*/
static irqreturn_t powertecscsi_intr(int irq, void *dev_id)
{
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, struct 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 = info->info.scsi.dma;
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, map_dir);
disable_dma(dmach);
set_dma_sg(dmach, info->sg, bufs);
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, struct scsi_pointer *SCp)
{
struct powertec_info *info = (struct powertec_info *)host->hostdata;
if (info->info.scsi.dma != NO_DMA)
disable_dma(info->info.scsi.dma);
}
/* 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 up to.
* length - length of buffer
* inout - 0 for reading, 1 for writing.
* Returns : length of data written to buffer.
*/
static int powertecscsi_show_info(struct seq_file *m, struct Scsi_Host *host)
{
struct powertec_info *info;
info = (struct powertec_info *)host->hostdata;
seq_printf(m, "PowerTec SCSI driver v%s\n", VERSION);
fas216_print_host(&info->info, m);
seq_printf(m, "Term : o%s\n",
info->term_ctl ? "n" : "ff");
fas216_print_stats(&info->info, m);
fas216_print_devices(&info->info, m);
return 0;
}
static ssize_t powertecscsi_show_term(struct device *dev, struct device_attribute *attr, 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, struct device_attribute *attr, 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 struct scsi_host_template powertecscsi_template = {
.module = THIS_MODULE,
.show_info = powertecscsi_show_info,
.write_info = powertecscsi_set_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 = SCSI_MAX_SG_CHAIN_SEGMENTS,
.dma_boundary = IOMD_DMA_BOUNDARY,
.cmd_per_lun = 2,
.use_clustering = ENABLE_CLUSTERING,
.proc_name = "powertec",
};
static int powertecscsi_probe(struct expansion_card *ec,
const struct ecard_id *id)
{
struct Scsi_Host *host;
struct powertec_info *info;
void __iomem *base;
int ret;
ret = ecard_request_resources(ec);
if (ret)
goto out;
base = ecardm_iomap(ec, ECARD_RES_IOCFAST, 0, 0);
if (!base) {
ret = -ENOMEM;
goto out_region;
}
host = scsi_host_alloc(&powertecscsi_template,
sizeof (struct powertec_info));
if (!host) {
ret = -ENOMEM;
goto out_region;
}
ecard_set_drvdata(ec, host);
info = (struct powertec_info *)host->hostdata;
info->base = base;
powertecscsi_terminator_ctl(host, term[ec->slot_no]);
info->ec = ec;
info->info.scsi.io_base = base + POWERTEC_FAS216_OFFSET;
info->info.scsi.io_shift = POWERTEC_FAS216_SHIFT;
info->info.scsi.irq = ec->irq;
info->info.scsi.dma = ec->dma;
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;
ec->irqaddr = base + POWERTEC_INTR_STATUS;
ec->irqmask = POWERTEC_INTR_BIT;
ecard_setirq(ec, &powertecscsi_ops, info);
device_create_file(&ec->dev, &dev_attr_bus_term);
ret = fas216_init(host);
if (ret)
goto out_free;
ret = request_irq(ec->irq, powertecscsi_intr,
0, "powertec", info);
if (ret) {
printk("scsi%d: IRQ%d not free: %d\n",
host->host_no, ec->irq, ret);
goto out_release;
}
if (info->info.scsi.dma != NO_DMA) {
if (request_dma(info->info.scsi.dma, "powertec")) {
printk("scsi%d: DMA%d not free, using PIO\n",
host->host_no, info->info.scsi.dma);
info->info.scsi.dma = NO_DMA;
} else {
set_dma_speed(info->info.scsi.dma, 180);
info->info.ifcfg.capabilities |= FASCAP_DMA;
}
}
ret = fas216_add(host, &ec->dev);
if (ret == 0)
goto out;
if (info->info.scsi.dma != NO_DMA)
free_dma(info->info.scsi.dma);
free_irq(ec->irq, host);
out_release:
fas216_release(host);
out_free:
device_remove_file(&ec->dev, &dev_attr_bus_term);
scsi_host_put(host);
out_region:
ecard_release_resources(ec);
out:
return ret;
}
static void powertecscsi_remove(struct expansion_card *ec)
{
struct Scsi_Host *host = ecard_get_drvdata(ec);
struct powertec_info *info = (struct powertec_info *)host->hostdata;
ecard_set_drvdata(ec, NULL);
fas216_remove(host);
device_remove_file(&ec->dev, &dev_attr_bus_term);
if (info->info.scsi.dma != NO_DMA)
free_dma(info->info.scsi.dma);
free_irq(ec->irq, info);
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 = 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_param_array(term, int, NULL, 0);
MODULE_PARM_DESC(term, "SCSI bus termination");
MODULE_LICENSE("GPL");

318
drivers/scsi/arm/queue.c Normal file
View file

@ -0,0 +1,318 @@
/*
* 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;
struct 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);
kfree(queue->alloc);
}
/*
* Function: int __queue_add(Queue_t *queue, struct 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, struct 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);
BUG_ON(BAD_MAGIC(q, QUEUE_MAGIC_FREE));
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 struct 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);
BUG_ON(BAD_MAGIC(q, QUEUE_MAGIC_USED));
SET_MAGIC(q, QUEUE_MAGIC_FREE);
list_add(ent, &queue->free);
return q->SCpnt;
}
/*
* Function: struct 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 : struct scsi_cmnd if successful (and a reference), or NULL if no command available
*/
struct scsi_cmnd *queue_remove_exclude(Queue_t *queue, unsigned long *exclude)
{
unsigned long flags;
struct list_head *l;
struct 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 +
(u8)(q->SCpnt->device->lun & 0x7), exclude)) {
SCpnt = __queue_remove(queue, l);
break;
}
}
spin_unlock_irqrestore(&queue->queue_lock, flags);
return SCpnt;
}
/*
* Function: struct scsi_cmnd *queue_remove (queue)
* Purpose : removes first SCSI command from a queue
* Params : queue - queue to remove command from
* Returns : struct scsi_cmnd if successful (and a reference), or NULL if no command available
*/
struct scsi_cmnd *queue_remove(Queue_t *queue)
{
unsigned long flags;
struct 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: struct 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 : struct scsi_cmnd if successful, or NULL if no command satisfies requirements
*/
struct scsi_cmnd *queue_remove_tgtluntag(Queue_t *queue, int target, int lun,
int tag)
{
unsigned long flags;
struct list_head *l;
struct 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, struct 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, struct 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");

107
drivers/scsi/arm/queue.h Normal file
View file

@ -0,0 +1,107 @@
/*
* 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: struct scsi_cmnd *queue_remove (queue)
* Purpose : removes first SCSI command from a queue
* Params : queue - queue to remove command from
* Returns : struct scsi_cmnd if successful (and a reference), or NULL if no command available
*/
extern struct scsi_cmnd *queue_remove (Queue_t *queue);
/*
* Function: struct 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 : struct scsi_cmnd if successful (and a reference), or NULL if no command available
*/
extern struct 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, struct 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, struct scsi_cmnd *SCpnt, int head);
/*
* Function: struct 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 : struct scsi_cmnd if successful, or NULL if no command satisfies requirements
*/
extern struct 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, struct 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, struct scsi_cmnd *SCpnt);
#endif /* QUEUE_H */

128
drivers/scsi/arm/scsi.h Normal file
View file

@ -0,0 +1,128 @@
/*
* 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.
*/
#include <linux/scatterlist.h>
#define BELT_AND_BRACES
/*
* The scatter-gather list handling. This contains all
* the yucky stuff that needs to be fixed properly.
*/
/*
* copy_SCp_to_sg() Assumes contiguous allocation at @sg of at-most @max
* entries of uninitialized memory. SCp is from scsi-ml and has a valid
* (possibly chained) sg-list
*/
static inline int copy_SCp_to_sg(struct scatterlist *sg, struct scsi_pointer *SCp, int max)
{
int bufs = SCp->buffers_residual;
/* FIXME: It should be easy for drivers to loop on copy_SCp_to_sg().
* and to remove this BUG_ON. Use min() in-its-place
*/
BUG_ON(bufs + 1 > max);
sg_set_buf(sg, SCp->ptr, SCp->this_residual);
if (bufs) {
struct scatterlist *src_sg;
unsigned i;
for_each_sg(sg_next(SCp->buffer), src_sg, bufs, i)
*(++sg) = *src_sg;
sg_mark_end(sg);
}
return bufs + 1;
}
static inline int next_SCp(struct scsi_pointer *SCp)
{
int ret = SCp->buffers_residual;
if (ret) {
SCp->buffer = sg_next(SCp->buffer);
SCp->buffers_residual--;
SCp->ptr = sg_virt(SCp->buffer);
SCp->this_residual = SCp->buffer->length;
} else {
SCp->ptr = NULL;
SCp->this_residual = 0;
}
return ret;
}
static inline unsigned char get_next_SCp_byte(struct scsi_pointer *SCp)
{
char c = *SCp->ptr;
SCp->ptr += 1;
SCp->this_residual -= 1;
return c;
}
static inline void put_next_SCp_byte(struct scsi_pointer *SCp, unsigned char c)
{
*SCp->ptr = c;
SCp->ptr += 1;
SCp->this_residual -= 1;
}
static inline void init_SCp(struct scsi_cmnd *SCpnt)
{
memset(&SCpnt->SCp, 0, sizeof(struct scsi_pointer));
if (scsi_bufflen(SCpnt)) {
unsigned long len = 0;
SCpnt->SCp.buffer = scsi_sglist(SCpnt);
SCpnt->SCp.buffers_residual = scsi_sg_count(SCpnt) - 1;
SCpnt->SCp.ptr = sg_virt(SCpnt->SCp.buffer);
SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length;
SCpnt->SCp.phase = scsi_bufflen(SCpnt);
#ifdef BELT_AND_BRACES
{ /*
* Calculate correct buffer length. Some commands
* come in with the wrong scsi_bufflen.
*/
struct scatterlist *sg;
unsigned i, sg_count = scsi_sg_count(SCpnt);
scsi_for_each_sg(SCpnt, sg, sg_count, i)
len += sg->length;
if (scsi_bufflen(SCpnt) != 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,
scsi_bufflen(SCpnt), len);
/*
* FIXME: Totaly naive fixup. We should abort
* with error
*/
SCpnt->SCp.phase =
min_t(unsigned long, len,
scsi_bufflen(SCpnt));
}
}
#endif
} else {
SCpnt->SCp.ptr = NULL;
SCpnt->SCp.this_residual = 0;
SCpnt->SCp.phase = 0;
}
}