// SPDX-License-Identifier: GPL-2.0-or-later
/* aha152x.c -- Adaptec AHA-152x driver
* Author : J ü rgen E . Fischer , fischer @ norbit . de
* Copyright 1993 - 2004 J ü rgen E . Fischer
*
* $ Id : aha152x . c , v 2 . 7 2004 / 01 / 24 11 : 42 : 59 fischer Exp $
*
* $ Log : aha152x . c , v $
* Revision 2 . 7 2004 / 01 / 24 11 : 42 : 59 fischer
* - gather code that is not used by PCMCIA at the end
* - move request_region for ! PCMCIA case to detection
* - migration to new scsi host api ( remove legacy code )
* - free host scribble before scsi_done
* - fix error handling
* - one isapnp device added to id_table
*
* Revision 2 . 6 2003 / 10 / 30 20 : 52 : 47 fischer
* - interfaces changes for kernel 2 . 6
* - aha152x_probe_one introduced for pcmcia stub
* - fixed pnpdev handling
* - instead of allocation a new one , reuse command for request sense after check condition and reset
* - fixes race in is_complete
*
* Revision 2 . 5 2002 / 04 / 14 11 : 24 : 53 fischer
* - isapnp support
* - abort fixed
* - 2 . 5 support
*
* Revision 2 . 4 2000 / 12 / 16 12 : 53 : 56 fischer
* - allow REQUEST SENSE to be queued
* - handle shared PCI interrupts
*
* Revision 2 . 3 2000 / 11 / 04 16 : 40 : 26 fischer
* - handle data overruns
* - extend timeout for data phases
*
* Revision 2 . 2 2000 / 08 / 08 19 : 54 : 53 fischer
* - minor changes
*
* Revision 2 . 1 2000 / 05 / 17 16 : 23 : 17 fischer
* - signature update
* - fix for data out w / o scatter gather
*
* Revision 2 . 0 1999 / 12 / 25 15 : 07 : 32 fischer
* - interrupt routine completly reworked
* - basic support for new eh code
*
* Revision 1 . 21 1999 / 11 / 10 23 : 46 : 36 fischer
* - default to synchronous operation
* - synchronous negotiation fixed
* - added timeout to loops
* - debugging output can be controlled through procfs
*
* Revision 1 . 20 1999 / 11 / 07 18 : 37 : 31 fischer
* - synchronous operation works
* - resid support for sg driver
*
* Revision 1 . 19 1999 / 11 / 02 22 : 39 : 59 fischer
* - moved leading comments to README . aha152x
* - new additional module parameters
* - updates for 2 . 3
* - support for the Tripace TC1550 controller
* - interrupt handling changed
*
* Revision 1 . 18 1996 / 09 / 07 20 : 10 : 40 fischer
* - fixed can_queue handling ( multiple outstanding commands working again )
*
* Revision 1 . 17 1996 / 08 / 17 16 : 05 : 14 fischer
* - biosparam improved
* - interrupt verification
* - updated documentation
* - cleanups
*
* Revision 1 . 16 1996 / 06 / 09 00 : 04 : 56 root
* - added configuration symbols for insmod ( aha152x / aha152x1 )
*
* Revision 1 . 15 1996 / 04 / 30 14 : 52 : 06 fischer
* - proc info fixed
* - support for extended translation for > 1 GB disks
*
* Revision 1 . 14 1996 / 01 / 17 15 : 11 : 20 fischer
* - fixed lockup in MESSAGE IN phase after reconnection
*
* Revision 1 . 13 1996 / 01 / 09 02 : 15 : 53 fischer
* - some cleanups
* - moved request_irq behind controller initialization
* ( to avoid spurious interrupts )
*
* Revision 1 . 12 1995 / 12 / 16 12 : 26 : 07 fischer
* - barrier ( ) s added
* - configurable RESET delay added
*
* Revision 1 . 11 1995 / 12 / 06 21 : 18 : 35 fischer
* - some minor updates
*
* Revision 1 . 10 1995 / 07 / 22 19 : 18 : 45 fischer
* - support for 2 controllers
* - started synchronous data transfers ( not working yet )
*
* Revision 1 . 9 1995 / 03 / 18 09 : 20 : 24 root
* - patches for PCMCIA and modules
*
* Revision 1 . 8 1995 / 01 / 21 22 : 07 : 19 root
* - snarf_region = > request_region
* - aha152x_intr interface change
*
* Revision 1 . 7 1995 / 01 / 02 23 : 19 : 36 root
* - updated COMMAND_SIZE to cmd_len
* - changed sti ( ) to restore_flags ( )
* - fixed some # ifdef which generated warnings
*
* Revision 1 . 6 1994 / 11 / 24 20 : 35 : 27 root
* - problem with odd number of bytes in fifo fixed
*
* Revision 1 . 5 1994 / 10 / 30 14 : 39 : 56 root
* - abort code fixed
* - debugging improved
*
* Revision 1 . 4 1994 / 09 / 12 11 : 33 : 01 root
* - irqaction to request_irq
* - abortion updated
*
* Revision 1 . 3 1994 / 08 / 04 13 : 53 : 05 root
* - updates for mid - level - driver changes
* - accept unexpected BUSFREE phase as error condition
* - parity check now configurable
*
* Revision 1 . 2 1994 / 07 / 03 12 : 56 : 36 root
* - cleaned up debugging code
* - more tweaking on reset delays
* - updated abort / reset code ( pretty untested . . . )
*
* Revision 1 . 1 1994 / 05 / 28 21 : 18 : 49 root
* - update for mid - level interface change ( abort - reset )
* - delays after resets adjusted for some slow devices
*
* Revision 1 . 0 1994 / 03 / 25 12 : 52 : 00 root
* - Fixed " more data than expected " problem
* - added new BIOS signatures
*
* Revision 0 . 102 1994 / 01 / 31 20 : 44 : 12 root
* - minor changes in insw / outsw handling
*
* Revision 0 . 101 1993 / 12 / 13 01 : 16 : 27 root
* - fixed STATUS phase ( non - GOOD stati were dropped sometimes ;
* fixes problems with CD - ROM sector size detection & media change )
*
* Revision 0 . 100 1993 / 12 / 10 16 : 58 : 47 root
* - fix for unsuccessful selections in case of non - continuous id assignments
* on the scsi bus .
*
* Revision 0 . 99 1993 / 10 / 24 16 : 19 : 59 root
* - fixed DATA IN ( rare read errors gone )
*
* Revision 0 . 98 1993 / 10 / 17 12 : 54 : 44 root
* - fixed some recent fixes ( shame on me )
* - moved initialization of scratch area to aha152x_queue
*
* Revision 0 . 97 1993 / 10 / 09 18 : 53 : 53 root
* - DATA IN fixed . Rarely left data in the fifo .
*
* Revision 0 . 96 1993 / 10 / 03 00 : 53 : 59 root
* - minor changes on DATA IN
*
* Revision 0 . 95 1993 / 09 / 24 10 : 36 : 01 root
* - change handling of MSGI after reselection
* - fixed sti / cli
* - minor changes
*
* Revision 0 . 94 1993 / 09 / 18 14 : 08 : 22 root
* - fixed bug in multiple outstanding command code
* - changed detection
* - support for kernel command line configuration
* - reset corrected
* - changed message handling
*
* Revision 0 . 93 1993 / 09 / 15 20 : 41 : 19 root
* - fixed bugs with multiple outstanding commands
*
* Revision 0 . 92 1993 / 09 / 13 02 : 46 : 33 root
* - multiple outstanding commands work ( no problems with IBM drive )
*
* Revision 0 . 91 1993 / 09 / 12 20 : 51 : 46 root
* added multiple outstanding commands
* ( some problem with this $ % & ? IBM device remain )
*
* Revision 0 . 9 1993 / 09 / 12 11 : 11 : 22 root
* - corrected auto - configuration
* - changed the auto - configuration ( added some ' # define ' s )
* - added support for dis - / reconnection
*
* Revision 0 . 8 1993 / 09 / 06 23 : 09 : 39 root
* - added support for the drive activity light
* - minor changes
*
* Revision 0 . 7 1993 / 09 / 05 14 : 30 : 15 root
* - improved phase detection
* - now using the new snarf_region code of 0 . 99 pl13
*
* Revision 0 . 6 1993 / 09 / 02 11 : 01 : 38 root
* first public release ; added some signatures and biosparam ( )
*
* Revision 0 . 5 1993 / 08 / 30 10 : 23 : 30 root
* fixed timing problems with my IBM drive
*
* Revision 0 . 4 1993 / 08 / 29 14 : 06 : 52 root
* fixed some problems with timeouts due incomplete commands
*
* Revision 0 . 3 1993 / 08 / 28 15 : 55 : 03 root
* writing data works too . mounted and worked on a dos partition
*
* Revision 0 . 2 1993 / 08 / 27 22 : 42 : 07 root
* reading data works . Mounted a msdos partition .
*
* Revision 0 . 1 1993 / 08 / 25 13 : 38 : 30 root
* first " damn thing doesn ' t work " version
*
* Revision 0 . 0 1993 / 08 / 14 19 : 54 : 25 root
* empty function bodies ; detect ( ) works .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
see Documentation / scsi / aha152x . rst for configuration details
**************************************************************************/
#include <linux/module.h>
#include <asm /irq.h>
#include <linux/io.h>
#include <linux/blkdev.h>
#include <linux/completion.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/wait.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/isapnp.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_dbg.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_eh.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_transport_spi.h>
#include <scsi/scsicam.h>
#include "aha152x.h"
static LIST_HEAD(aha152x_host_list);
/* DEFINES */
/* For PCMCIA cards, always use AUTOCONF */
#if defined (AHA152X_PCMCIA) || defined (MODULE)
#if !defined (AUTOCONF)
#define AUTOCONF
#endif
#endif
#if !defined (AUTOCONF) && !defined (SETUP0)
#error define AUTOCONF or SETUP0
#endif
#define DO_LOCK(flags) spin_lock_irqsave(&QLOCK,flags)
#define DO_UNLOCK(flags) spin_unlock_irqrestore(&QLOCK,flags)
#define LEAD "(scsi%d:%d:%d) "
#define INFO_LEAD KERN_INFO LEAD
#define CMDINFO(cmd) \
(cmd) ? ((cmd)->device->host->host_no) : -1 , \
(cmd) ? ((cmd)->device->id & 0 x0f) : -1 , \
(cmd) ? ((u8)(cmd)->device->lun & 0 x07) : -1
static inline void
CMD_INC_RESID(struct scsi_cmnd *cmd, int inc)
{
scsi_set_resid(cmd, scsi_get_resid(cmd) + inc);
}
#define DELAY_DEFAULT 1000
#if defined (AHA152X_PCMCIA)
#define IRQ_MIN 0
#define IRQ_MAX 16
#else
#define IRQ_MIN 9
#if defined (__PPC)
#define IRQ_MAX (irq_get_nr_irqs()-1 )
#else
#define IRQ_MAX 12
#endif
#endif
enum {
not_issued = 0 x0001, /* command not yet issued */
selecting = 0 x0002, /* target is being selected */
identified = 0 x0004, /* IDENTIFY was sent */
disconnected = 0 x0008, /* target disconnected */
completed = 0 x0010, /* target sent COMMAND COMPLETE */
aborted = 0 x0020, /* ABORT was sent */
resetted = 0 x0040, /* BUS DEVICE RESET was sent */
spiordy = 0 x0080, /* waiting for SPIORDY to raise */
syncneg = 0 x0100, /* synchronous negotiation in progress */
aborting = 0 x0200, /* ABORT is pending */
resetting = 0 x0400, /* BUS DEVICE RESET is pending */
check_condition = 0 x0800, /* requesting sense after CHECK CONDITION */
};
struct aha152x_cmd_priv {
char *ptr;
int this_residual;
struct scatterlist *buffer;
int status;
int message;
int sent_command;
int phase;
};
static struct aha152x_cmd_priv *aha152x_priv(struct scsi_cmnd *cmd)
{
return scsi_cmd_priv(cmd);
}
MODULE_AUTHOR("Jürgen Fischer" );
MODULE_DESCRIPTION(AHA152X_REVID);
MODULE_LICENSE("GPL" );
#if !defined (AHA152X_PCMCIA)
#if defined (MODULE)
static int io[] = {0 , 0 };
module_param_hw_array(io, int , ioport, NULL, 0 );
MODULE_PARM_DESC(io,"base io address of controller" );
static int irq[] = {0 , 0 };
module_param_hw_array(irq, int , irq, NULL, 0 );
MODULE_PARM_DESC(irq,"interrupt for controller" );
static int scsiid[] = {7 , 7 };
module_param_array(scsiid, int , NULL, 0 );
MODULE_PARM_DESC(scsiid,"scsi id of controller" );
static int reconnect[] = {1 , 1 };
module_param_array(reconnect, int , NULL, 0 );
MODULE_PARM_DESC(reconnect,"allow targets to disconnect" );
static int parity[] = {1 , 1 };
module_param_array(parity, int , NULL, 0 );
MODULE_PARM_DESC(parity,"use scsi parity" );
static int sync[] = {1 , 1 };
module_param_array(sync, int , NULL, 0 );
MODULE_PARM_DESC(sync,"use synchronous transfers" );
static int delay[] = {DELAY_DEFAULT, DELAY_DEFAULT};
module_param_array(delay, int , NULL, 0 );
MODULE_PARM_DESC(delay,"scsi reset delay" );
static int exttrans[] = {0 , 0 };
module_param_array(exttrans, int , NULL, 0 );
MODULE_PARM_DESC(exttrans,"use extended translation" );
static int aha152x[] = {0 , 11 , 7 , 1 , 1 , 0 , DELAY_DEFAULT, 0 };
module_param_array(aha152x, int , NULL, 0 );
MODULE_PARM_DESC(aha152x, "parameters for first controller" );
static int aha152x1[] = {0 , 11 , 7 , 1 , 1 , 0 , DELAY_DEFAULT, 0 };
module_param_array(aha152x1, int , NULL, 0 );
MODULE_PARM_DESC(aha152x1, "parameters for second controller" );
#endif /* MODULE */
#ifdef __ISAPNP__
static struct isapnp_device_id id_table[] = {
{ ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('A' , 'D' , 'P' ), ISAPNP_FUNCTION(0 x1502), 0 },
{ ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('A' , 'D' , 'P' ), ISAPNP_FUNCTION(0 x1505), 0 },
{ ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('A' , 'D' , 'P' ), ISAPNP_FUNCTION(0 x1510), 0 },
{ ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('A' , 'D' , 'P' ), ISAPNP_FUNCTION(0 x1515), 0 },
{ ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('A' , 'D' , 'P' ), ISAPNP_FUNCTION(0 x1520), 0 },
{ ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('A' , 'D' , 'P' ), ISAPNP_FUNCTION(0 x2015), 0 },
{ ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('A' , 'D' , 'P' ), ISAPNP_FUNCTION(0 x1522), 0 },
{ ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('A' , 'D' , 'P' ), ISAPNP_FUNCTION(0 x2215), 0 },
{ ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('A' , 'D' , 'P' ), ISAPNP_FUNCTION(0 x1530), 0 },
{ ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('A' , 'D' , 'P' ), ISAPNP_FUNCTION(0 x3015), 0 },
{ ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('A' , 'D' , 'P' ), ISAPNP_FUNCTION(0 x1532), 0 },
{ ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('A' , 'D' , 'P' ), ISAPNP_FUNCTION(0 x3215), 0 },
{ ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('A' , 'D' , 'P' ), ISAPNP_FUNCTION(0 x6360), 0 },
{ ISAPNP_DEVICE_SINGLE_END, }
};
MODULE_DEVICE_TABLE(isapnp, id_table);
#endif /* ISAPNP */
#endif /* !AHA152X_PCMCIA */
static const struct scsi_host_template aha152x_driver_template;
/*
* internal states of the host
*
*/
enum aha152x_state {
idle=0 ,
unknown,
seldo,
seldi,
selto,
busfree,
msgo,
cmd,
msgi,
status,
datai,
datao,
parerr,
rsti,
maxstate
};
/*
* current state information of the host
*
*/
struct aha152x_hostdata {
struct scsi_cmnd *issue_SC;
/* pending commands to issue */
struct scsi_cmnd *current_SC;
/* current command on the bus */
struct scsi_cmnd *disconnected_SC;
/* commands that disconnected */
struct scsi_cmnd *done_SC;
/* command that was completed */
spinlock_t lock;
/* host lock */
#if defined (AHA152X_STAT)
int total_commands;
int disconnections;
int busfree_without_any_action;
int busfree_without_old_command;
int busfree_without_new_command;
int busfree_without_done_command;
int busfree_with_check_condition;
int count[maxstate];
int count_trans[maxstate];
unsigned long time[maxstate];
#endif
int commands; /* current number of commands */
int reconnect; /* disconnection allowed */
int parity; /* parity checking enabled */
int synchronous; /* synchronous transferes enabled */
int delay; /* reset out delay */
int ext_trans; /* extended translation enabled */
int swint; /* software-interrupt was fired during detect() */
int service; /* bh needs to be run */
int in_intr; /* bh is running */
/* current state,
previous state ,
last state different from current state */
enum aha152x_state state, prevstate, laststate;
int target;
/* reconnecting target */
unsigned char syncrate[8 ];
/* current synchronous transfer agreements */
unsigned char syncneg[8 ];
/* 0: no negotiation;
* 1 : negotiation in progress ;
* 2 : negotiation completed
*/
int cmd_i;
/* number of sent bytes of current command */
int msgi_len;
/* number of received message bytes */
unsigned char msgi[256 ];
/* received message bytes */
int msgo_i, msgo_len;
/* number of sent bytes and length of current messages */
unsigned char msgo[256 ];
/* pending messages */
int data_len;
/* number of sent/received bytes in dataphase */
unsigned long io_port0;
unsigned long io_port1;
#ifdef __ISAPNP__
struct pnp_dev *pnpdev;
#endif
struct list_head host_list;
};
/*
* host specific command extension
*
*/
struct aha152x_scdata {
struct scsi_cmnd *next; /* next sc in queue */
struct completion *done;/* semaphore to block on */
struct scsi_eh_save ses;
};
/* access macros for hostdata */
#define HOSTDATA(shpnt) ((struct aha152x_hostdata *) &shpnt->hostdata)
#define HOSTNO ((shpnt)->host_no)
#define CURRENT_SC (HOSTDATA(shpnt)->current_SC)
#define DONE_SC (HOSTDATA(shpnt)->done_SC)
#define ISSUE_SC (HOSTDATA(shpnt)->issue_SC)
#define DISCONNECTED_SC (HOSTDATA(shpnt)->disconnected_SC)
#define QLOCK (HOSTDATA(shpnt)->lock)
#define QLOCKER (HOSTDATA(shpnt)->locker)
#define QLOCKERL (HOSTDATA(shpnt)->lockerl)
#define STATE (HOSTDATA(shpnt)->state)
#define PREVSTATE (HOSTDATA(shpnt)->prevstate)
#define LASTSTATE (HOSTDATA(shpnt)->laststate)
#define RECONN_TARGET (HOSTDATA(shpnt)->target)
#define CMD_I (HOSTDATA(shpnt)->cmd_i)
#define MSGO(i) (HOSTDATA(shpnt)->msgo[i])
#define MSGO_I (HOSTDATA(shpnt)->msgo_i)
#define MSGOLEN (HOSTDATA(shpnt)->msgo_len)
#define ADDMSGO(x) (MSGOLEN<256 ? (void )(MSGO(MSGOLEN++)=x) : aha152x_error(shpnt,"MSGO overflow" ))
#define MSGI(i) (HOSTDATA(shpnt)->msgi[i])
#define MSGILEN (HOSTDATA(shpnt)->msgi_len)
#define ADDMSGI(x) (MSGILEN<256 ? (void )(MSGI(MSGILEN++)=x) : aha152x_error(shpnt,"MSGI overflow" ))
#define DATA_LEN (HOSTDATA(shpnt)->data_len)
#define SYNCRATE (HOSTDATA(shpnt)->syncrate[CURRENT_SC->device->id])
#define SYNCNEG (HOSTDATA(shpnt)->syncneg[CURRENT_SC->device->id])
#define DELAY (HOSTDATA(shpnt)->delay)
#define EXT_TRANS (HOSTDATA(shpnt)->ext_trans)
#define TC1550 (HOSTDATA(shpnt)->tc1550)
#define RECONNECT (HOSTDATA(shpnt)->reconnect)
#define PARITY (HOSTDATA(shpnt)->parity)
#define SYNCHRONOUS (HOSTDATA(shpnt)->synchronous)
#define HOSTIOPORT0 (HOSTDATA(shpnt)->io_port0)
#define HOSTIOPORT1 (HOSTDATA(shpnt)->io_port1)
#define SCDATA(SCpnt) ((struct aha152x_scdata *) (SCpnt)->host_scribble)
#define SCNEXT(SCpnt) SCDATA(SCpnt)->next
#define SCSEM(SCpnt) SCDATA(SCpnt)->done
#define SG_ADDRESS(buffer) ((char *) sg_virt((buffer)))
/* state handling */
static void seldi_run(struct Scsi_Host *shpnt);
static void seldo_run(struct Scsi_Host *shpnt);
static void selto_run(struct Scsi_Host *shpnt);
static void busfree_run(struct Scsi_Host *shpnt);
static void msgo_init(struct Scsi_Host *shpnt);
static void msgo_run(struct Scsi_Host *shpnt);
static void msgo_end(struct Scsi_Host *shpnt);
static void cmd_init(struct Scsi_Host *shpnt);
static void cmd_run(struct Scsi_Host *shpnt);
static void cmd_end(struct Scsi_Host *shpnt);
static void datai_init(struct Scsi_Host *shpnt);
static void datai_run(struct Scsi_Host *shpnt);
static void datai_end(struct Scsi_Host *shpnt);
static void datao_init(struct Scsi_Host *shpnt);
static void datao_run(struct Scsi_Host *shpnt);
static void datao_end(struct Scsi_Host *shpnt);
static void status_run(struct Scsi_Host *shpnt);
static void msgi_run(struct Scsi_Host *shpnt);
static void msgi_end(struct Scsi_Host *shpnt);
static void parerr_run(struct Scsi_Host *shpnt);
static void rsti_run(struct Scsi_Host *shpnt);
static void is_complete(struct Scsi_Host *shpnt);
/*
* driver states
*
*/
static struct {
char *name;
void (*init)(struct Scsi_Host *);
void (*run)(struct Scsi_Host *);
void (*end)(struct Scsi_Host *);
int spio;
} states[] = {
{ "idle" , NULL, NULL, NULL, 0 },
{ "unknown" , NULL, NULL, NULL, 0 },
{ "seldo" , NULL, seldo_run, NULL, 0 },
{ "seldi" , NULL, seldi_run, NULL, 0 },
{ "selto" , NULL, selto_run, NULL, 0 },
{ "busfree" , NULL, busfree_run, NULL, 0 },
{ "msgo" , msgo_init, msgo_run, msgo_end, 1 },
{ "cmd" , cmd_init, cmd_run, cmd_end, 1 },
{ "msgi" , NULL, msgi_run, msgi_end, 1 },
{ "status" , NULL, status_run, NULL, 1 },
{ "datai" , datai_init, datai_run, datai_end, 0 },
{ "datao" , datao_init, datao_run, datao_end, 0 },
{ "parerr" , NULL, parerr_run, NULL, 0 },
{ "rsti" , NULL, rsti_run, NULL, 0 },
};
/* setup & interrupt */
static irqreturn_t intr(int irq, void *dev_id);
static void reset_ports(struct Scsi_Host *shpnt);
static void aha152x_error(struct Scsi_Host *shpnt, char *msg);
static void done(struct Scsi_Host *shpnt, unsigned char status_byte,
unsigned char host_byte);
/* diagnostics */
static void show_command(struct scsi_cmnd * ptr);
static void show_queues(struct Scsi_Host *shpnt);
static void disp_enintr(struct Scsi_Host *shpnt);
/*
* queue services :
*
*/
static inline void append_SC(struct scsi_cmnd **SC, struct scsi_cmnd *new_SC)
{
struct scsi_cmnd *end;
SCNEXT(new_SC) = NULL;
if (!*SC)
*SC = new_SC;
else {
for (end = *SC; SCNEXT(end); end = SCNEXT(end))
;
SCNEXT(end) = new_SC;
}
}
static inline struct scsi_cmnd *remove_first_SC(struct scsi_cmnd ** SC)
{
struct scsi_cmnd *ptr;
ptr = *SC;
if (ptr) {
*SC = SCNEXT(*SC);
SCNEXT(ptr)=NULL;
}
return ptr;
}
static inline struct scsi_cmnd *remove_lun_SC(struct scsi_cmnd ** SC,
int target, int lun)
{
struct scsi_cmnd *ptr, *prev;
for (ptr = *SC, prev = NULL;
ptr && ((ptr->device->id != target) || (ptr->device->lun != lun));
prev = ptr, ptr = SCNEXT(ptr))
;
if (ptr) {
if (prev)
SCNEXT(prev) = SCNEXT(ptr);
else
*SC = SCNEXT(ptr);
SCNEXT(ptr)=NULL;
}
return ptr;
}
static inline struct scsi_cmnd *remove_SC(struct scsi_cmnd **SC,
struct scsi_cmnd *SCp)
{
struct scsi_cmnd *ptr, *prev;
for (ptr = *SC, prev = NULL;
ptr && SCp!=ptr;
prev = ptr, ptr = SCNEXT(ptr))
;
if (ptr) {
if (prev)
SCNEXT(prev) = SCNEXT(ptr);
else
*SC = SCNEXT(ptr);
SCNEXT(ptr)=NULL;
}
return ptr;
}
static irqreturn_t swintr(int irqno, void *dev_id)
{
struct Scsi_Host *shpnt = dev_id;
HOSTDATA(shpnt)->swint++;
SETPORT(DMACNTRL0, INTEN);
return IRQ_HANDLED;
}
struct Scsi_Host *aha152x_probe_one(struct aha152x_setup *setup)
{
struct Scsi_Host *shpnt;
shpnt = scsi_host_alloc(&aha152x_driver_template, sizeof (struct aha152x_hostdata));
if (!shpnt) {
printk(KERN_ERR "aha152x: scsi_host_alloc failed\n" );
return NULL;
}
memset(HOSTDATA(shpnt), 0 , sizeof *HOSTDATA(shpnt));
INIT_LIST_HEAD(&HOSTDATA(shpnt)->host_list);
/* need to have host registered before triggering any interrupt */
list_add_tail(&HOSTDATA(shpnt)->host_list, &aha152x_host_list);
shpnt->io_port = setup->io_port;
shpnt->n_io_port = IO_RANGE;
shpnt->irq = setup->irq;
if (!setup->tc1550) {
HOSTIOPORT0 = setup->io_port;
HOSTIOPORT1 = setup->io_port;
} else {
HOSTIOPORT0 = setup->io_port+0 x10;
HOSTIOPORT1 = setup->io_port-0 x10;
}
spin_lock_init(&QLOCK);
RECONNECT = setup->reconnect;
SYNCHRONOUS = setup->synchronous;
PARITY = setup->parity;
DELAY = setup->delay;
EXT_TRANS = setup->ext_trans;
SETPORT(SCSIID, setup->scsiid << 4 );
shpnt->this_id = setup->scsiid;
if (setup->reconnect)
shpnt->can_queue = AHA152X_MAXQUEUE;
/* RESET OUT */
printk("aha152x: resetting bus...\n" );
SETPORT(SCSISEQ, SCSIRSTO);
mdelay(256 );
SETPORT(SCSISEQ, 0 );
mdelay(DELAY);
reset_ports(shpnt);
printk(KERN_INFO
"aha152x%d%s: "
"vital data: rev=%x, "
"io=0x%03lx (0x%03lx/0x%03lx), "
"irq=%d, "
"scsiid=%d, "
"reconnect=%s, "
"parity=%s, "
"synchronous=%s, "
"delay=%d, "
"extended translation=%s\n" ,
shpnt->host_no, setup->tc1550 ? " (tc1550 mode)" : "" ,
GETPORT(REV) & 0 x7,
shpnt->io_port, HOSTIOPORT0, HOSTIOPORT1,
shpnt->irq,
shpnt->this_id,
RECONNECT ? "enabled" : "disabled" ,
PARITY ? "enabled" : "disabled" ,
SYNCHRONOUS ? "enabled" : "disabled" ,
DELAY,
EXT_TRANS ? "enabled" : "disabled" );
/* not expecting any interrupts */
SETPORT(SIMODE0, 0 );
SETPORT(SIMODE1, 0 );
if (request_irq(shpnt->irq, swintr, IRQF_SHARED, "aha152x" , shpnt)) {
printk(KERN_ERR "aha152x%d: irq %d busy.\n" , shpnt->host_no, shpnt->irq);
goto out_host_put;
}
HOSTDATA(shpnt)->swint = 0 ;
printk(KERN_INFO "aha152x%d: trying software interrupt, " , shpnt->host_no);
mb();
SETPORT(DMACNTRL0, SWINT|INTEN);
mdelay(1000 );
free_irq(shpnt->irq, shpnt);
if (!HOSTDATA(shpnt)->swint) {
if (TESTHI(DMASTAT, INTSTAT)) {
printk("lost.\n" );
} else {
printk("failed.\n" );
}
SETPORT(DMACNTRL0, INTEN);
printk(KERN_ERR "aha152x%d: irq %d possibly wrong. "
"Please verify.\n" , shpnt->host_no, shpnt->irq);
goto out_host_put;
}
printk("ok.\n" );
/* clear interrupts */
SETPORT(SSTAT0, 0 x7f);
SETPORT(SSTAT1, 0 xef);
if (request_irq(shpnt->irq, intr, IRQF_SHARED, "aha152x" , shpnt)) {
printk(KERN_ERR "aha152x%d: failed to reassign irq %d.\n" , shpnt->host_no, shpnt->irq);
goto out_host_put;
}
if ( scsi_add_host(shpnt, NULL) ) {
free_irq(shpnt->irq, shpnt);
printk(KERN_ERR "aha152x%d: failed to add host.\n" , shpnt->host_no);
goto out_host_put;
}
scsi_scan_host(shpnt);
return shpnt;
out_host_put:
list_del(&HOSTDATA(shpnt)->host_list);
scsi_host_put(shpnt);
return NULL;
}
void aha152x_release(struct Scsi_Host *shpnt)
{
if (!shpnt)
return ;
scsi_remove_host(shpnt);
if (shpnt->irq)
free_irq(shpnt->irq, shpnt);
#if !defined (AHA152X_PCMCIA)
if (shpnt->io_port)
release_region(shpnt->io_port, IO_RANGE);
#endif
#ifdef __ISAPNP__
if (HOSTDATA(shpnt)->pnpdev)
pnp_device_detach(HOSTDATA(shpnt)->pnpdev);
#endif
list_del(&HOSTDATA(shpnt)->host_list);
scsi_host_put(shpnt);
}
/*
* setup controller to generate interrupts depending
* on current state ( lock has to be acquired )
*
*/
static int setup_expected_interrupts(struct Scsi_Host *shpnt)
{
if (CURRENT_SC) {
struct aha152x_cmd_priv *acp = aha152x_priv(CURRENT_SC);
acp->phase |= 1 << 16 ;
if (acp->phase & selecting) {
SETPORT(SSTAT1, SELTO);
SETPORT(SIMODE0, ENSELDO | (DISCONNECTED_SC ? ENSELDI : 0 ));
SETPORT(SIMODE1, ENSELTIMO);
} else {
SETPORT(SIMODE0, (acp->phase & spiordy) ? ENSPIORDY : 0 );
SETPORT(SIMODE1, ENPHASEMIS | ENSCSIRST | ENSCSIPERR | ENBUSFREE);
}
} else if (STATE==seldi) {
SETPORT(SIMODE0, 0 );
SETPORT(SIMODE1, ENPHASEMIS | ENSCSIRST | ENSCSIPERR | ENBUSFREE);
} else {
SETPORT(SIMODE0, DISCONNECTED_SC ? ENSELDI : 0 );
SETPORT(SIMODE1, ENSCSIRST | ( (ISSUE_SC||DONE_SC) ? ENBUSFREE : 0 ));
}
if (!HOSTDATA(shpnt)->in_intr)
SETBITS(DMACNTRL0, INTEN);
return TESTHI(DMASTAT, INTSTAT);
}
/*
* Queue a command and setup interrupts for a free bus .
*/
static int aha152x_internal_queue(struct scsi_cmnd *SCpnt,
struct completion *complete, int phase)
{
struct aha152x_cmd_priv *acp = aha152x_priv(SCpnt);
struct Scsi_Host *shpnt = SCpnt->device->host;
unsigned long flags;
acp->phase = not_issued | phase;
acp->status = 0 x1; /* Illegal status by SCSI standard */
acp->message = 0 ;
acp->sent_command = 0 ;
if (acp->phase & (resetting | check_condition)) {
if (!SCpnt->host_scribble || SCSEM(SCpnt) || SCNEXT(SCpnt)) {
scmd_printk(KERN_ERR, SCpnt, "cannot reuse command\n" );
return FAILED;
}
} else {
SCpnt->host_scribble = kmalloc(sizeof (struct aha152x_scdata), GFP_ATOMIC);
if (!SCpnt->host_scribble) {
scmd_printk(KERN_ERR, SCpnt, "allocation failed\n" );
return FAILED;
}
}
SCNEXT(SCpnt) = NULL;
SCSEM(SCpnt) = complete;
/* setup scratch area
SCp . ptr : buffer pointer
SCp . this_residual : buffer length
SCp . buffer : next buffer
SCp.phase : current state of the command */
if ((phase & resetting) || !scsi_sglist(SCpnt)) {
acp->ptr = NULL;
acp->this_residual = 0 ;
scsi_set_resid(SCpnt, 0 );
acp->buffer = NULL;
} else {
scsi_set_resid(SCpnt, scsi_bufflen(SCpnt));
acp->buffer = scsi_sglist(SCpnt);
acp->ptr = SG_ADDRESS(acp->buffer);
acp->this_residual = acp->buffer->length;
}
DO_LOCK(flags);
#if defined (AHA152X_STAT)
HOSTDATA(shpnt)->total_commands++;
#endif
/* Turn led on, when this is the first command. */
HOSTDATA(shpnt)->commands++;
if (HOSTDATA(shpnt)->commands==1 )
SETPORT(PORTA, 1 );
append_SC(&ISSUE_SC, SCpnt);
if (!HOSTDATA(shpnt)->in_intr)
setup_expected_interrupts(shpnt);
DO_UNLOCK(flags);
return 0 ;
}
/*
* queue a command
*
*/
static int aha152x_queue_lck(struct scsi_cmnd *SCpnt)
{
return aha152x_internal_queue(SCpnt, NULL, 0 );
}
static DEF_SCSI_QCMD(aha152x_queue)
/*
*
*/
static void reset_done(struct scsi_cmnd *SCpnt)
{
if (SCSEM(SCpnt)) {
complete(SCSEM(SCpnt));
} else {
printk(KERN_ERR "aha152x: reset_done w/o completion\n" );
}
}
static void aha152x_scsi_done(struct scsi_cmnd *SCpnt)
{
if (aha152x_priv(SCpnt)->phase & resetting)
reset_done(SCpnt);
else
scsi_done(SCpnt);
}
/*
* Abort a command
*
*/
static int aha152x_abort(struct scsi_cmnd *SCpnt)
{
struct Scsi_Host *shpnt = SCpnt->device->host;
struct scsi_cmnd *ptr;
unsigned long flags;
DO_LOCK(flags);
ptr=remove_SC(&ISSUE_SC, SCpnt);
if (ptr) {
HOSTDATA(shpnt)->commands--;
if (!HOSTDATA(shpnt)->commands)
SETPORT(PORTA, 0 );
DO_UNLOCK(flags);
kfree(SCpnt->host_scribble);
SCpnt->host_scribble=NULL;
return SUCCESS;
}
DO_UNLOCK(flags);
/*
* FIXME :
* for current command : queue ABORT for message out and raise ATN
* for disconnected command : pseudo SC with ABORT message or ABORT on reselection ?
*
*/
scmd_printk(KERN_ERR, SCpnt,
"cannot abort running or disconnected command\n" );
return FAILED;
}
/*
* Reset a device
*
*/
static int aha152x_device_reset(struct scsi_cmnd * SCpnt)
{
struct Scsi_Host *shpnt = SCpnt->device->host;
DECLARE_COMPLETION_ONSTACK(done);
int ret, issued, disconnected;
unsigned char old_cmd_len = SCpnt->cmd_len;
unsigned long flags;
unsigned long timeleft;
if (CURRENT_SC==SCpnt) {
scmd_printk(KERN_ERR, SCpnt, "cannot reset current device\n" );
return FAILED;
}
DO_LOCK(flags);
issued = remove_SC(&ISSUE_SC, SCpnt) == NULL;
disconnected = issued && remove_SC(&DISCONNECTED_SC, SCpnt);
DO_UNLOCK(flags);
SCpnt->cmd_len = 0 ;
aha152x_internal_queue(SCpnt, &done, resetting);
timeleft = wait_for_completion_timeout(&done, 100 *HZ);
if (!timeleft) {
/* remove command from issue queue */
DO_LOCK(flags);
remove_SC(&ISSUE_SC, SCpnt);
DO_UNLOCK(flags);
}
SCpnt->cmd_len = old_cmd_len;
DO_LOCK(flags);
if (aha152x_priv(SCpnt)->phase & resetted) {
HOSTDATA(shpnt)->commands--;
if (!HOSTDATA(shpnt)->commands)
SETPORT(PORTA, 0 );
kfree(SCpnt->host_scribble);
SCpnt->host_scribble=NULL;
ret = SUCCESS;
} else {
/* requeue */
if (!issued) {
append_SC(&ISSUE_SC, SCpnt);
} else if (disconnected) {
append_SC(&DISCONNECTED_SC, SCpnt);
}
ret = FAILED;
}
DO_UNLOCK(flags);
return ret;
}
static void free_hard_reset_SCs(struct Scsi_Host *shpnt,
struct scsi_cmnd **SCs)
{
struct scsi_cmnd *ptr;
ptr=*SCs;
while (ptr) {
struct scsi_cmnd *next;
if (SCDATA(ptr)) {
next = SCNEXT(ptr);
} else {
scmd_printk(KERN_DEBUG, ptr,
"queue corrupted at %p\n" , ptr);
next = NULL;
}
if (!ptr->device->soft_reset) {
remove_SC(SCs, ptr);
HOSTDATA(shpnt)->commands--;
kfree(ptr->host_scribble);
ptr->host_scribble=NULL;
}
ptr = next;
}
}
/*
* Reset the bus
*
* AIC - 6260 has a hard reset ( MRST signal ) , but apparently
* one cannot trigger it via software . So live with
* a soft reset ; no - one seemed to have cared .
*/
static int aha152x_bus_reset_host(struct Scsi_Host *shpnt)
{
unsigned long flags;
DO_LOCK(flags);
free_hard_reset_SCs(shpnt, &ISSUE_SC);
free_hard_reset_SCs(shpnt, &DISCONNECTED_SC);
SETPORT(SCSISEQ, SCSIRSTO);
mdelay(256 );
SETPORT(SCSISEQ, 0 );
mdelay(DELAY);
setup_expected_interrupts(shpnt);
if (HOSTDATA(shpnt)->commands==0 )
SETPORT(PORTA, 0 );
DO_UNLOCK(flags);
return SUCCESS;
}
/*
* Reset the bus
*
*/
static int aha152x_bus_reset(struct scsi_cmnd *SCpnt)
{
return aha152x_bus_reset_host(SCpnt->device->host);
}
/*
* Restore default values to the AIC - 6260 registers and reset the fifos
*
*/
static void reset_ports(struct Scsi_Host *shpnt)
{
unsigned long flags;
/* disable interrupts */
SETPORT(DMACNTRL0, RSTFIFO);
SETPORT(SCSISEQ, 0 );
SETPORT(SXFRCTL1, 0 );
SETPORT(SCSISIG, 0 );
SETRATE(0 );
/* clear all interrupt conditions */
SETPORT(SSTAT0, 0 x7f);
SETPORT(SSTAT1, 0 xef);
SETPORT(SSTAT4, SYNCERR | FWERR | FRERR);
SETPORT(DMACNTRL0, 0 );
SETPORT(DMACNTRL1, 0 );
SETPORT(BRSTCNTRL, 0 xf1);
/* clear SCSI fifos and transfer count */
SETPORT(SXFRCTL0, CH1|CLRCH1|CLRSTCNT);
SETPORT(SXFRCTL0, CH1);
DO_LOCK(flags);
setup_expected_interrupts(shpnt);
DO_UNLOCK(flags);
}
/*
* Reset the host ( bus and controller )
*
*/
int aha152x_host_reset_host(struct Scsi_Host *shpnt)
{
aha152x_bus_reset_host(shpnt);
reset_ports(shpnt);
return SUCCESS;
}
/*
* Return the " logical geometry "
*
*/
static int aha152x_biosparam(struct scsi_device *sdev, struct block_device *bdev,
sector_t capacity, int *info_array)
{
struct Scsi_Host *shpnt = sdev->host;
/* try default translation */
info_array[0 ] = 64 ;
info_array[1 ] = 32 ;
info_array[2 ] = (unsigned long )capacity / (64 * 32 );
/* for disks >1GB do some guessing */
if (info_array[2 ] >= 1024 ) {
int info[3 ];
/* try to figure out the geometry from the partition table */
if (scsicam_bios_param(bdev, capacity, info) < 0 ||
!((info[0 ] == 64 && info[1 ] == 32 ) || (info[0 ] == 255 && info[1 ] == 63 ))) {
if (EXT_TRANS) {
printk(KERN_NOTICE
"aha152x: unable to verify geometry for disk with >1GB.\n"
" using extended translation.\n" );
info_array[0 ] = 255 ;
info_array[1 ] = 63 ;
info_array[2 ] = (unsigned long )capacity / (255 * 63 );
} else {
printk(KERN_NOTICE
"aha152x: unable to verify geometry for disk with >1GB.\n"
" Using default translation. Please verify yourself.\n"
" Perhaps you need to enable extended translation in the driver.\n"
" See Documentation/scsi/aha152x.rst for details.\n" );
}
} else {
info_array[0 ] = info[0 ];
info_array[1 ] = info[1 ];
info_array[2 ] = info[2 ];
if (info[0 ] == 255 && !EXT_TRANS) {
printk(KERN_NOTICE
"aha152x: current partition table is using extended translation.\n"
" using it also, although it's not explicitly enabled.\n" );
}
}
}
return 0 ;
}
/*
* Internal done function
*
*/
static void done(struct Scsi_Host *shpnt, unsigned char status_byte,
unsigned char host_byte)
{
if (CURRENT_SC) {
if (DONE_SC)
scmd_printk(KERN_ERR, CURRENT_SC,
"there's already a completed command %p "
"- will cause abort\n" , DONE_SC);
DONE_SC = CURRENT_SC;
CURRENT_SC = NULL;
set_status_byte(DONE_SC, status_byte);
set_host_byte(DONE_SC, host_byte);
} else
printk(KERN_ERR "aha152x: done() called outside of command\n" );
}
static struct work_struct aha152x_tq;
/*
* Run service completions on the card with interrupts enabled .
*
*/
static void run(struct work_struct *work)
{
struct aha152x_hostdata *hd;
list_for_each_entry(hd, &aha152x_host_list, host_list) {
struct Scsi_Host *shost = container_of((void *)hd, struct Scsi_Host, hostdata);
is_complete(shost);
}
}
/*
* Interrupt handler
*
*/
static irqreturn_t intr(int irqno, void *dev_id)
{
struct Scsi_Host *shpnt = dev_id;
unsigned long flags;
unsigned char rev, dmacntrl0;
/*
* Read a couple of registers that are known to not be all 1 ' s . If
* we read all 1 ' s ( - 1 ) , that means that either :
*
* a . The host adapter chip has gone bad , and we cannot control it ,
* OR
* b . The host adapter is a PCMCIA card that has been ejected
*
* In either case , we cannot do anything with the host adapter at
* this point in time . So just ignore the interrupt and return .
* In the latter case , the interrupt might actually be meant for
* someone else sharing this IRQ , and that driver will handle it .
*/
rev = GETPORT(REV);
dmacntrl0 = GETPORT(DMACNTRL0);
if ((rev == 0 xFF) && (dmacntrl0 == 0 xFF))
return IRQ_NONE;
if ( TESTLO(DMASTAT, INTSTAT) )
return IRQ_NONE;
/* no more interrupts from the controller, while we're busy.
INTEN is restored by the BH handler */
CLRBITS(DMACNTRL0, INTEN);
DO_LOCK(flags);
if ( HOSTDATA(shpnt)->service==0 ) {
HOSTDATA(shpnt)->service=1 ;
/* Poke the BH handler */
INIT_WORK(&aha152x_tq, run);
schedule_work(&aha152x_tq);
}
DO_UNLOCK(flags);
return IRQ_HANDLED;
}
/*
* busfree phase
* - handle completition / disconnection / error of current command
* - start selection for next command ( if any )
*/
static void busfree_run(struct Scsi_Host *shpnt)
{
unsigned long flags;
#if defined (AHA152X_STAT)
int action=0 ;
#endif
SETPORT(SXFRCTL0, CH1|CLRCH1|CLRSTCNT);
SETPORT(SXFRCTL0, CH1);
SETPORT(SSTAT1, CLRBUSFREE);
if (CURRENT_SC) {
struct aha152x_cmd_priv *acp = aha152x_priv(CURRENT_SC);
#if defined (AHA152X_STAT)
action++;
#endif
acp->phase &= ~syncneg;
if (acp->phase & completed) {
/* target sent COMMAND COMPLETE */
done(shpnt, acp->status, DID_OK);
} else if (acp->phase & aborted) {
done(shpnt, acp->status, DID_ABORT);
} else if (acp->phase & resetted) {
done(shpnt, acp->status, DID_RESET);
} else if (acp->phase & disconnected) {
/* target sent DISCONNECT */
#if defined (AHA152X_STAT)
HOSTDATA(shpnt)->disconnections++;
#endif
append_SC(&DISCONNECTED_SC, CURRENT_SC);
acp->phase |= 1 << 16 ;
CURRENT_SC = NULL;
} else {
done(shpnt, SAM_STAT_GOOD, DID_ERROR);
}
#if defined (AHA152X_STAT)
} else {
HOSTDATA(shpnt)->busfree_without_old_command++;
#endif
}
DO_LOCK(flags);
if (DONE_SC) {
#if defined (AHA152X_STAT)
action++;
#endif
if (aha152x_priv(DONE_SC)->phase & check_condition) {
struct scsi_cmnd *cmd = HOSTDATA(shpnt)->done_SC;
struct aha152x_scdata *sc = SCDATA(cmd);
scsi_eh_restore_cmnd(cmd, &sc->ses);
aha152x_priv(cmd)->status = SAM_STAT_CHECK_CONDITION;
HOSTDATA(shpnt)->commands--;
if (!HOSTDATA(shpnt)->commands)
SETPORT(PORTA, 0 ); /* turn led off */
} else if (aha152x_priv(DONE_SC)->status == SAM_STAT_CHECK_CONDITION) {
#if defined (AHA152X_STAT)
HOSTDATA(shpnt)->busfree_with_check_condition++;
#endif
if (!(aha152x_priv(DONE_SC)->phase & not_issued)) {
struct aha152x_scdata *sc;
struct scsi_cmnd *ptr = DONE_SC;
DONE_SC=NULL;
sc = SCDATA(ptr);
/* It was allocated in aha152x_internal_queue? */
BUG_ON(!sc);
scsi_eh_prep_cmnd(ptr, &sc->ses, NULL, 0 , ~0 );
DO_UNLOCK(flags);
aha152x_internal_queue(ptr, NULL, check_condition);
DO_LOCK(flags);
}
}
if (DONE_SC) {
struct scsi_cmnd *ptr = DONE_SC;
DONE_SC=NULL;
/* turn led off, when no commands are in the driver */
HOSTDATA(shpnt)->commands--;
if (!HOSTDATA(shpnt)->commands)
SETPORT(PORTA, 0 ); /* turn led off */
if (!(aha152x_priv(ptr)->phase & resetting)) {
kfree(ptr->host_scribble);
ptr->host_scribble=NULL;
}
DO_UNLOCK(flags);
aha152x_scsi_done(ptr);
DO_LOCK(flags);
}
DONE_SC=NULL;
#if defined (AHA152X_STAT)
} else {
HOSTDATA(shpnt)->busfree_without_done_command++;
#endif
}
if (ISSUE_SC)
CURRENT_SC = remove_first_SC(&ISSUE_SC);
DO_UNLOCK(flags);
if (CURRENT_SC) {
struct aha152x_cmd_priv *acp = aha152x_priv(CURRENT_SC);
#if defined (AHA152X_STAT)
action++;
#endif
acp->phase |= selecting;
/* clear selection timeout */
SETPORT(SSTAT1, SELTO);
SETPORT(SCSIID, (shpnt->this_id << OID_) | CURRENT_SC->device->id);
SETPORT(SXFRCTL1, (PARITY ? ENSPCHK : 0 ) | ENSTIMER);
SETPORT(SCSISEQ, ENSELO | ENAUTOATNO | (DISCONNECTED_SC ? ENRESELI : 0 ));
} else {
#if defined (AHA152X_STAT)
HOSTDATA(shpnt)->busfree_without_new_command++;
#endif
SETPORT(SCSISEQ, DISCONNECTED_SC ? ENRESELI : 0 );
}
#if defined (AHA152X_STAT)
if (!action)
HOSTDATA(shpnt)->busfree_without_any_action++;
#endif
}
/*
* Selection done ( OUT )
* - queue IDENTIFY message and SDTR to selected target for message out
* ( ATN asserted automagically via ENAUTOATNO in busfree ( ) )
*/
static void seldo_run(struct Scsi_Host *shpnt)
{
struct aha152x_cmd_priv *acp = aha152x_priv(CURRENT_SC);
SETPORT(SCSISIG, 0 );
SETPORT(SSTAT1, CLRBUSFREE);
SETPORT(SSTAT1, CLRPHASECHG);
acp->phase &= ~(selecting | not_issued);
SETPORT(SCSISEQ, 0 );
if (TESTLO(SSTAT0, SELDO)) {
scmd_printk(KERN_ERR, CURRENT_SC,
"aha152x: passing bus free condition\n" );
done(shpnt, SAM_STAT_GOOD, DID_NO_CONNECT);
return ;
}
SETPORT(SSTAT0, CLRSELDO);
ADDMSGO(IDENTIFY(RECONNECT, CURRENT_SC->device->lun));
if (acp->phase & aborting) {
ADDMSGO(ABORT);
} else if (acp->phase & resetting) {
ADDMSGO(BUS_DEVICE_RESET);
} else if (SYNCNEG==0 && SYNCHRONOUS) {
acp->phase |= syncneg;
MSGOLEN += spi_populate_sync_msg(&MSGO(MSGOLEN), 50 , 8 );
SYNCNEG=1 ; /* negotiation in progress */
}
SETRATE(SYNCRATE);
}
/*
* Selection timeout
* - return command to mid - level with failure cause
*
*/
static void selto_run(struct Scsi_Host *shpnt)
{
struct aha152x_cmd_priv *acp;
SETPORT(SCSISEQ, 0 );
SETPORT(SSTAT1, CLRSELTIMO);
if (!CURRENT_SC)
return ;
acp = aha152x_priv(CURRENT_SC);
acp->phase &= ~selecting;
if (acp->phase & aborted)
done(shpnt, SAM_STAT_GOOD, DID_ABORT);
else if (TESTLO(SSTAT0, SELINGO))
done(shpnt, SAM_STAT_GOOD, DID_BUS_BUSY);
else
/* ARBITRATION won, but SELECTION failed */
done(shpnt, SAM_STAT_GOOD, DID_NO_CONNECT);
}
/*
* Selection in done
* - put current command back to issue queue
* ( reconnection of a disconnected nexus instead
* of successful selection out )
*
*/
static void seldi_run(struct Scsi_Host *shpnt)
{
int selid;
int target;
unsigned long flags;
SETPORT(SCSISIG, 0 );
SETPORT(SSTAT0, CLRSELDI);
SETPORT(SSTAT1, CLRBUSFREE);
SETPORT(SSTAT1, CLRPHASECHG);
if (CURRENT_SC) {
struct aha152x_cmd_priv *acp = aha152x_priv(CURRENT_SC);
if (!(acp->phase & not_issued))
scmd_printk(KERN_ERR, CURRENT_SC,
"command should not have been issued yet\n" );
DO_LOCK(flags);
append_SC(&ISSUE_SC, CURRENT_SC);
DO_UNLOCK(flags);
CURRENT_SC = NULL;
}
if (!DISCONNECTED_SC)
return ;
RECONN_TARGET=-1 ;
selid = GETPORT(SELID) & ~(1 << shpnt->this_id);
if (selid==0 ) {
shost_printk(KERN_INFO, shpnt,
"target id unknown (%02x)\n" , selid);
return ;
}
for (target=7 ; !(selid & (1 << target)); target--)
;
if (selid & ~(1 << target)) {
shost_printk(KERN_INFO, shpnt,
"multiple targets reconnected (%02x)\n" , selid);
}
SETPORT(SCSIID, (shpnt->this_id << OID_) | target);
SETPORT(SCSISEQ, 0 );
SETRATE(HOSTDATA(shpnt)->syncrate[target]);
RECONN_TARGET=target;
}
/*
* message in phase
* - handle initial message after reconnection to identify
* reconnecting nexus
* - queue command on DISCONNECTED_SC on DISCONNECT message
* - set completed flag on COMMAND COMPLETE
* ( other completition code moved to busfree_run )
* - handle response to SDTR
* - clear synchronous transfer agreements on BUS RESET
*
* FIXME : what about SAVE POINTERS , RESTORE POINTERS ?
*
*/
static void msgi_run(struct Scsi_Host *shpnt)
{
for (;;) {
struct aha152x_cmd_priv *acp;
int sstat1 = GETPORT(SSTAT1);
if (sstat1 & (PHASECHG|PHASEMIS|BUSFREE) || !(sstat1 & REQINIT))
return ;
if (TESTLO(SSTAT0, SPIORDY))
return ;
ADDMSGI(GETPORT(SCSIDAT));
if (!CURRENT_SC) {
if (LASTSTATE!=seldi) {
shost_printk(KERN_ERR, shpnt,
"message in w/o current command"
" not after reselection\n" );
}
/*
* Handle reselection
*/
if (!(MSGI(0 ) & IDENTIFY_BASE)) {
shost_printk(KERN_ERR, shpnt,
"target didn't identify after reselection\n" );
continue ;
}
CURRENT_SC = remove_lun_SC(&DISCONNECTED_SC, RECONN_TARGET, MSGI(0 ) & 0 x3f);
if (!CURRENT_SC) {
show_queues(shpnt);
shost_printk(KERN_ERR, shpnt,
"no disconnected command"
" for target %d/%d\n" ,
RECONN_TARGET, MSGI(0 ) & 0 x3f);
continue ;
}
acp = aha152x_priv(CURRENT_SC);
acp->message = MSGI(0 );
acp->phase &= ~disconnected;
MSGILEN=0 ;
/* next message if any */
continue ;
}
acp = aha152x_priv(CURRENT_SC);
acp->message = MSGI(0 );
switch (MSGI(0 )) {
case DISCONNECT:
if (!RECONNECT)
scmd_printk(KERN_WARNING, CURRENT_SC,
"target was not allowed to disconnect\n" );
acp->phase |= disconnected;
break ;
case COMMAND_COMPLETE:
acp->phase |= completed;
break ;
case MESSAGE_REJECT:
if (SYNCNEG==1 ) {
scmd_printk(KERN_INFO, CURRENT_SC,
"Synchronous Data Transfer Request"
" was rejected\n" );
SYNCNEG=2 ; /* negotiation completed */
} else
scmd_printk(KERN_INFO, CURRENT_SC,
"inbound message (MESSAGE REJECT)\n" );
break ;
case SAVE_POINTERS:
break ;
case RESTORE_POINTERS:
break ;
case EXTENDED_MESSAGE:
if (MSGILEN<2 || MSGILEN<MSGI(1 )+2 ) {
/* not yet completed */
continue ;
}
switch (MSGI(2 )) {
case EXTENDED_SDTR:
{
long ticks;
if (MSGI(1 ) != 3 ) {
scmd_printk(KERN_ERR, CURRENT_SC,
"SDTR message length!=3\n" );
break ;
}
if (!HOSTDATA(shpnt)->synchronous)
break ;
printk(INFO_LEAD, CMDINFO(CURRENT_SC));
spi_print_msg(&MSGI(0 ));
printk("\n" );
ticks = (MSGI(3 ) * 4 + 49 ) / 50 ;
if (syncneg) {
/* negotiation in progress */
if (ticks > 9 || MSGI(4 ) < 1 || MSGI(4 ) > 8 ) {
ADDMSGO(MESSAGE_REJECT);
scmd_printk(KERN_INFO,
CURRENT_SC,
"received Synchronous Data Transfer Request invalid - rejected\n" );
break ;
}
SYNCRATE |= ((ticks - 2 ) << 4 ) + MSGI(4 );
} else if (ticks <= 9 && MSGI(4 ) >= 1 ) {
ADDMSGO(EXTENDED_MESSAGE);
ADDMSGO(3 );
ADDMSGO(EXTENDED_SDTR);
if (ticks < 4 ) {
ticks = 4 ;
ADDMSGO(50 );
} else
ADDMSGO(MSGI(3 ));
if (MSGI(4 ) > 8 )
MSGI(4 ) = 8 ;
ADDMSGO(MSGI(4 ));
SYNCRATE |= ((ticks - 2 ) << 4 ) + MSGI(4 );
} else {
/* requested SDTR is too slow, do it asynchronously */
scmd_printk(KERN_INFO,
CURRENT_SC,
"Synchronous Data Transfer Request too slow - Rejecting\n" );
ADDMSGO(MESSAGE_REJECT);
}
/* negotiation completed */
SYNCNEG=2 ;
SETRATE(SYNCRATE);
}
break ;
case BUS_DEVICE_RESET:
{
int i;
for (i=0 ; i<8 ; i++) {
HOSTDATA(shpnt)->syncrate[i]=0 ;
HOSTDATA(shpnt)->syncneg[i]=0 ;
}
}
break ;
case EXTENDED_MODIFY_DATA_POINTER:
case EXTENDED_EXTENDED_IDENTIFY:
case EXTENDED_WDTR:
default :
ADDMSGO(MESSAGE_REJECT);
break ;
}
break ;
}
MSGILEN=0 ;
}
}
static void msgi_end(struct Scsi_Host *shpnt)
{
if (MSGILEN>0 )
scmd_printk(KERN_WARNING, CURRENT_SC,
"target left before message completed (%d)\n" ,
MSGILEN);
if (MSGOLEN > 0 && !(GETPORT(SSTAT1) & BUSFREE))
SETPORT(SCSISIG, P_MSGI | SIG_ATNO);
}
/*
* message out phase
*
*/
static void msgo_init(struct Scsi_Host *shpnt)
{
if (MSGOLEN==0 ) {
if ((aha152x_priv(CURRENT_SC)->phase & syncneg) &&
SYNCNEG == 2 && SYNCRATE == 0 ) {
ADDMSGO(IDENTIFY(RECONNECT, CURRENT_SC->device->lun));
} else {
scmd_printk(KERN_INFO, CURRENT_SC,
"unexpected MESSAGE OUT phase; rejecting\n" );
ADDMSGO(MESSAGE_REJECT);
}
}
}
/*
* message out phase
*
*/
static void msgo_run(struct Scsi_Host *shpnt)
{
struct aha152x_cmd_priv *acp = aha152x_priv(CURRENT_SC);
while (MSGO_I<MSGOLEN) {
if (TESTLO(SSTAT0, SPIORDY))
return ;
if (MSGO_I==MSGOLEN-1 ) {
/* Leave MESSAGE OUT after transfer */
SETPORT(SSTAT1, CLRATNO);
}
if (MSGO(MSGO_I) & IDENTIFY_BASE)
acp->phase |= identified;
if (MSGO(MSGO_I)==ABORT)
acp->phase |= aborted;
if (MSGO(MSGO_I)==BUS_DEVICE_RESET)
acp->phase |= resetted;
SETPORT(SCSIDAT, MSGO(MSGO_I++));
}
}
static void msgo_end(struct Scsi_Host *shpnt)
{
if (MSGO_I<MSGOLEN) {
scmd_printk(KERN_ERR, CURRENT_SC,
"message sent incompletely (%d/%d)\n" ,
MSGO_I, MSGOLEN);
if (SYNCNEG==1 ) {
scmd_printk(KERN_INFO, CURRENT_SC,
"Synchronous Data Transfer Request was rejected\n" );
SYNCNEG=2 ;
}
}
MSGO_I = 0 ;
MSGOLEN = 0 ;
}
/*
* command phase
*
*/
static void cmd_init(struct Scsi_Host *shpnt)
{
if (aha152x_priv(CURRENT_SC)->sent_command) {
scmd_printk(KERN_ERR, CURRENT_SC,
"command already sent\n" );
done(shpnt, SAM_STAT_GOOD, DID_ERROR);
return ;
}
CMD_I=0 ;
}
/*
* command phase
*
*/
static void cmd_run(struct Scsi_Host *shpnt)
{
while (CMD_I<CURRENT_SC->cmd_len) {
if (TESTLO(SSTAT0, SPIORDY))
return ;
SETPORT(SCSIDAT, CURRENT_SC->cmnd[CMD_I++]);
}
}
static void cmd_end(struct Scsi_Host *shpnt)
{
if (CMD_I<CURRENT_SC->cmd_len)
scmd_printk(KERN_ERR, CURRENT_SC,
"command sent incompletely (%d/%d)\n" ,
CMD_I, CURRENT_SC->cmd_len);
else
aha152x_priv(CURRENT_SC)->sent_command++;
}
/*
* status phase
*
*/
static void status_run(struct Scsi_Host *shpnt)
{
if (TESTLO(SSTAT0, SPIORDY))
return ;
aha152x_priv(CURRENT_SC)->status = GETPORT(SCSIDAT);
}
/*
* data in phase
*
*/
static void datai_init(struct Scsi_Host *shpnt)
{
SETPORT(DMACNTRL0, RSTFIFO);
SETPORT(DMACNTRL0, RSTFIFO|ENDMA);
SETPORT(SXFRCTL0, CH1|CLRSTCNT);
SETPORT(SXFRCTL0, CH1|SCSIEN|DMAEN);
SETPORT(SIMODE0, 0 );
SETPORT(SIMODE1, ENSCSIPERR | ENSCSIRST | ENPHASEMIS | ENBUSFREE);
DATA_LEN=0 ;
}
static void datai_run(struct Scsi_Host *shpnt)
{
struct aha152x_cmd_priv *acp;
unsigned long the_time;
int fifodata, data_count;
/*
* loop while the phase persists or the fifos are not empty
*
*/
while (TESTLO(DMASTAT, INTSTAT) || TESTLO(DMASTAT, DFIFOEMP) || TESTLO(SSTAT2, SEMPTY)) {
/* FIXME: maybe this should be done by setting up
* STCNT to trigger ENSWRAP interrupt , instead of
* polling for DFIFOFULL
*/
the_time=jiffies + 100 *HZ;
while (TESTLO(DMASTAT, DFIFOFULL|INTSTAT) && time_before(jiffies,the_time))
barrier();
if (TESTLO(DMASTAT, DFIFOFULL|INTSTAT)) {
scmd_printk(KERN_ERR, CURRENT_SC, "datai timeout\n" );
break ;
}
if (TESTHI(DMASTAT, DFIFOFULL)) {
fifodata = 128 ;
} else {
the_time=jiffies + 100 *HZ;
while (TESTLO(SSTAT2, SEMPTY) && time_before(jiffies,the_time))
barrier();
if (TESTLO(SSTAT2, SEMPTY)) {
scmd_printk(KERN_ERR, CURRENT_SC,
"datai sempty timeout" );
break ;
}
fifodata = GETPORT(FIFOSTAT);
}
acp = aha152x_priv(CURRENT_SC);
if (acp->this_residual > 0 ) {
while (fifodata > 0 && acp->this_residual > 0 ) {
data_count = fifodata > acp->this_residual ?
acp->this_residual : fifodata;
fifodata -= data_count;
if (data_count & 1 ) {
SETPORT(DMACNTRL0, ENDMA|_8 BIT);
*acp->ptr++ = GETPORT(DATAPORT);
acp->this_residual--;
DATA_LEN++;
SETPORT(DMACNTRL0, ENDMA);
}
if (data_count > 1 ) {
data_count >>= 1 ;
insw(DATAPORT, acp->ptr, data_count);
acp->ptr += 2 * data_count;
acp->this_residual -= 2 * data_count;
DATA_LEN += 2 * data_count;
}
if (acp->this_residual == 0 &&
!sg_is_last(acp->buffer)) {
/* advance to next buffer */
acp->buffer = sg_next(acp->buffer);
acp->ptr = SG_ADDRESS(acp->buffer);
acp->this_residual = acp->buffer->length;
}
}
} else if (fifodata > 0 ) {
scmd_printk(KERN_ERR, CURRENT_SC,
"no buffers left for %d(%d) bytes"
" (data overrun!?)\n" ,
fifodata, GETPORT(FIFOSTAT));
SETPORT(DMACNTRL0, ENDMA|_8 BIT);
while (fifodata>0 ) {
GETPORT(DATAPORT);
fifodata--;
DATA_LEN++;
}
SETPORT(DMACNTRL0, ENDMA|_8 BIT);
}
}
if (TESTLO(DMASTAT, INTSTAT) ||
TESTLO(DMASTAT, DFIFOEMP) ||
TESTLO(SSTAT2, SEMPTY) ||
GETPORT(FIFOSTAT)>0 ) {
/*
* something went wrong , if there ' s something left in the fifos
* or the phase didn ' t change
*/
scmd_printk(KERN_ERR, CURRENT_SC,
"fifos should be empty and phase should have changed\n" );
}
if (DATA_LEN!=GETSTCNT()) {
scmd_printk(KERN_ERR, CURRENT_SC,
"manual transfer count differs from automatic "
"(count=%d;stcnt=%d;diff=%d;fifostat=%d)" ,
DATA_LEN, GETSTCNT(), GETSTCNT()-DATA_LEN,
GETPORT(FIFOSTAT));
mdelay(10000 );
}
}
static void datai_end(struct Scsi_Host *shpnt)
{
CMD_INC_RESID(CURRENT_SC, -GETSTCNT());
SETPORT(SXFRCTL0, CH1|CLRSTCNT);
SETPORT(DMACNTRL0, 0 );
}
/*
* data out phase
*
*/
static void datao_init(struct Scsi_Host *shpnt)
{
SETPORT(DMACNTRL0, WRITE_READ | RSTFIFO);
SETPORT(DMACNTRL0, WRITE_READ | ENDMA);
SETPORT(SXFRCTL0, CH1|CLRSTCNT);
SETPORT(SXFRCTL0, CH1|SCSIEN|DMAEN);
SETPORT(SIMODE0, 0 );
SETPORT(SIMODE1, ENSCSIPERR | ENSCSIRST | ENPHASEMIS | ENBUSFREE );
DATA_LEN = scsi_get_resid(CURRENT_SC);
}
static void datao_run(struct Scsi_Host *shpnt)
{
struct aha152x_cmd_priv *acp = aha152x_priv(CURRENT_SC);
unsigned long the_time;
int data_count;
/* until phase changes or all data sent */
while (TESTLO(DMASTAT, INTSTAT) && acp->this_residual > 0 ) {
data_count = 128 ;
if (data_count > acp->this_residual)
data_count = acp->this_residual;
if (TESTLO(DMASTAT, DFIFOEMP)) {
scmd_printk(KERN_ERR, CURRENT_SC,
"datao fifo not empty (%d)" ,
GETPORT(FIFOSTAT));
break ;
}
if (data_count & 1 ) {
SETPORT(DMACNTRL0,WRITE_READ|ENDMA|_8 BIT);
SETPORT(DATAPORT, *acp->ptr++);
acp->this_residual--;
CMD_INC_RESID(CURRENT_SC, -1 );
SETPORT(DMACNTRL0,WRITE_READ|ENDMA);
}
if (data_count > 1 ) {
data_count >>= 1 ;
outsw(DATAPORT, acp->ptr, data_count);
acp->ptr += 2 * data_count;
acp->this_residual -= 2 * data_count;
CMD_INC_RESID(CURRENT_SC, -2 * data_count);
}
if (acp->this_residual == 0 && !sg_is_last(acp->buffer)) {
/* advance to next buffer */
acp->buffer = sg_next(acp->buffer);
acp->ptr = SG_ADDRESS(acp->buffer);
acp->this_residual = acp->buffer->length;
}
the_time=jiffies + 100 *HZ;
while (TESTLO(DMASTAT, DFIFOEMP|INTSTAT) && time_before(jiffies,the_time))
barrier();
if (TESTLO(DMASTAT, DFIFOEMP|INTSTAT)) {
scmd_printk(KERN_ERR, CURRENT_SC, "dataout timeout\n" );
break ;
}
}
}
static void datao_end(struct Scsi_Host *shpnt)
{
struct aha152x_cmd_priv *acp = aha152x_priv(CURRENT_SC);
if (TESTLO(DMASTAT, DFIFOEMP)) {
u32 datao_cnt = GETSTCNT();
int datao_out = DATA_LEN - scsi_get_resid(CURRENT_SC);
int done;
struct scatterlist *sg = scsi_sglist(CURRENT_SC);
CMD_INC_RESID(CURRENT_SC, datao_out - datao_cnt);
done = scsi_bufflen(CURRENT_SC) - scsi_get_resid(CURRENT_SC);
/* Locate the first SG entry not yet sent */
while (done > 0 && !sg_is_last(sg)) {
if (done < sg->length)
break ;
done -= sg->length;
sg = sg_next(sg);
}
acp->buffer = sg;
acp->ptr = SG_ADDRESS(acp->buffer) + done;
acp->this_residual = acp->buffer->length - done;
}
SETPORT(SXFRCTL0, CH1|CLRCH1|CLRSTCNT);
SETPORT(SXFRCTL0, CH1);
SETPORT(DMACNTRL0, 0 );
}
/*
* figure out what state we ' re in
*
*/
static int update_state(struct Scsi_Host *shpnt)
{
int dataphase=0 ;
unsigned int stat0 = GETPORT(SSTAT0);
unsigned int stat1 = GETPORT(SSTAT1);
PREVSTATE = STATE;
STATE=unknown;
if (stat1 & SCSIRSTI) {
STATE=rsti;
SETPORT(SCSISEQ,0 );
SETPORT(SSTAT1,SCSIRSTI);
} else if (stat0 & SELDI && PREVSTATE == busfree) {
STATE=seldi;
} else if (stat0 & SELDO && CURRENT_SC &&
(aha152x_priv(CURRENT_SC)->phase & selecting)) {
STATE=seldo;
} else if (stat1 & SELTO) {
STATE=selto;
} else if (stat1 & BUSFREE) {
STATE=busfree;
SETPORT(SSTAT1,BUSFREE);
} else if (stat1 & SCSIPERR) {
STATE=parerr;
SETPORT(SSTAT1,SCSIPERR);
} else if (stat1 & REQINIT) {
switch (GETPORT(SCSISIG) & P_MASK) {
case P_MSGI: STATE=msgi; break ;
case P_MSGO: STATE=msgo; break ;
case P_DATAO: STATE=datao; break ;
case P_DATAI: STATE=datai; break ;
case P_STATUS: STATE=status; break ;
case P_CMD: STATE=cmd; break ;
}
dataphase=1 ;
}
if ((stat0 & SELDI) && STATE!=seldi && !dataphase) {
scmd_printk(KERN_INFO, CURRENT_SC, "reselection missed?" );
}
if (STATE!=PREVSTATE) {
LASTSTATE=PREVSTATE;
}
return dataphase;
}
/*
* handle parity error
*
* FIXME : in which phase ?
*
*/
static void parerr_run(struct Scsi_Host *shpnt)
{
scmd_printk(KERN_ERR, CURRENT_SC, "parity error\n" );
done(shpnt, SAM_STAT_GOOD, DID_PARITY);
}
/*
* handle reset in
*
*/
static void rsti_run(struct Scsi_Host *shpnt)
{
struct scsi_cmnd *ptr;
shost_printk(KERN_NOTICE, shpnt, "scsi reset in\n" );
ptr=DISCONNECTED_SC;
while (ptr) {
struct scsi_cmnd *next = SCNEXT(ptr);
if (!ptr->device->soft_reset) {
remove_SC(&DISCONNECTED_SC, ptr);
kfree(ptr->host_scribble);
ptr->host_scribble=NULL;
set_host_byte(ptr, DID_RESET);
aha152x_scsi_done(ptr);
}
ptr = next;
}
if (CURRENT_SC && !CURRENT_SC->device->soft_reset)
done(shpnt, SAM_STAT_GOOD, DID_RESET);
}
/*
* bottom - half handler
*
*/
static void is_complete(struct Scsi_Host *shpnt)
{
int dataphase;
unsigned long flags;
int pending;
if (!shpnt)
return ;
DO_LOCK(flags);
if ( HOSTDATA(shpnt)->service==0 ) {
DO_UNLOCK(flags);
return ;
}
HOSTDATA(shpnt)->service = 0 ;
if (HOSTDATA(shpnt)->in_intr) {
DO_UNLOCK(flags);
/* aha152x_error never returns.. */
aha152x_error(shpnt, "bottom-half already running!?" );
}
HOSTDATA(shpnt)->in_intr++;
/*
* loop while there are interrupt conditions pending
*
*/
do {
unsigned long start = jiffies;
DO_UNLOCK(flags);
dataphase=update_state(shpnt);
/*
* end previous state
*
*/
if (PREVSTATE!=STATE && states[PREVSTATE].end)
states[PREVSTATE].end(shpnt);
/*
* disable SPIO mode if previous phase used it
* and this one doesn ' t
*
*/
if (states[PREVSTATE].spio && !states[STATE].spio) {
SETPORT(SXFRCTL0, CH1);
SETPORT(DMACNTRL0, 0 );
if (CURRENT_SC)
aha152x_priv(CURRENT_SC)->phase &= ~spiordy;
}
/*
* accept current dataphase phase
*
*/
if (dataphase) {
SETPORT(SSTAT0, REQINIT);
SETPORT(SCSISIG, GETPORT(SCSISIG) & P_MASK);
SETPORT(SSTAT1, PHASECHG);
}
/*
* enable SPIO mode if previous didn ' t use it
* and this one does
*
*/
if (!states[PREVSTATE].spio && states[STATE].spio) {
SETPORT(DMACNTRL0, 0 );
SETPORT(SXFRCTL0, CH1|SPIOEN);
if (CURRENT_SC)
aha152x_priv(CURRENT_SC)->phase |= spiordy;
}
/*
* initialize for new state
*
*/
if (PREVSTATE!=STATE && states[STATE].init)
states[STATE].init(shpnt);
/*
* handle current state
*
*/
if (states[STATE].run)
states[STATE].run(shpnt);
else
scmd_printk(KERN_ERR, CURRENT_SC,
"unexpected state (%x)\n" , STATE);
/*
* setup controller to interrupt on
* the next expected condition and
* loop if it ' s already there
*
*/
DO_LOCK(flags);
pending=setup_expected_interrupts(shpnt);
#if defined (AHA152X_STAT)
HOSTDATA(shpnt)->count[STATE]++;
if (PREVSTATE!=STATE)
HOSTDATA(shpnt)->count_trans[STATE]++;
HOSTDATA(shpnt)->time[STATE] += jiffies-start;
#endif
} while (pending);
/*
* enable interrupts and leave bottom - half
*
*/
HOSTDATA(shpnt)->in_intr--;
SETBITS(DMACNTRL0, INTEN);
DO_UNLOCK(flags);
}
/*
* Dump the current driver status and panic
*/
static void aha152x_error(struct Scsi_Host *shpnt, char *msg)
{
shost_printk(KERN_EMERG, shpnt, "%s\n" , msg);
show_queues(shpnt);
panic("aha152x panic\n" );
}
/*
* display enabled interrupts
*/
static void disp_enintr(struct Scsi_Host *shpnt)
{
int s0, s1;
s0 = GETPORT(SIMODE0);
s1 = GETPORT(SIMODE1);
shost_printk(KERN_DEBUG, shpnt,
"enabled interrupts (%s%s%s%s%s%s%s%s%s%s%s%s%s%s)\n" ,
(s0 & ENSELDO) ? "ENSELDO " : "" ,
(s0 & ENSELDI) ? "ENSELDI " : "" ,
(s0 & ENSELINGO) ? "ENSELINGO " : "" ,
(s0 & ENSWRAP) ? "ENSWRAP " : "" ,
(s0 & ENSDONE) ? "ENSDONE " : "" ,
(s0 & ENSPIORDY) ? "ENSPIORDY " : "" ,
(s0 & ENDMADONE) ? "ENDMADONE " : "" ,
(s1 & ENSELTIMO) ? "ENSELTIMO " : "" ,
(s1 & ENATNTARG) ? "ENATNTARG " : "" ,
(s1 & ENPHASEMIS) ? "ENPHASEMIS " : "" ,
(s1 & ENBUSFREE) ? "ENBUSFREE " : "" ,
(s1 & ENSCSIPERR) ? "ENSCSIPERR " : "" ,
(s1 & ENPHASECHG) ? "ENPHASECHG " : "" ,
(s1 & ENREQINIT) ? "ENREQINIT " : "" );
}
/*
* Show the command data of a command
*/
static void show_command(struct scsi_cmnd *ptr)
{
const int phase = aha152x_priv(ptr)->phase;
scsi_print_command(ptr);
scmd_printk(KERN_DEBUG, ptr,
"request_bufflen=%d; resid=%d; "
"phase |%s%s%s%s%s%s%s%s%s; next=0x%p" ,
scsi_bufflen(ptr), scsi_get_resid(ptr),
phase & not_issued ? "not issued|" : "" ,
phase & selecting ? "selecting|" : "" ,
phase & identified ? "identified|" : "" ,
phase & disconnected ? "disconnected|" : "" ,
phase & completed ? "completed|" : "" ,
phase & spiordy ? "spiordy|" : "" ,
phase & syncneg ? "syncneg|" : "" ,
phase & aborted ? "aborted|" : "" ,
phase & resetted ? "resetted|" : "" ,
SCDATA(ptr) ? SCNEXT(ptr) : NULL);
}
/*
* Dump the queued data
*/
static void show_queues(struct Scsi_Host *shpnt)
{
struct scsi_cmnd *ptr;
unsigned long flags;
DO_LOCK(flags);
printk(KERN_DEBUG "\nqueue status:\nissue_SC:\n" );
for (ptr = ISSUE_SC; ptr; ptr = SCNEXT(ptr))
show_command(ptr);
DO_UNLOCK(flags);
printk(KERN_DEBUG "current_SC:\n" );
if (CURRENT_SC)
show_command(CURRENT_SC);
else
printk(KERN_DEBUG "none\n" );
printk(KERN_DEBUG "disconnected_SC:\n" );
for (ptr = DISCONNECTED_SC; ptr; ptr = SCDATA(ptr) ? SCNEXT(ptr) : NULL)
show_command(ptr);
disp_enintr(shpnt);
}
static void get_command(struct seq_file *m, struct scsi_cmnd * ptr)
{
struct aha152x_cmd_priv *acp = aha152x_priv(ptr);
const int phase = acp->phase;
int i;
seq_printf(m, "%p: target=%d; lun=%d; cmnd=( " ,
ptr, ptr->device->id, (u8)ptr->device->lun);
for (i = 0 ; i < COMMAND_SIZE(ptr->cmnd[0 ]); i++)
seq_printf(m, "0x%02x " , ptr->cmnd[i]);
seq_printf(m, "); resid=%d; residual=%d; buffers=%d; phase |" ,
scsi_get_resid(ptr), acp->this_residual,
sg_nents(acp->buffer) - 1 );
if (phase & not_issued)
seq_puts(m, "not issued|" );
if (phase & selecting)
seq_puts(m, "selecting|" );
if (phase & disconnected)
seq_puts(m, "disconnected|" );
if (phase & aborted)
seq_puts(m, "aborted|" );
if (phase & identified)
seq_puts(m, "identified|" );
if (phase & completed)
seq_puts(m, "completed|" );
if (phase & spiordy)
seq_puts(m, "spiordy|" );
if (phase & syncneg)
seq_puts(m, "syncneg|" );
seq_printf(m, "; next=0x%p\n" , SCNEXT(ptr));
}
static void get_ports(struct seq_file *m, struct Scsi_Host *shpnt)
{
int s;
seq_printf(m, "\n%s: %s(%s) " , CURRENT_SC ? "on bus" : "waiting" , states[STATE].name, states[PREVSTATE].name);
s = GETPORT(SCSISEQ);
seq_puts(m, "SCSISEQ( " );
if (s & TEMODEO)
seq_puts(m, "TARGET MODE " );
if (s & ENSELO)
seq_puts(m, "SELO " );
if (s & ENSELI)
seq_puts(m, "SELI " );
if (s & ENRESELI)
seq_puts(m, "RESELI " );
if (s & ENAUTOATNO)
seq_puts(m, "AUTOATNO " );
if (s & ENAUTOATNI)
seq_puts(m, "AUTOATNI " );
if (s & ENAUTOATNP)
seq_puts(m, "AUTOATNP " );
if (s & SCSIRSTO)
seq_puts(m, "SCSIRSTO " );
seq_puts(m, ");" );
seq_puts(m, " SCSISIG(" );
s = GETPORT(SCSISIG);
switch (s & P_MASK) {
case P_DATAO:
seq_puts(m, "DATA OUT" );
break ;
case P_DATAI:
seq_puts(m, "DATA IN" );
break ;
case P_CMD:
seq_puts(m, "COMMAND" );
break ;
case P_STATUS:
seq_puts(m, "STATUS" );
break ;
case P_MSGO:
seq_puts(m, "MESSAGE OUT" );
break ;
case P_MSGI:
seq_puts(m, "MESSAGE IN" );
break ;
default :
seq_puts(m, "*invalid*" );
break ;
}
seq_puts(m, "); " );
seq_printf(m, "INTSTAT (%s); " , TESTHI(DMASTAT, INTSTAT) ? "hi" : "lo" );
seq_puts(m, "SSTAT( " );
s = GETPORT(SSTAT0);
if (s & TARGET)
seq_puts(m, "TARGET " );
if (s & SELDO)
seq_puts(m, "SELDO " );
if (s & SELDI)
seq_puts(m, "SELDI " );
if (s & SELINGO)
seq_puts(m, "SELINGO " );
if (s & SWRAP)
seq_puts(m, "SWRAP " );
if (s & SDONE)
seq_puts(m, "SDONE " );
if (s & SPIORDY)
seq_puts(m, "SPIORDY " );
if (s & DMADONE)
seq_puts(m, "DMADONE " );
s = GETPORT(SSTAT1);
if (s & SELTO)
seq_puts(m, "SELTO " );
if (s & ATNTARG)
seq_puts(m, "ATNTARG " );
if (s & SCSIRSTI)
seq_puts(m, "SCSIRSTI " );
if (s & PHASEMIS)
seq_puts(m, "PHASEMIS " );
if (s & BUSFREE)
seq_puts(m, "BUSFREE " );
if (s & SCSIPERR)
seq_puts(m, "SCSIPERR " );
if (s & PHASECHG)
seq_puts(m, "PHASECHG " );
if (s & REQINIT)
seq_puts(m, "REQINIT " );
seq_puts(m, "); " );
seq_puts(m, "SSTAT( " );
s = GETPORT(SSTAT0) & GETPORT(SIMODE0);
if (s & TARGET)
seq_puts(m, "TARGET " );
if (s & SELDO)
seq_puts(m, "SELDO " );
if (s & SELDI)
seq_puts(m, "SELDI " );
if (s & SELINGO)
seq_puts(m, "SELINGO " );
if (s & SWRAP)
seq_puts(m, "SWRAP " );
if (s & SDONE)
seq_puts(m, "SDONE " );
if (s & SPIORDY)
seq_puts(m, "SPIORDY " );
if (s & DMADONE)
seq_puts(m, "DMADONE " );
s = GETPORT(SSTAT1) & GETPORT(SIMODE1);
if (s & SELTO)
seq_puts(m, "SELTO " );
if (s & ATNTARG)
seq_puts(m, "ATNTARG " );
if (s & SCSIRSTI)
seq_puts(m, "SCSIRSTI " );
if (s & PHASEMIS)
seq_puts(m, "PHASEMIS " );
if (s & BUSFREE)
seq_puts(m, "BUSFREE " );
if (s & SCSIPERR)
seq_puts(m, "SCSIPERR " );
if (s & PHASECHG)
seq_puts(m, "PHASECHG " );
if (s & REQINIT)
seq_puts(m, "REQINIT " );
seq_puts(m, "); " );
seq_puts(m, "SXFRCTL0( " );
s = GETPORT(SXFRCTL0);
if (s & SCSIEN)
seq_puts(m, "SCSIEN " );
if (s & DMAEN)
seq_puts(m, "DMAEN " );
if (s & CH1)
seq_puts(m, "CH1 " );
if (s & CLRSTCNT)
seq_puts(m, "CLRSTCNT " );
if (s & SPIOEN)
seq_puts(m, "SPIOEN " );
if (s & CLRCH1)
seq_puts(m, "CLRCH1 " );
seq_puts(m, "); " );
seq_puts(m, "SIGNAL( " );
s = GETPORT(SCSISIG);
if (s & SIG_ATNI)
seq_puts(m, "ATNI " );
if (s & SIG_SELI)
seq_puts(m, "SELI " );
if (s & SIG_BSYI)
seq_puts(m, "BSYI " );
if (s & SIG_REQI)
seq_puts(m, "REQI " );
if (s & SIG_ACKI)
seq_puts(m, "ACKI " );
seq_puts(m, "); " );
seq_printf(m, "SELID(%02x), " , GETPORT(SELID));
seq_printf(m, "STCNT(%d), " , GETSTCNT());
seq_puts(m, "SSTAT2( " );
s = GETPORT(SSTAT2);
if (s & SOFFSET)
seq_puts(m, "SOFFSET " );
if (s & SEMPTY)
seq_puts(m, "SEMPTY " );
if (s & SFULL)
seq_puts(m, "SFULL " );
seq_printf(m, "); SFCNT (%d); " , s & (SFULL | SFCNT));
s = GETPORT(SSTAT3);
seq_printf(m, "SCSICNT (%d), OFFCNT(%d), " , (s & 0 xf0) >> 4 , s & 0 x0f);
seq_puts(m, "SSTAT4( " );
s = GETPORT(SSTAT4);
if (s & SYNCERR)
seq_puts(m, "SYNCERR " );
if (s & FWERR)
seq_puts(m, "FWERR " );
if (s & FRERR)
seq_puts(m, "FRERR " );
seq_puts(m, "); " );
seq_puts(m, "DMACNTRL0( " );
s = GETPORT(DMACNTRL0);
seq_printf(m, "%s " , s & _8 BIT ? "8BIT" : "16BIT" );
seq_printf(m, "%s " , s & DMA ? "DMA" : "PIO" );
seq_printf(m, "%s " , s & WRITE_READ ? "WRITE" : "READ" );
if (s & ENDMA)
seq_puts(m, "ENDMA " );
if (s & INTEN)
seq_puts(m, "INTEN " );
if (s & RSTFIFO)
seq_puts(m, "RSTFIFO " );
if (s & SWINT)
seq_puts(m, "SWINT " );
seq_puts(m, "); " );
seq_puts(m, "DMASTAT( " );
s = GETPORT(DMASTAT);
if (s & ATDONE)
seq_puts(m, "ATDONE " );
if (s & WORDRDY)
seq_puts(m, "WORDRDY " );
if (s & DFIFOFULL)
seq_puts(m, "DFIFOFULL " );
if (s & DFIFOEMP)
seq_puts(m, "DFIFOEMP " );
seq_puts(m, ")\n" );
seq_puts(m, "enabled interrupts( " );
s = GETPORT(SIMODE0);
if (s & ENSELDO)
seq_puts(m, "ENSELDO " );
if (s & ENSELDI)
seq_puts(m, "ENSELDI " );
if (s & ENSELINGO)
seq_puts(m, "ENSELINGO " );
if (s & ENSWRAP)
seq_puts(m, "ENSWRAP " );
if (s & ENSDONE)
seq_puts(m, "ENSDONE " );
if (s & ENSPIORDY)
seq_puts(m, "ENSPIORDY " );
if (s & ENDMADONE)
seq_puts(m, "ENDMADONE " );
s = GETPORT(SIMODE1);
if (s & ENSELTIMO)
seq_puts(m, "ENSELTIMO " );
if (s & ENATNTARG)
seq_puts(m, "ENATNTARG " );
if (s & ENPHASEMIS)
seq_puts(m, "ENPHASEMIS " );
if (s & ENBUSFREE)
seq_puts(m, "ENBUSFREE " );
if (s & ENSCSIPERR)
seq_puts(m, "ENSCSIPERR " );
if (s & ENPHASECHG)
seq_puts(m, "ENPHASECHG " );
if (s & ENREQINIT)
seq_puts(m, "ENREQINIT " );
seq_puts(m, ")\n" );
}
static int aha152x_set_info(struct Scsi_Host *shpnt, char *buffer, int length)
{
if (!shpnt || !buffer || length<8 || strncmp("aha152x " , buffer, 8 )!=0 )
return -EINVAL;
#if defined (AHA152X_STAT)
if (length>13 && strncmp("reset" , buffer+8 , 5 )==0 ) {
int i;
HOSTDATA(shpnt)->total_commands=0 ;
HOSTDATA(shpnt)->disconnections=0 ;
HOSTDATA(shpnt)->busfree_without_any_action=0 ;
HOSTDATA(shpnt)->busfree_without_old_command=0 ;
HOSTDATA(shpnt)->busfree_without_new_command=0 ;
HOSTDATA(shpnt)->busfree_without_done_command=0 ;
HOSTDATA(shpnt)->busfree_with_check_condition=0 ;
for (i = idle; i<maxstate; i++) {
HOSTDATA(shpnt)->count[i]=0 ;
HOSTDATA(shpnt)->count_trans[i]=0 ;
HOSTDATA(shpnt)->time[i]=0 ;
}
shost_printk(KERN_INFO, shpnt, "aha152x: stats reset.\n" );
} else
#endif
{
return -EINVAL;
}
return length;
}
static int aha152x_show_info(struct seq_file *m, struct Scsi_Host *shpnt)
{
int i;
struct scsi_cmnd *ptr;
unsigned long flags;
seq_puts(m, AHA152X_REVID "\n" );
seq_printf(m, "ioports 0x%04lx to 0x%04lx\n" ,
shpnt->io_port, shpnt->io_port + shpnt->n_io_port - 1 );
seq_printf(m, "interrupt 0x%02x\n" , shpnt->irq);
seq_printf(m, "disconnection/reconnection %s\n" ,
RECONNECT ? "enabled" : "disabled" );
seq_printf(m, "parity checking %s\n" ,
PARITY ? "enabled" : "disabled" );
seq_printf(m, "synchronous transfers %s\n" ,
SYNCHRONOUS ? "enabled" : "disabled" );
seq_printf(m, "%d commands currently queued\n" , HOSTDATA(shpnt)->commands);
if (SYNCHRONOUS) {
seq_puts(m, "synchronously operating targets (tick=50 ns):\n" );
for (i = 0 ; i < 8 ; i++)
if (HOSTDATA(shpnt)->syncrate[i] & 0 x7f)
seq_printf(m, "target %d: period %dT/%dns; req/ack offset %d\n" ,
i,
(((HOSTDATA(shpnt)->syncrate[i] & 0 x70) >> 4 ) + 2 ),
(((HOSTDATA(shpnt)->syncrate[i] & 0 x70) >> 4 ) + 2 ) * 50 ,
HOSTDATA(shpnt)->syncrate[i] & 0 x0f);
}
seq_puts(m, "\nqueue status:\n" );
DO_LOCK(flags);
if (ISSUE_SC) {
seq_puts(m, "not yet issued commands:\n" );
for (ptr = ISSUE_SC; ptr; ptr = SCNEXT(ptr))
get_command(m, ptr);
} else
seq_puts(m, "no not yet issued commands\n" );
DO_UNLOCK(flags);
if (CURRENT_SC) {
seq_puts(m, "current command:\n" );
get_command(m, CURRENT_SC);
} else
seq_puts(m, "no current command\n" );
if (DISCONNECTED_SC) {
seq_puts(m, "disconnected commands:\n" );
for (ptr = DISCONNECTED_SC; ptr; ptr = SCNEXT(ptr))
get_command(m, ptr);
} else
seq_puts(m, "no disconnected commands\n" );
get_ports(m, shpnt);
#if defined (AHA152X_STAT)
seq_printf(m, "statistics:\n"
"total commands: %d\n"
"disconnections: %d\n"
"busfree with check condition: %d\n"
"busfree without old command: %d\n"
"busfree without new command: %d\n"
"busfree without done command: %d\n"
"busfree without any action: %d\n"
"state "
"transitions "
"count "
"time\n" ,
HOSTDATA(shpnt)->total_commands,
HOSTDATA(shpnt)->disconnections,
HOSTDATA(shpnt)->busfree_with_check_condition,
HOSTDATA(shpnt)->busfree_without_old_command,
HOSTDATA(shpnt)->busfree_without_new_command,
HOSTDATA(shpnt)->busfree_without_done_command,
HOSTDATA(shpnt)->busfree_without_any_action);
for (i=0 ; i<maxstate; i++) {
seq_printf(m, "%-10s %-12d %-12d %-12ld\n" ,
states[i].name,
HOSTDATA(shpnt)->count_trans[i],
HOSTDATA(shpnt)->count[i],
HOSTDATA(shpnt)->time[i]);
}
#endif
return 0 ;
}
static const struct scsi_host_template aha152x_driver_template = {
.module = THIS_MODULE,
.name = AHA152X_REVID,
.proc_name = "aha152x" ,
.show_info = aha152x_show_info,
.write_info = aha152x_set_info,
.queuecommand = aha152x_queue,
.eh_abort_handler = aha152x_abort,
.eh_device_reset_handler = aha152x_device_reset,
.eh_bus_reset_handler = aha152x_bus_reset,
.bios_param = aha152x_biosparam,
.can_queue = 1 ,
.this_id = 7 ,
.sg_tablesize = SG_ALL,
.dma_boundary = PAGE_SIZE - 1 ,
.cmd_size = sizeof (struct aha152x_cmd_priv),
};
#if !defined (AHA152X_PCMCIA)
static int setup_count;
static struct aha152x_setup setup[2 ];
/* possible i/o addresses for the AIC-6260; default first */
static unsigned short ports[] = { 0 x340, 0 x140 };
#if !defined (SKIP_BIOSTEST)
/* possible locations for the Adaptec BIOS; defaults first */
static unsigned int addresses[] =
{
0 xdc000, /* default first */
0 xc8000,
0 xcc000,
0 xd0000,
0 xd4000,
0 xd8000,
0 xe0000,
0 xeb800, /* VTech Platinum SMP */
0 xf0000,
};
/* signatures for various AIC-6[23]60 based controllers.
The point in detecting signatures is to avoid useless and maybe
harmful probes on ports . I ' m not sure that all listed boards pass
auto - configuration . For those which fail the BIOS signature is
obsolete , because user intervention to supply the configuration is
needed anyway . May be an information whether or not the BIOS supports
extended translation could be also useful here. */
static struct signature {
unsigned char *signature;
int sig_offset;
int sig_length;
} signatures[] =
{
{ "Adaptec AHA-1520 BIOS" , 0 x102e, 21 },
/* Adaptec 152x */
{ "Adaptec AHA-1520B" , 0 x000b, 17 },
/* Adaptec 152x rev B */
{ "Adaptec AHA-1520B" , 0 x0026, 17 },
/* Iomega Jaz Jet ISA (AIC6370Q) */
{ "Adaptec ASW-B626 BIOS" , 0 x1029, 21 },
/* on-board controller */
{ "Adaptec BIOS: ASW-B626" , 0 x000f, 22 },
/* on-board controller */
{ "Adaptec ASW-B626 S2" , 0 x2e6c, 19 },
/* on-board controller */
{ "Adaptec BIOS:AIC-6360" , 0 x000c, 21 },
/* on-board controller */
{ "ScsiPro SP-360 BIOS" , 0 x2873, 19 },
/* ScsiPro-Controller */
{ "GA-400 LOCAL BUS SCSI BIOS" , 0 x102e, 26 },
/* Gigabyte Local-Bus-SCSI */
{ "Adaptec BIOS:AVA-282X" , 0 x000c, 21 },
/* Adaptec 282x */
{ "Adaptec IBM Dock II SCSI" , 0 x2edd, 24 },
/* IBM Thinkpad Dock II */
{ "Adaptec BIOS:AHA-1532P" , 0 x001c, 22 },
/* IBM Thinkpad Dock II SCSI */
{ "DTC3520A Host Adapter BIOS" , 0 x318a, 26 },
/* DTC 3520A ISA SCSI */
};
#endif /* !SKIP_BIOSTEST */
/*
* Test , if port_base is valid .
*
*/
static int aha152x_porttest(int io_port)
{
int i;
SETPORT(io_port + O_DMACNTRL1, 0 ); /* reset stack pointer */
for (i = 0 ; i < 16 ; i++)
SETPORT(io_port + O_STACK, i);
SETPORT(io_port + O_DMACNTRL1, 0 ); /* reset stack pointer */
for (i = 0 ; i < 16 && GETPORT(io_port + O_STACK) == i; i++)
;
return (i == 16 );
}
static int tc1550_porttest(int io_port)
{
int i;
SETPORT(io_port + O_TC_DMACNTRL1, 0 ); /* reset stack pointer */
for (i = 0 ; i < 16 ; i++)
SETPORT(io_port + O_STACK, i);
SETPORT(io_port + O_TC_DMACNTRL1, 0 ); /* reset stack pointer */
for (i = 0 ; i < 16 && GETPORT(io_port + O_TC_STACK) == i; i++)
;
return (i == 16 );
}
static int checksetup(struct aha152x_setup *setup)
{
int i;
for (i = 0 ; i < ARRAY_SIZE(ports) && (setup->io_port != ports[i]); i++)
;
if (i == ARRAY_SIZE(ports))
return 0 ;
if (!request_region(setup->io_port, IO_RANGE, "aha152x" )) {
printk(KERN_ERR "aha152x: io port 0x%x busy.\n" , setup->io_port);
return 0 ;
}
if ( aha152x_porttest(setup->io_port) ) {
setup->tc1550=0 ;
} else if ( tc1550_porttest(setup->io_port) ) {
setup->tc1550=1 ;
} else {
release_region(setup->io_port, IO_RANGE);
return 0 ;
}
release_region(setup->io_port, IO_RANGE);
if ((setup->irq < IRQ_MIN) || (setup->irq > IRQ_MAX))
return 0 ;
if ((setup->scsiid < 0 ) || (setup->scsiid > 7 ))
return 0 ;
if ((setup->reconnect < 0 ) || (setup->reconnect > 1 ))
return 0 ;
if ((setup->parity < 0 ) || (setup->parity > 1 ))
return 0 ;
if ((setup->synchronous < 0 ) || (setup->synchronous > 1 ))
return 0 ;
if ((setup->ext_trans < 0 ) || (setup->ext_trans > 1 ))
return 0 ;
return 1 ;
}
static int __init aha152x_init(void )
{
int i, j, ok;
#if defined (AUTOCONF)
aha152x_config conf;
#endif
#ifdef __ISAPNP__
struct pnp_dev *dev=NULL, *pnpdev[2 ] = {NULL, NULL};
#endif
if ( setup_count ) {
printk(KERN_INFO "aha152x: processing commandline: " );
for (i = 0 ; i<setup_count; i++) {
if (!checksetup(&setup[i])) {
printk(KERN_ERR "\naha152x: %s\n" , setup[i].conf);
printk(KERN_ERR "aha152x: invalid line\n" );
}
}
printk("ok\n" );
}
#if defined (SETUP0)
if (setup_count < ARRAY_SIZE(setup)) {
struct aha152x_setup override = SETUP0;
if (setup_count == 0 || (override.io_port != setup[0 ].io_port)) {
if (!checksetup(&override)) {
printk(KERN_ERR "\naha152x: invalid override SETUP0={0x%x,%d,%d,%d,%d,%d,%d,%d}\n" ,
override.io_port,
override.irq,
override.scsiid,
override.reconnect,
override.parity,
override.synchronous,
override.delay,
override.ext_trans);
} else
setup[setup_count++] = override;
}
}
#endif
#if defined (SETUP1)
if (setup_count < ARRAY_SIZE(setup)) {
struct aha152x_setup override = SETUP1;
if (setup_count == 0 || (override.io_port != setup[0 ].io_port)) {
if (!checksetup(&override)) {
printk(KERN_ERR "\naha152x: invalid override SETUP1={0x%x,%d,%d,%d,%d,%d,%d,%d}\n" ,
override.io_port,
override.irq,
override.scsiid,
override.reconnect,
override.parity,
override.synchronous,
override.delay,
override.ext_trans);
} else
setup[setup_count++] = override;
}
}
#endif
#if defined (MODULE)
if (setup_count<ARRAY_SIZE(setup) && (aha152x[0 ]!=0 || io[0 ]!=0 || irq[0 ]!=0 )) {
if (aha152x[0 ]!=0 ) {
setup[setup_count].conf = "" ;
setup[setup_count].io_port = aha152x[0 ];
setup[setup_count].irq = aha152x[1 ];
setup[setup_count].scsiid = aha152x[2 ];
setup[setup_count].reconnect = aha152x[3 ];
setup[setup_count].parity = aha152x[4 ];
setup[setup_count].synchronous = aha152x[5 ];
setup[setup_count].delay = aha152x[6 ];
setup[setup_count].ext_trans = aha152x[7 ];
} else if (io[0 ] != 0 || irq[0 ] != 0 ) {
if (io[0 ]!=0 ) setup[setup_count].io_port = io[0 ];
if (irq[0 ]!=0 ) setup[setup_count].irq = irq[0 ];
setup[setup_count].scsiid = scsiid[0 ];
setup[setup_count].reconnect = reconnect[0 ];
setup[setup_count].parity = parity[0 ];
setup[setup_count].synchronous = sync[0 ];
setup[setup_count].delay = delay[0 ];
setup[setup_count].ext_trans = exttrans[0 ];
}
if (checksetup(&setup[setup_count]))
setup_count++;
else
printk(KERN_ERR "aha152x: invalid module params io=0x%x, irq=%d,scsiid=%d,reconnect=%d,parity=%d,sync=%d,delay=%d,exttrans=%d\n" ,
setup[setup_count].io_port,
setup[setup_count].irq,
setup[setup_count].scsiid,
setup[setup_count].reconnect,
setup[setup_count].parity,
setup[setup_count].synchronous,
setup[setup_count].delay,
setup[setup_count].ext_trans);
}
if (setup_count<ARRAY_SIZE(setup) && (aha152x1[0 ]!=0 || io[1 ]!=0 || irq[1 ]!=0 )) {
if (aha152x1[0 ]!=0 ) {
setup[setup_count].conf = "" ;
setup[setup_count].io_port = aha152x1[0 ];
setup[setup_count].irq = aha152x1[1 ];
setup[setup_count].scsiid = aha152x1[2 ];
setup[setup_count].reconnect = aha152x1[3 ];
setup[setup_count].parity = aha152x1[4 ];
setup[setup_count].synchronous = aha152x1[5 ];
setup[setup_count].delay = aha152x1[6 ];
setup[setup_count].ext_trans = aha152x1[7 ];
} else if (io[1 ] != 0 || irq[1 ] != 0 ) {
if (io[1 ]!=0 ) setup[setup_count].io_port = io[1 ];
if (irq[1 ]!=0 ) setup[setup_count].irq = irq[1 ];
setup[setup_count].scsiid = scsiid[1 ];
setup[setup_count].reconnect = reconnect[1 ];
setup[setup_count].parity = parity[1 ];
setup[setup_count].synchronous = sync[1 ];
setup[setup_count].delay = delay[1 ];
setup[setup_count].ext_trans = exttrans[1 ];
}
if (checksetup(&setup[setup_count]))
setup_count++;
else
printk(KERN_ERR "aha152x: invalid module params io=0x%x, irq=%d,scsiid=%d,reconnect=%d,parity=%d,sync=%d,delay=%d,exttrans=%d\n" ,
setup[setup_count].io_port,
setup[setup_count].irq,
setup[setup_count].scsiid,
setup[setup_count].reconnect,
setup[setup_count].parity,
setup[setup_count].synchronous,
setup[setup_count].delay,
setup[setup_count].ext_trans);
}
#endif
#ifdef __ISAPNP__
for (i=0 ; setup_count<ARRAY_SIZE(setup) && id_table[i].vendor; i++) {
while ( setup_count<ARRAY_SIZE(setup) &&
(dev=pnp_find_dev(NULL, id_table[i].vendor, id_table[i].function, dev)) ) {
if (pnp_device_attach(dev) < 0 )
continue ;
if (pnp_activate_dev(dev) < 0 ) {
pnp_device_detach(dev);
continue ;
}
if (!pnp_port_valid(dev, 0 )) {
pnp_device_detach(dev);
continue ;
}
if (setup_count==1 && pnp_port_start(dev, 0 )==setup[0 ].io_port) {
pnp_device_detach(dev);
continue ;
}
setup[setup_count].io_port = pnp_port_start(dev, 0 );
setup[setup_count].irq = pnp_irq(dev, 0 );
setup[setup_count].scsiid = 7 ;
setup[setup_count].reconnect = 1 ;
setup[setup_count].parity = 1 ;
setup[setup_count].synchronous = 1 ;
setup[setup_count].delay = DELAY_DEFAULT;
setup[setup_count].ext_trans = 0 ;
#if defined (__ISAPNP__)
pnpdev[setup_count] = dev;
#endif
printk (KERN_INFO
"aha152x: found ISAPnP adapter at io=0x%03x, irq=%d\n" ,
setup[setup_count].io_port, setup[setup_count].irq);
setup_count++;
}
}
#endif
#if defined (AUTOCONF)
if (setup_count<ARRAY_SIZE(setup)) {
#if !defined (SKIP_BIOSTEST)
ok = 0 ;
for (i = 0 ; i < ARRAY_SIZE(addresses) && !ok; i++) {
void __iomem *p = ioremap(addresses[i], 0 x4000);
if (!p)
continue ;
for (j = 0 ; j<ARRAY_SIZE(signatures) && !ok; j++)
ok = check_signature(p + signatures[j].sig_offset,
signatures[j].signature, signatures[j].sig_length);
iounmap(p);
}
if (!ok && setup_count == 0 )
return -ENODEV;
printk(KERN_INFO "aha152x: BIOS test: passed, " );
#else
printk(KERN_INFO "aha152x: " );
#endif /* !SKIP_BIOSTEST */
ok = 0 ;
for (i = 0 ; i < ARRAY_SIZE(ports) && setup_count < 2 ; i++) {
if ((setup_count == 1 ) && (setup[0 ].io_port == ports[i]))
continue ;
if (!request_region(ports[i], IO_RANGE, "aha152x" )) {
printk(KERN_ERR "aha152x: io port 0x%x busy.\n" , ports[i]);
continue ;
}
if (aha152x_porttest(ports[i])) {
setup[setup_count].tc1550 = 0 ;
conf.cf_port =
(GETPORT(ports[i] + O_PORTA) << 8 ) + GETPORT(ports[i] + O_PORTB);
} else if (tc1550_porttest(ports[i])) {
setup[setup_count].tc1550 = 1 ;
conf.cf_port =
(GETPORT(ports[i] + O_TC_PORTA) << 8 ) + GETPORT(ports[i] + O_TC_PORTB);
} else {
release_region(ports[i], IO_RANGE);
continue ;
}
release_region(ports[i], IO_RANGE);
ok++;
setup[setup_count].io_port = ports[i];
setup[setup_count].irq = IRQ_MIN + conf.cf_irq;
setup[setup_count].scsiid = conf.cf_id;
setup[setup_count].reconnect = conf.cf_tardisc;
setup[setup_count].parity = !conf.cf_parity;
setup[setup_count].synchronous = conf.cf_syncneg;
setup[setup_count].delay = DELAY_DEFAULT;
setup[setup_count].ext_trans = 0 ;
setup_count++;
}
if (ok)
printk("auto configuration: ok, " );
}
#endif
printk("%d controller(s) configured\n" , setup_count);
for (i=0 ; i<setup_count; i++) {
if ( request_region(setup[i].io_port, IO_RANGE, "aha152x" ) ) {
struct Scsi_Host *shpnt = aha152x_probe_one(&setup[i]);
if ( !shpnt ) {
release_region(setup[i].io_port, IO_RANGE);
#if defined (__ISAPNP__)
} else if ( pnpdev[i] ) {
HOSTDATA(shpnt)->pnpdev=pnpdev[i];
pnpdev[i]=NULL;
#endif
}
} else {
printk(KERN_ERR "aha152x: io port 0x%x busy.\n" , setup[i].io_port);
}
#if defined (__ISAPNP__)
if ( pnpdev[i] )
pnp_device_detach(pnpdev[i]);
#endif
}
return 0 ;
}
static void __exit aha152x_exit(void )
{
struct aha152x_hostdata *hd, *tmp;
list_for_each_entry_safe(hd, tmp, &aha152x_host_list, host_list) {
struct Scsi_Host *shost = container_of((void *)hd, struct Scsi_Host, hostdata);
aha152x_release(shost);
}
}
module_init(aha152x_init);
module_exit(aha152x_exit);
#if !defined (MODULE)
static int __init aha152x_setup(char *str)
{
int ints[10 ];
get_options(str, ARRAY_SIZE(ints), ints);
if (setup_count>=ARRAY_SIZE(setup)) {
printk(KERN_ERR "aha152x: you can only configure up to two controllers\n" );
return 1 ;
}
setup[setup_count].conf = str;
setup[setup_count].io_port = ints[0 ] >= 1 ? ints[1 ] : 0 x340;
setup[setup_count].irq = ints[0 ] >= 2 ? ints[2 ] : 11 ;
setup[setup_count].scsiid = ints[0 ] >= 3 ? ints[3 ] : 7 ;
setup[setup_count].reconnect = ints[0 ] >= 4 ? ints[4 ] : 1 ;
setup[setup_count].parity = ints[0 ] >= 5 ? ints[5 ] : 1 ;
setup[setup_count].synchronous = ints[0 ] >= 6 ? ints[6 ] : 1 ;
setup[setup_count].delay = ints[0 ] >= 7 ? ints[7 ] : DELAY_DEFAULT;
setup[setup_count].ext_trans = ints[0 ] >= 8 ? ints[8 ] : 0 ;
if (ints[0 ] > 8 )
printk(KERN_NOTICE "aha152x: usage: aha152x=<IOBASE>[,<IRQ>[,<SCSI ID>"
"[,<RECONNECT>[,<PARITY>[,<SYNCHRONOUS>[,<DELAY>[,<EXT_TRANS>]]]]]]]\n" );
else
setup_count++;
return 1 ;
}
__setup("aha152x=" , aha152x_setup);
#endif
#endif /* !AHA152X_PCMCIA */
Messung V0.5 in Prozent C=96 H=93 G=94
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.135Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-29)
¤
*Eine klare Vorstellung vom Zielzustand