mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 09:08:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
84
drivers/scsi/arm/Kconfig
Normal file
84
drivers/scsi/arm/Kconfig
Normal 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
13
drivers/scsi/arm/Makefile
Normal 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
|
138
drivers/scsi/arm/acornscsi-io.S
Normal file
138
drivers/scsi/arm/acornscsi-io.S
Normal 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
3014
drivers/scsi/arm/acornscsi.c
Normal file
File diff suppressed because it is too large
Load diff
353
drivers/scsi/arm/acornscsi.h
Normal file
353
drivers/scsi/arm/acornscsi.h
Normal 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
358
drivers/scsi/arm/arxescsi.c
Normal 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
340
drivers/scsi/arm/cumana_1.c
Normal 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
521
drivers/scsi/arm/cumana_2.c
Normal 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
645
drivers/scsi/arm/eesox.c
Normal 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
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
393
drivers/scsi/arm/fas216.h
Normal 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
171
drivers/scsi/arm/msgqueue.c
Normal 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");
|
82
drivers/scsi/arm/msgqueue.h
Normal file
82
drivers/scsi/arm/msgqueue.h
Normal 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
226
drivers/scsi/arm/oak.c
Normal 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
451
drivers/scsi/arm/powertec.c
Normal 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
318
drivers/scsi/arm/queue.c
Normal 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
107
drivers/scsi/arm/queue.h
Normal 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
128
drivers/scsi/arm/scsi.h
Normal 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;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue