diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/scsi/aha152x.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/scsi/aha152x.c')
-rw-r--r-- | drivers/scsi/aha152x.c | 3982 |
1 files changed, 3982 insertions, 0 deletions
diff --git a/drivers/scsi/aha152x.c b/drivers/scsi/aha152x.c new file mode 100644 index 00000000000..d7b8efe8640 --- /dev/null +++ b/drivers/scsi/aha152x.c @@ -0,0 +1,3982 @@ +/* aha152x.c -- Adaptec AHA-152x driver + * Author: Jürgen E. Fischer, fischer@norbit.de + * Copyright 1993-2004 Jürgen E. Fischer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * + * $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 >1GB 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.99pl13 + * + * 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.txt for configuration details + + **************************************************************************/ + +#include <linux/module.h> +#include <linux/sched.h> +#include <asm/irq.h> +#include <asm/io.h> +#include <linux/blkdev.h> +#include <asm/system.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 <asm/semaphore.h> +#include <scsi/scsicam.h> + +#include "scsi.h" +#include <scsi/scsi_host.h> +#include "aha152x.h" + + +/* DEFINES */ + +/* For PCMCIA cards, always use AUTOCONF */ +#if defined(PCMCIA) || defined(MODULE) +#if !defined(AUTOCONF) +#define AUTOCONF +#endif +#endif + +#if !defined(AUTOCONF) && !defined(SETUP0) +#error define AUTOCONF or SETUP0 +#endif + +#if defined(AHA152X_DEBUG) +#define DEBUG_DEFAULT debug_eh + +#define DPRINTK(when,msgs...) \ + do { if(HOSTDATA(shpnt)->debug & (when)) printk(msgs); } while(0) + +#define DO_LOCK(flags) \ + do { \ + if(spin_is_locked(&QLOCK)) { \ + DPRINTK(debug_intr, DEBUG_LEAD "(%s:%d) already locked at %s:%d\n", CMDINFO(CURRENT_SC), __FUNCTION__, __LINE__, QLOCKER, QLOCKERL); \ + } \ + DPRINTK(debug_locks, DEBUG_LEAD "(%s:%d) locking\n", CMDINFO(CURRENT_SC), __FUNCTION__, __LINE__); \ + spin_lock_irqsave(&QLOCK,flags); \ + DPRINTK(debug_locks, DEBUG_LEAD "(%s:%d) locked\n", CMDINFO(CURRENT_SC), __FUNCTION__, __LINE__); \ + QLOCKER=__FUNCTION__; \ + QLOCKERL=__LINE__; \ + } while(0) + +#define DO_UNLOCK(flags) \ + do { \ + DPRINTK(debug_locks, DEBUG_LEAD "(%s:%d) unlocking (locked at %s:%d)\n", CMDINFO(CURRENT_SC), __FUNCTION__, __LINE__, QLOCKER, QLOCKERL); \ + spin_unlock_irqrestore(&QLOCK,flags); \ + DPRINTK(debug_locks, DEBUG_LEAD "(%s:%d) unlocked\n", CMDINFO(CURRENT_SC), __FUNCTION__, __LINE__); \ + QLOCKER="(not locked)"; \ + QLOCKERL=0; \ + } while(0) + +#else +#define DPRINTK(when,msgs...) +#define DO_LOCK(flags) spin_lock_irqsave(&QLOCK,flags) +#define DO_UNLOCK(flags) spin_unlock_irqrestore(&QLOCK,flags) +#endif + +#define LEAD "(scsi%d:%d:%d) " +#define WARN_LEAD KERN_WARNING LEAD +#define INFO_LEAD KERN_INFO LEAD +#define NOTE_LEAD KERN_NOTICE LEAD +#define ERR_LEAD KERN_ERR LEAD +#define DEBUG_LEAD KERN_DEBUG LEAD +#define CMDINFO(cmd) \ + (cmd) ? ((cmd)->device->host->host_no) : -1, \ + (cmd) ? ((cmd)->device->id & 0x0f) : -1, \ + (cmd) ? ((cmd)->device->lun & 0x07) : -1 + +#define DELAY_DEFAULT 1000 + +#if defined(PCMCIA) +#define IRQ_MIN 0 +#define IRQ_MAX 16 +#else +#define IRQ_MIN 9 +#if defined(__PPC) +#define IRQ_MAX (NR_IRQS-1) +#else +#define IRQ_MAX 12 +#endif +#endif + +enum { + not_issued = 0x0001, /* command not yet issued */ + selecting = 0x0002, /* target is beeing selected */ + identified = 0x0004, /* IDENTIFY was sent */ + disconnected = 0x0008, /* target disconnected */ + completed = 0x0010, /* target sent COMMAND COMPLETE */ + aborted = 0x0020, /* ABORT was sent */ + resetted = 0x0040, /* BUS DEVICE RESET was sent */ + spiordy = 0x0080, /* waiting for SPIORDY to raise */ + syncneg = 0x0100, /* synchronous negotiation in progress */ + aborting = 0x0200, /* ABORT is pending */ + resetting = 0x0400, /* BUS DEVICE RESET is pending */ + check_condition = 0x0800, /* requesting sense after CHECK CONDITION */ +}; + +MODULE_AUTHOR("Jürgen Fischer"); +MODULE_DESCRIPTION(AHA152X_REVID); +MODULE_LICENSE("GPL"); + +#if !defined(PCMCIA) +#if defined(MODULE) +static int io[] = {0, 0}; +module_param_array(io, int, NULL, 0); +MODULE_PARM_DESC(io,"base io address of controller"); + +static int irq[] = {0, 0}; +module_param_array(irq, int, 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"); + +#if !defined(AHA152X_DEBUG) +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"); +#else +static int debug[] = {DEBUG_DEFAULT, DEBUG_DEFAULT}; +module_param_array(debug, int, NULL, 0); +MODULE_PARM_DESC(debug, "flags for driver debugging"); + +static int aha152x[] = {0, 11, 7, 1, 1, 1, DELAY_DEFAULT, 0, DEBUG_DEFAULT}; +module_param_array(aha152x, int, NULL, 0); +MODULE_PARM_DESC(aha152x, "parameters for first controller"); + +static int aha152x1[] = {0, 11, 7, 1, 1, 1, DELAY_DEFAULT, 0, DEBUG_DEFAULT}; +module_param_array(aha152x1, int, NULL, 0); +MODULE_PARM_DESC(aha152x1, "parameters for second controller"); +#endif /* !defined(AHA152X_DEBUG) */ +#endif /* MODULE */ + +#ifdef __ISAPNP__ +static struct isapnp_device_id id_table[] __devinitdata = { + { ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('A','D','P'), ISAPNP_FUNCTION(0x1505), 0 }, + { ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('A','D','P'), ISAPNP_FUNCTION(0x1530), 0 }, + { ISAPNP_DEVICE_SINGLE_END, } +}; +MODULE_DEVICE_TABLE(isapnp, id_table); +#endif /* ISAPNP */ + +#endif /* !PCMCIA */ + +static int registered_count=0; +static struct Scsi_Host *aha152x_host[2]; +static 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 { + Scsi_Cmnd *issue_SC; + /* pending commands to issue */ + + Scsi_Cmnd *current_SC; + /* current command on the bus */ + + Scsi_Cmnd *disconnected_SC; + /* commands that disconnected */ + + Scsi_Cmnd *done_SC; + /* command that was completed */ + + spinlock_t lock; + /* host lock */ + +#if defined(AHA152X_DEBUG) + const char *locker; + /* which function has the lock */ + int lockerl; /* where did it get it */ + + int debug; /* current debugging setting */ +#endif + +#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 +}; + + +/* + * host specific command extension + * + */ +struct aha152x_scdata { + Scsi_Cmnd *next; /* next sc in queue */ + struct semaphore *sem; /* semaphore to block on */ +}; + + +/* 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)->sem + +#define SG_ADDRESS(buffer) ((char *) (page_address((buffer)->page)+(buffer)->offset)) + +/* 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, struct pt_regs *); +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, int error); + +/* diagnostics */ +static void disp_ports(struct Scsi_Host *shpnt); +static void show_command(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(Scsi_Cmnd **SC, Scsi_Cmnd *new_SC) +{ + 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 Scsi_Cmnd *remove_first_SC(Scsi_Cmnd ** SC) +{ + Scsi_Cmnd *ptr; + + ptr = *SC; + if (ptr) { + *SC = SCNEXT(*SC); + SCNEXT(ptr)=NULL; + } + return ptr; +} + +static inline Scsi_Cmnd *remove_lun_SC(Scsi_Cmnd ** SC, int target, int lun) +{ + 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 Scsi_Cmnd *remove_SC(Scsi_Cmnd **SC, Scsi_Cmnd *SCp) +{ + 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 inline struct Scsi_Host *lookup_irq(int irqno) +{ + int i; + + for(i=0; i<ARRAY_SIZE(aha152x_host); i++) + if(aha152x_host[i] && aha152x_host[i]->irq==irqno) + return aha152x_host[i]; + + return NULL; +} + +static irqreturn_t swintr(int irqno, void *dev_id, struct pt_regs *regs) +{ + struct Scsi_Host *shpnt = lookup_irq(irqno); + + if (!shpnt) { + printk(KERN_ERR "aha152x: catched software interrupt %d for unknown controller.\n", irqno); + return IRQ_NONE; + } + + 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; + } + + /* need to have host registered before triggering any interrupt */ + aha152x_host[registered_count] = shpnt; + + memset(HOSTDATA(shpnt), 0, sizeof *HOSTDATA(shpnt)); + + 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+0x10; + HOSTIOPORT1 = setup->io_port-0x10; + } + + spin_lock_init(&QLOCK); + RECONNECT = setup->reconnect; + SYNCHRONOUS = setup->synchronous; + PARITY = setup->parity; + DELAY = setup->delay; + EXT_TRANS = setup->ext_trans; + +#if defined(AHA152X_DEBUG) + HOSTDATA(shpnt)->debug = setup->debug; +#endif + + 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) & 0x7, + 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, SA_INTERRUPT|SA_SHIRQ, "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, 0x7f); + SETPORT(SSTAT1, 0xef); + + if ( request_irq(shpnt->irq, intr, SA_INTERRUPT|SA_SHIRQ, "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); + + registered_count++; + + return shpnt; + +out_host_put: + aha152x_host[registered_count]=NULL; + scsi_host_put(shpnt); + + return NULL; +} + +void aha152x_release(struct Scsi_Host *shpnt) +{ + if(!shpnt) + return; + + if (shpnt->irq) + free_irq(shpnt->irq, shpnt); + +#if !defined(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 + + scsi_remove_host(shpnt); + 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) { + CURRENT_SC->SCp.phase |= 1 << 16; + + if(CURRENT_SC->SCp.phase & selecting) { + DPRINTK(debug_intr, DEBUG_LEAD "expecting: (seldo) (seltimo) (seldi)\n", CMDINFO(CURRENT_SC)); + SETPORT(SSTAT1, SELTO); + SETPORT(SIMODE0, ENSELDO | (DISCONNECTED_SC ? ENSELDI : 0)); + SETPORT(SIMODE1, ENSELTIMO); + } else { + DPRINTK(debug_intr, DEBUG_LEAD "expecting: (phase change) (busfree) %s\n", CMDINFO(CURRENT_SC), CURRENT_SC->SCp.phase & spiordy ? "(spiordy)" : ""); + SETPORT(SIMODE0, (CURRENT_SC->SCp.phase & spiordy) ? ENSPIORDY : 0); + SETPORT(SIMODE1, ENPHASEMIS | ENSCSIRST | ENSCSIPERR | ENBUSFREE); + } + } else if(STATE==seldi) { + DPRINTK(debug_intr, DEBUG_LEAD "expecting: (phase change) (identify)\n", CMDINFO(CURRENT_SC)); + SETPORT(SIMODE0, 0); + SETPORT(SIMODE1, ENPHASEMIS | ENSCSIRST | ENSCSIPERR | ENBUSFREE); + } else { + DPRINTK(debug_intr, DEBUG_LEAD "expecting: %s %s\n", + CMDINFO(CURRENT_SC), + DISCONNECTED_SC ? "(reselection)" : "", + ISSUE_SC ? "(busfree)" : ""); + 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(Scsi_Cmnd *SCpnt, struct semaphore *sem, int phase, void (*done)(Scsi_Cmnd *)) +{ + struct Scsi_Host *shpnt = SCpnt->device->host; + unsigned long flags; + +#if defined(AHA152X_DEBUG) + if (HOSTDATA(shpnt)->debug & debug_queue) { + printk(INFO_LEAD "queue: %p; cmd_len=%d pieces=%d size=%u cmnd=", + CMDINFO(SCpnt), SCpnt, SCpnt->cmd_len, SCpnt->use_sg, SCpnt->request_bufflen); + print_command(SCpnt->cmnd); + } +#endif + + SCpnt->scsi_done = done; + SCpnt->resid = SCpnt->request_bufflen; + SCpnt->SCp.phase = not_issued | phase; + SCpnt->SCp.Status = CHECK_CONDITION; + SCpnt->SCp.Message = 0; + SCpnt->SCp.have_data_in = 0; + SCpnt->SCp.sent_command = 0; + + if(SCpnt->SCp.phase & (resetting|check_condition)) { + if(SCpnt->host_scribble==0 || SCSEM(SCpnt) || SCNEXT(SCpnt)) { + printk(ERR_LEAD "cannot reuse command\n", CMDINFO(SCpnt)); + return FAILED; + } + } else { + SCpnt->host_scribble = kmalloc(sizeof(struct aha152x_scdata), GFP_ATOMIC); + if(SCpnt->host_scribble==0) { + printk(ERR_LEAD "allocation failed\n", CMDINFO(SCpnt)); + return FAILED; + } + } + + SCNEXT(SCpnt) = NULL; + SCSEM(SCpnt) = sem; + + /* setup scratch area + SCp.ptr : buffer pointer + SCp.this_residual : buffer length + SCp.buffer : next buffer + SCp.buffers_residual : left buffers in list + SCp.phase : current state of the command */ + if (SCpnt->use_sg) { + SCpnt->SCp.buffer = (struct scatterlist *) SCpnt->request_buffer; + SCpnt->SCp.ptr = SG_ADDRESS(SCpnt->SCp.buffer); + SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length; + SCpnt->SCp.buffers_residual = SCpnt->use_sg - 1; + } else { + SCpnt->SCp.ptr = (char *) SCpnt->request_buffer; + SCpnt->SCp.this_residual = SCpnt->request_bufflen; + SCpnt->SCp.buffer = NULL; + SCpnt->SCp.buffers_residual = 0; + } + + 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(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) +{ +#if 0 + if(*SCpnt->cmnd == REQUEST_SENSE) { + SCpnt->result = 0; + done(SCpnt); + + return 0; + } +#endif + + return aha152x_internal_queue(SCpnt, NULL, 0, done); +} + + +/* + * + * + */ +static void reset_done(Scsi_Cmnd *SCpnt) +{ +#if 0 + struct Scsi_Host *shpnt = SCpnt->host; + DPRINTK(debug_eh, INFO_LEAD "reset_done called\n", CMDINFO(SCpnt)); +#endif + if(SCSEM(SCpnt)) { + up(SCSEM(SCpnt)); + } else { + printk(KERN_ERR "aha152x: reset_done w/o semaphore\n"); + } +} + +/* + * Abort a command + * + */ +static int aha152x_abort(Scsi_Cmnd *SCpnt) +{ + struct Scsi_Host *shpnt = SCpnt->device->host; + Scsi_Cmnd *ptr; + unsigned long flags; + +#if defined(AHA152X_DEBUG) + if(HOSTDATA(shpnt)->debug & debug_eh) { + printk(DEBUG_LEAD "abort(%p)", CMDINFO(SCpnt), SCpnt); + show_queues(shpnt); + } +#endif + + DO_LOCK(flags); + + ptr=remove_SC(&ISSUE_SC, SCpnt); + + if(ptr) { + DPRINTK(debug_eh, DEBUG_LEAD "not yet issued - SUCCESS\n", CMDINFO(SCpnt)); + + 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? + * + */ + + printk(ERR_LEAD "cannot abort running or disconnected command\n", CMDINFO(SCpnt)); + + return FAILED; +} + +static void timer_expired(unsigned long p) +{ + Scsi_Cmnd *SCp = (Scsi_Cmnd *)p; + struct semaphore *sem = SCSEM(SCp); + struct Scsi_Host *shpnt = SCp->device->host; + unsigned long flags; + + /* remove command from issue queue */ + DO_LOCK(flags); + remove_SC(&ISSUE_SC, SCp); |