summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/atari_NCR5380.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/atari_NCR5380.c')
-rw-r--r--drivers/scsi/atari_NCR5380.c2266
1 files changed, 1015 insertions, 1251 deletions
diff --git a/drivers/scsi/atari_NCR5380.c b/drivers/scsi/atari_NCR5380.c
index db87ece6edb2..e65478651ca9 100644
--- a/drivers/scsi/atari_NCR5380.c
+++ b/drivers/scsi/atari_NCR5380.c
@@ -1,15 +1,15 @@
/*
* NCR 5380 generic driver routines. These should make it *trivial*
- * to implement 5380 SCSI drivers under Linux with a non-trantor
- * architecture.
+ * to implement 5380 SCSI drivers under Linux with a non-trantor
+ * architecture.
*
- * Note that these routines also work with NR53c400 family chips.
+ * Note that these routines also work with NR53c400 family chips.
*
* Copyright 1993, Drew Eckhardt
- * Visionary Computing
- * (Unix and Linux consulting and custom programming)
- * drew@colorado.edu
- * +1 (303) 666-5836
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 666-5836
*
* For more information, please consult
*
@@ -24,84 +24,10 @@
* 1+ (800) 334-5454
*/
-/*
- * ++roman: To port the 5380 driver to the Atari, I had to do some changes in
- * this file, too:
- *
- * - Some of the debug statements were incorrect (undefined variables and the
- * like). I fixed that.
- *
- * - In information_transfer(), I think a #ifdef was wrong. Looking at the
- * possible DMA transfer size should also happen for REAL_DMA. I added this
- * in the #if statement.
- *
- * - When using real DMA, information_transfer() should return in a DATAOUT
- * phase after starting the DMA. It has nothing more to do.
- *
- * - The interrupt service routine should run main after end of DMA, too (not
- * only after RESELECTION interrupts). Additionally, it should _not_ test
- * for more interrupts after running main, since a DMA process may have
- * been started and interrupts are turned on now. The new int could happen
- * inside the execution of NCR5380_intr(), leading to recursive
- * calls.
- *
- * - I've added a function merge_contiguous_buffers() that tries to
- * merge scatter-gather buffers that are located at contiguous
- * physical addresses and can be processed with the same DMA setup.
- * Since most scatter-gather operations work on a page (4K) of
- * 4 buffers (1K), in more than 90% of all cases three interrupts and
- * DMA setup actions are saved.
- *
- * - I've deleted all the stuff for AUTOPROBE_IRQ, REAL_DMA_POLL, PSEUDO_DMA
- * and USLEEP, because these were messing up readability and will never be
- * needed for Atari SCSI.
- *
- * - I've revised the NCR5380_main() calling scheme (relax the 'main_running'
- * stuff), and 'main' is executed in a bottom half if awoken by an
- * interrupt.
- *
- * - The code was quite cluttered up by "#if (NDEBUG & NDEBUG_*) printk..."
- * constructs. In my eyes, this made the source rather unreadable, so I
- * finally replaced that by the *_PRINTK() macros.
- *
- */
-
-/*
- * Further development / testing that should be done :
- * 1. Test linked command handling code after Eric is ready with
- * the high level code.
- */
+/* Ported to Atari by Roman Hodek and others. */
/* Adapted for the sun3 by Sam Creasey. */
-#include <scsi/scsi_dbg.h>
-#include <scsi/scsi_transport_spi.h>
-
-#if (NDEBUG & NDEBUG_LISTS)
-#define LIST(x, y) \
- do { \
- printk("LINE:%d Adding %p to %p\n", \
- __LINE__, (void*)(x), (void*)(y)); \
- if ((x) == (y)) \
- udelay(5); \
- } while (0)
-#define REMOVE(w, x, y, z) \
- do { \
- printk("LINE:%d Removing: %p->%p %p->%p \n", \
- __LINE__, (void*)(w), (void*)(x), \
- (void*)(y), (void*)(z)); \
- if ((x) == (y)) \
- udelay(5); \
- } while (0)
-#else
-#define LIST(x,y)
-#define REMOVE(w,x,y,z)
-#endif
-
-#ifndef notyet
-#undef LINKED
-#endif
-
/*
* Design
*
@@ -126,17 +52,7 @@
* piece of hardware that requires you to sit in a loop polling for
* the REQ signal as long as you are connected. Some devices are
* brain dead (ie, many TEXEL CD ROM drives) and won't disconnect
- * while doing long seek operations.
- *
- * The workaround for this is to keep track of devices that have
- * disconnected. If the device hasn't disconnected, for commands that
- * should disconnect, we do something like
- *
- * while (!REQ is asserted) { sleep for N usecs; poll for M usecs }
- *
- * Some tweaking of N and M needs to be done. An algorithm based
- * on "time to data" would give the best results as long as short time
- * to datas (ie, on the same track) were considered, however these
+ * while doing long seek operations. [...] These
* broken devices are the exception rather than the rule and I'd rather
* spend my time optimizing for the normal case.
*
@@ -177,12 +93,10 @@
*
* These macros control options :
* AUTOSENSE - if defined, REQUEST SENSE will be performed automatically
- * for commands that return with a CHECK CONDITION status.
+ * for commands that return with a CHECK CONDITION status.
*
* DIFFERENTIAL - if defined, NCR53c81 chips will use external differential
- * transceivers.
- *
- * LINKED - if defined, linked commands are supported.
+ * transceivers.
*
* REAL_DMA - if defined, REAL DMA is used during the data transfer phases.
*
@@ -195,17 +109,17 @@
* NCR5380_write(register, value) - write to the specific register
*
* NCR5380_implementation_fields - additional fields needed for this
- * specific implementation of the NCR5380
+ * specific implementation of the NCR5380
*
* Either real DMA *or* pseudo DMA may be implemented
* REAL functions :
* NCR5380_REAL_DMA should be defined if real DMA is to be used.
* Note that the DMA setup functions should return the number of bytes
- * that they were able to program the controller for.
+ * that they were able to program the controller for.
*
* Also note that generic i386/PC versions of these macros are
- * available as NCR5380_i386_dma_write_setup,
- * NCR5380_i386_dma_read_setup, and NCR5380_i386_dma_residual.
+ * available as NCR5380_i386_dma_write_setup,
+ * NCR5380_i386_dma_read_setup, and NCR5380_i386_dma_residual.
*
* NCR5380_dma_write_setup(instance, src, count) - initialize
* NCR5380_dma_read_setup(instance, dst, count) - initialize
@@ -221,18 +135,8 @@
* possible) function may be used.
*/
-/* Macros ease life... :-) */
-#define SETUP_HOSTDATA(in) \
- struct NCR5380_hostdata *hostdata = \
- (struct NCR5380_hostdata *)(in)->hostdata
-#define HOSTDATA(in) ((struct NCR5380_hostdata *)(in)->hostdata)
-
-#define NEXT(cmd) ((struct scsi_cmnd *)(cmd)->host_scribble)
-#define SET_NEXT(cmd,next) ((cmd)->host_scribble = (void *)(next))
-#define NEXTADDR(cmd) ((struct scsi_cmnd **)&(cmd)->host_scribble)
-
-#define HOSTNO instance->host_no
-#define H_NO(cmd) (cmd)->device->host->host_no
+static int do_abort(struct Scsi_Host *);
+static void do_reset(struct Scsi_Host *);
#ifdef SUPPORT_TAGS
@@ -251,9 +155,7 @@
* cannot know it in advance :-( We just see a QUEUE_FULL status being
* returned. So, in this case, the driver internal queue size assumption is
* reduced to the number of active tags if QUEUE_FULL is returned by the
- * target. The command is returned to the mid-level, but with status changed
- * to BUSY, since --as I've seen-- the mid-level can't handle QUEUE_FULL
- * correctly.
+ * target.
*
* We're also not allowed running tagged commands as long as an untagged
* command is active. And REQUEST SENSE commands after a contingent allegiance
@@ -304,7 +206,8 @@ static void __init init_tags(struct NCR5380_hostdata *hostdata)
static int is_lun_busy(struct scsi_cmnd *cmd, int should_be_tagged)
{
u8 lun = cmd->device->lun;
- SETUP_HOSTDATA(cmd->device->host);
+ struct Scsi_Host *instance = cmd->device->host;
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
if (hostdata->busy[cmd->device->id] & (1 << lun))
return 1;
@@ -314,8 +217,8 @@ static int is_lun_busy(struct scsi_cmnd *cmd, int should_be_tagged)
return 0;
if (hostdata->TagAlloc[scmd_id(cmd)][lun].nr_allocated >=
hostdata->TagAlloc[scmd_id(cmd)][lun].queue_size) {
- dprintk(NDEBUG_TAGS, "scsi%d: target %d lun %d: no free tags\n",
- H_NO(cmd), cmd->device->id, lun);
+ dsprintk(NDEBUG_TAGS, instance, "target %d lun %d: no free tags\n",
+ scmd_id(cmd), lun);
return 1;
}
return 0;
@@ -330,7 +233,8 @@ static int is_lun_busy(struct scsi_cmnd *cmd, int should_be_tagged)
static void cmd_get_tag(struct scsi_cmnd *cmd, int should_be_tagged)
{
u8 lun = cmd->device->lun;
- SETUP_HOSTDATA(cmd->device->host);
+ struct Scsi_Host *instance = cmd->device->host;
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
/* If we or the target don't support tagged queuing, allocate the LUN for
* an untagged command.
@@ -340,18 +244,16 @@ static void cmd_get_tag(struct scsi_cmnd *cmd, int should_be_tagged)
!cmd->device->tagged_supported) {
cmd->tag = TAG_NONE;
hostdata->busy[cmd->device->id] |= (1 << lun);
- dprintk(NDEBUG_TAGS, "scsi%d: target %d lun %d now allocated by untagged "
- "command\n", H_NO(cmd), cmd->device->id, lun);
+ dsprintk(NDEBUG_TAGS, instance, "target %d lun %d now allocated by untagged command\n",
+ scmd_id(cmd), lun);
} else {
struct tag_alloc *ta = &hostdata->TagAlloc[scmd_id(cmd)][lun];
cmd->tag = find_first_zero_bit(ta->allocated, MAX_TAGS);
set_bit(cmd->tag, ta->allocated);
ta->nr_allocated++;
- dprintk(NDEBUG_TAGS, "scsi%d: using tag %d for target %d lun %d "
- "(now %d tags in use)\n",
- H_NO(cmd), cmd->tag, cmd->device->id,
- lun, ta->nr_allocated);
+ dsprintk(NDEBUG_TAGS, instance, "using tag %d for target %d lun %d (%d tags allocated)\n",
+ cmd->tag, scmd_id(cmd), lun, ta->nr_allocated);
}
}
@@ -363,21 +265,22 @@ static void cmd_get_tag(struct scsi_cmnd *cmd, int should_be_tagged)
static void cmd_free_tag(struct scsi_cmnd *cmd)
{
u8 lun = cmd->device->lun;
- SETUP_HOSTDATA(cmd->device->host);
+ struct Scsi_Host *instance = cmd->device->host;
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
if (cmd->tag == TAG_NONE) {
hostdata->busy[cmd->device->id] &= ~(1 << lun);
- dprintk(NDEBUG_TAGS, "scsi%d: target %d lun %d untagged cmd finished\n",
- H_NO(cmd), cmd->device->id, lun);
+ dsprintk(NDEBUG_TAGS, instance, "target %d lun %d untagged cmd freed\n",
+ scmd_id(cmd), lun);
} else if (cmd->tag >= MAX_TAGS) {
- printk(KERN_NOTICE "scsi%d: trying to free bad tag %d!\n",
- H_NO(cmd), cmd->tag);
+ shost_printk(KERN_NOTICE, instance,
+ "trying to free bad tag %d!\n", cmd->tag);
} else {
struct tag_alloc *ta = &hostdata->TagAlloc[scmd_id(cmd)][lun];
clear_bit(cmd->tag, ta->allocated);
ta->nr_allocated--;
- dprintk(NDEBUG_TAGS, "scsi%d: freed tag %d for target %d lun %d\n",
- H_NO(cmd), cmd->tag, cmd->device->id, lun);
+ dsprintk(NDEBUG_TAGS, instance, "freed tag %d for target %d lun %d\n",
+ cmd->tag, scmd_id(cmd), lun);
}
}
@@ -401,17 +304,15 @@ static void free_all_tags(struct NCR5380_hostdata *hostdata)
#endif /* SUPPORT_TAGS */
-
-/*
- * Function: void merge_contiguous_buffers( struct scsi_cmnd *cmd )
- *
- * Purpose: Try to merge several scatter-gather requests into one DMA
- * transfer. This is possible if the scatter buffers lie on
- * physical contiguous addresses.
- *
- * Parameters: struct scsi_cmnd *cmd
- * The command to work on. The first scatter buffer's data are
- * assumed to be already transferred into ptr/this_residual.
+/**
+ * merge_contiguous_buffers - coalesce scatter-gather list entries
+ * @cmd: command requesting IO
+ *
+ * Try to merge several scatter-gather buffers into one DMA transfer.
+ * This is possible if the scatter buffers lie on physically
+ * contiguous addresses. The first scatter-gather buffer's data are
+ * assumed to be already transferred into cmd->SCp.this_residual.
+ * Every buffer merged avoids an interrupt and a DMA setup operation.
*/
static void merge_contiguous_buffers(struct scsi_cmnd *cmd)
@@ -463,9 +364,7 @@ static inline void initialize_SCp(struct scsi_cmnd *cmd)
cmd->SCp.buffers_residual = scsi_sg_count(cmd) - 1;
cmd->SCp.ptr = sg_virt(cmd->SCp.buffer);
cmd->SCp.this_residual = cmd->SCp.buffer->length;
- /* ++roman: Try to merge some scatter-buffers if they are at
- * contiguous physical addresses.
- */
+
merge_contiguous_buffers(cmd);
} else {
cmd->SCp.buffer = NULL;
@@ -473,31 +372,110 @@ static inline void initialize_SCp(struct scsi_cmnd *cmd)
cmd->SCp.ptr = NULL;
cmd->SCp.this_residual = 0;
}
+
+ cmd->SCp.Status = 0;
+ cmd->SCp.Message = 0;
+}
+
+/**
+ * NCR5380_poll_politely2 - wait for two chip register values
+ * @instance: controller to poll
+ * @reg1: 5380 register to poll
+ * @bit1: Bitmask to check
+ * @val1: Expected value
+ * @reg2: Second 5380 register to poll
+ * @bit2: Second bitmask to check
+ * @val2: Second expected value
+ * @wait: Time-out in jiffies
+ *
+ * Polls the chip in a reasonably efficient manner waiting for an
+ * event to occur. After a short quick poll we begin to yield the CPU
+ * (if possible). In irq contexts the time-out is arbitrarily limited.
+ * Callers may hold locks as long as they are held in irq mode.
+ *
+ * Returns 0 if either or both event(s) occurred otherwise -ETIMEDOUT.
+ */
+
+static int NCR5380_poll_politely2(struct Scsi_Host *instance,
+ int reg1, int bit1, int val1,
+ int reg2, int bit2, int val2, int wait)
+{
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+ unsigned long deadline = jiffies + wait;
+ unsigned long n;
+
+ /* Busy-wait for up to 10 ms */
+ n = min(10000U, jiffies_to_usecs(wait));
+ n *= hostdata->accesses_per_ms;
+ n /= 2000;
+ do {
+ if ((NCR5380_read(reg1) & bit1) == val1)
+ return 0;
+ if ((NCR5380_read(reg2) & bit2) == val2)
+ return 0;
+ cpu_relax();
+ } while (n--);
+
+ if (irqs_disabled() || in_interrupt())
+ return -ETIMEDOUT;
+
+ /* Repeatedly sleep for 1 ms until deadline */
+ while (time_is_after_jiffies(deadline)) {
+ schedule_timeout_uninterruptible(1);
+ if ((NCR5380_read(reg1) & bit1) == val1)
+ return 0;
+ if ((NCR5380_read(reg2) & bit2) == val2)
+ return 0;
+ }
+
+ return -ETIMEDOUT;
}
-#include <linux/delay.h>
+static inline int NCR5380_poll_politely(struct Scsi_Host *instance,
+ int reg, int bit, int val, int wait)
+{
+ return NCR5380_poll_politely2(instance, reg, bit, val,
+ reg, bit, val, wait);
+}
#if NDEBUG
static struct {
unsigned char mask;
const char *name;
} signals[] = {
- { SR_DBP, "PARITY"}, { SR_RST, "RST" }, { SR_BSY, "BSY" },
- { SR_REQ, "REQ" }, { SR_MSG, "MSG" }, { SR_CD, "CD" }, { SR_IO, "IO" },
- { SR_SEL, "SEL" }, {0, NULL}
-}, basrs[] = {
- {BASR_ATN, "ATN"}, {BASR_ACK, "ACK"}, {0, NULL}
-}, icrs[] = {
- {ICR_ASSERT_RST, "ASSERT RST"},{ICR_ASSERT_ACK, "ASSERT ACK"},
- {ICR_ASSERT_BSY, "ASSERT BSY"}, {ICR_ASSERT_SEL, "ASSERT SEL"},
- {ICR_ASSERT_ATN, "ASSERT ATN"}, {ICR_ASSERT_DATA, "ASSERT DATA"},
+ {SR_DBP, "PARITY"},
+ {SR_RST, "RST"},
+ {SR_BSY, "BSY"},
+ {SR_REQ, "REQ"},
+ {SR_MSG, "MSG"},
+ {SR_CD, "CD"},
+ {SR_IO, "IO"},
+ {SR_SEL, "SEL"},
+ {0, NULL}
+},
+basrs[] = {
+ {BASR_ATN, "ATN"},
+ {BASR_ACK, "ACK"},
{0, NULL}
-}, mrs[] = {
- {MR_BLOCK_DMA_MODE, "MODE BLOCK DMA"}, {MR_TARGET, "MODE TARGET"},
- {MR_ENABLE_PAR_CHECK, "MODE PARITY CHECK"}, {MR_ENABLE_PAR_INTR,
- "MODE PARITY INTR"}, {MR_ENABLE_EOP_INTR,"MODE EOP INTR"},
+},
+icrs[] = {
+ {ICR_ASSERT_RST, "ASSERT RST"},
+ {ICR_ASSERT_ACK, "ASSERT ACK"},
+ {ICR_ASSERT_BSY, "ASSERT BSY"},
+ {ICR_ASSERT_SEL, "ASSERT SEL"},
+ {ICR_ASSERT_ATN, "ASSERT ATN"},
+ {ICR_ASSERT_DATA, "ASSERT DATA"},
+ {0, NULL}
+},
+mrs[] = {
+ {MR_BLOCK_DMA_MODE, "MODE BLOCK DMA"},
+ {MR_TARGET, "MODE TARGET"},
+ {MR_ENABLE_PAR_CHECK, "MODE PARITY CHECK"},
+ {MR_ENABLE_PAR_INTR, "MODE PARITY INTR"},
+ {MR_ENABLE_EOP_INTR, "MODE EOP INTR"},
{MR_MONITOR_BSY, "MODE MONITOR BSY"},
- {MR_DMA_MODE, "MODE DMA"}, {MR_ARBITRATE, "MODE ARBITRATION"},
+ {MR_DMA_MODE, "MODE DMA"},
+ {MR_ARBITRATE, "MODE ARBITRATION"},
{0, NULL}
};
@@ -511,15 +489,13 @@ static struct {
static void NCR5380_print(struct Scsi_Host *instance)
{
unsigned char status, data, basr, mr, icr, i;
- unsigned long flags;
- local_irq_save(flags);
data = NCR5380_read(CURRENT_SCSI_DATA_REG);
status = NCR5380_read(STATUS_REG);
mr = NCR5380_read(MODE_REG);
icr = NCR5380_read(INITIATOR_COMMAND_REG);
basr = NCR5380_read(BUS_AND_STATUS_REG);
- local_irq_restore(flags);
+
printk("STATUS_REG: %02x ", status);
for (i = 0; signals[i].mask; ++i)
if (status & signals[i].mask)
@@ -543,8 +519,12 @@ static struct {
unsigned char value;
const char *name;
} phases[] = {
- {PHASE_DATAOUT, "DATAOUT"}, {PHASE_DATAIN, "DATAIN"}, {PHASE_CMDOUT, "CMDOUT"},
- {PHASE_STATIN, "STATIN"}, {PHASE_MSGOUT, "MSGOUT"}, {PHASE_MSGIN, "MSGIN"},
+ {PHASE_DATAOUT, "DATAOUT"},
+ {PHASE_DATAIN, "DATAIN"},
+ {PHASE_CMDOUT, "CMDOUT"},
+ {PHASE_STATIN, "STATIN"},
+ {PHASE_MSGOUT, "MSGOUT"},
+ {PHASE_MSGIN, "MSGIN"},
{PHASE_UNKNOWN, "UNKNOWN"}
};
@@ -553,8 +533,6 @@ static struct {
* @instance: adapter to dump
*
* Print the current SCSI phase for debugging purposes
- *
- * Locks: none
*/
static void NCR5380_print_phase(struct Scsi_Host *instance)
@@ -564,54 +542,21 @@ static void NCR5380_print_phase(struct Scsi_Host *instance)
status = NCR5380_read(STATUS_REG);
if (!(status & SR_REQ))
- printk(KERN_DEBUG "scsi%d: REQ not asserted, phase unknown.\n", HOSTNO);
+ shost_printk(KERN_DEBUG, instance, "REQ not asserted, phase unknown.\n");
else {
for (i = 0; (phases[i].value != PHASE_UNKNOWN) &&
(phases[i].value != (status & PHASE_MASK)); ++i)
;
- printk(KERN_DEBUG "scsi%d: phase %s\n", HOSTNO, phases[i].name);
+ shost_printk(KERN_DEBUG, instance, "phase %s\n", phases[i].name);
}
}
-
#endif
-/*
- * ++roman: New scheme of calling NCR5380_main()
- *
- * If we're not in an interrupt, we can call our main directly, it cannot be
- * already running. Else, we queue it on a task queue, if not 'main_running'
- * tells us that a lower level is already executing it. This way,
- * 'main_running' needs not be protected in a special way.
- *
- * queue_main() is a utility function for putting our main onto the task
- * queue, if main_running is false. It should be called only from a
- * interrupt or bottom half.
- */
-
-#include <linux/gfp.h>
-#include <linux/workqueue.h>
-#include <linux/interrupt.h>
-
-static inline void queue_main(struct NCR5380_hostdata *hostdata)
-{
- if (!hostdata->main_running) {
- /* If in interrupt and NCR5380_main() not already running,
- queue it on the 'immediate' task queue, to be processed
- immediately after the current interrupt processing has
- finished. */
- schedule_work(&hostdata->main_task);
- }
- /* else: nothing to do: the running NCR5380_main() will pick up
- any newly queued command. */
-}
-
/**
* NCR58380_info - report driver and host information
* @instance: relevant scsi host instance
*
* For use as the host template info() handler.
- *
- * Locks: none
*/
static const char *NCR5380_info(struct Scsi_Host *instance)
@@ -630,13 +575,14 @@ static void prepare_info(struct Scsi_Host *instance)
"base 0x%lx, irq %d, "
"can_queue %d, cmd_per_lun %d, "
"sg_tablesize %d, this_id %d, "
- "flags { %s}, "
+ "flags { %s%s}, "
"options { %s} ",
instance->hostt->name, instance->io_port, instance->n_io_port,
instance->base, instance->irq,
instance->can_queue, instance->cmd_per_lun,
instance->sg_tablesize, instance->this_id,
hostdata->flags & FLAG_TAGGED_QUEUING ? "TAGGED_QUEUING " : "",
+ hostdata->flags & FLAG_TOSHIBA_DELAY ? "TOSHIBA_DELAY " : "",
#ifdef DIFFERENTIAL
"DIFFERENTIAL "
#endif
@@ -653,102 +599,6 @@ static void prepare_info(struct Scsi_Host *instance)
}
/**
- * NCR5380_print_status - dump controller info
- * @instance: controller to dump
- *
- * Print commands in the various queues, called from NCR5380_abort
- * to aid debugging.
- */
-
-static void lprint_Scsi_Cmnd(struct scsi_cmnd *cmd)
-{
- int i, s;
- unsigned char *command;
- printk("scsi%d: destination target %d, lun %llu\n",
- H_NO(cmd), cmd->device->id, cmd->device->lun);
- printk(KERN_CONT " command = ");
- command = cmd->cmnd;
- printk(KERN_CONT "%2d (0x%02x)", command[0], command[0]);
- for (i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i)
- printk(KERN_CONT " %02x", command[i]);
- printk("\n");
-}
-
-static void NCR5380_print_status(struct Scsi_Host *instance)
-{
- struct NCR5380_hostdata *hostdata;
- struct scsi_cmnd *ptr;
- unsigned long flags;
-
- NCR5380_dprint(NDEBUG_ANY, instance);
- NCR5380_dprint_phase(NDEBUG_ANY, instance);
-
- hostdata = (struct NCR5380_hostdata *)instance->hostdata;
-
- local_irq_save(flags);
- printk("NCR5380: coroutine is%s running.\n",
- hostdata->main_running ? "" : "n't");
- if (!hostdata->connected)
- printk("scsi%d: no currently connected command\n", HOSTNO);
- else
- lprint_Scsi_Cmnd((struct scsi_cmnd *) hostdata->connected);
- printk("scsi%d: issue_queue\n", HOSTNO);
- for (ptr = (struct scsi_cmnd *)hostdata->issue_queue; ptr; ptr = NEXT(ptr))
- lprint_Scsi_Cmnd(ptr);
-
- printk("scsi%d: disconnected_queue\n", HOSTNO);
- for (ptr = (struct scsi_cmnd *) hostdata->disconnected_queue; ptr;
- ptr = NEXT(ptr))
- lprint_Scsi_Cmnd(ptr);
-
- local_irq_restore(flags);
- printk("\n");
-}
-
-static void show_Scsi_Cmnd(struct scsi_cmnd *cmd, struct seq_file *m)
-{
- int i, s;
- unsigned char *command;
- seq_printf(m, "scsi%d: destination target %d, lun %llu\n",
- H_NO(cmd), cmd->device->id, cmd->device->lun);
- seq_puts(m, " command = ");
- command = cmd->cmnd;
- seq_printf(m, "%2d (0x%02x)", command[0], command[0]);
- for (i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i)
- seq_printf(m, " %02x", command[i]);
- seq_putc(m, '\n');
-}
-
-static int __maybe_unused NCR5380_show_info(struct seq_file *m,
- struct Scsi_Host *instance)
-{
- struct NCR5380_hostdata *hostdata;
- struct scsi_cmnd *ptr;
- unsigned long flags;
-
- hostdata = (struct NCR5380_hostdata *)instance->hostdata;
-
- local_irq_save(flags);
- seq_printf(m, "NCR5380: coroutine is%s running.\n",
- hostdata->main_running ? "" : "n't");
- if (!hostdata->connected)
- seq_printf(m, "scsi%d: no currently connected command\n", HOSTNO);
- else
- show_Scsi_Cmnd((struct scsi_cmnd *) hostdata->connected, m);
- seq_printf(m, "scsi%d: issue_queue\n", HOSTNO);
- for (ptr = (struct scsi_cmnd *)hostdata->issue_queue; ptr; ptr = NEXT(ptr))
- show_Scsi_Cmnd(ptr, m);
-
- seq_printf(m, "scsi%d: disconnected_queue\n", HOSTNO);
- for (ptr = (struct scsi_cmnd *) hostdata->disconnected_queue; ptr;
- ptr = NEXT(ptr))
- show_Scsi_Cmnd(ptr, m);
-
- local_irq_restore(flags);
- return 0;
-}
-
-/**
* NCR5380_init - initialise an NCR5380
* @instance: adapter to configure
* @flags: control flags
@@ -764,11 +614,11 @@ static int __maybe_unused NCR5380_show_info(struct seq_file *m,
static int __init NCR5380_init(struct Scsi_Host *instance, int flags)
{
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
int i;
- SETUP_HOSTDATA(instance);
+ unsigned long deadline;
hostdata->host = instance;
- hostdata->aborted = 0;
hostdata->id_mask = 1 << instance->this_id;
hostdata->id_higher_mask = 0;
for (i = hostdata->id_mask; i <= 0x80; i <<= 1)
@@ -782,13 +632,21 @@ static int __init NCR5380_init(struct Scsi_Host *instance, int flags)
#if defined (REAL_DMA)
hostdata->dma_len = 0;
#endif
- hostdata->targets_present = 0;
+ spin_lock_init(&hostdata->lock);
hostdata->connected = NULL;
- hostdata->issue_queue = NULL;
- hostdata->disconnected_queue = NULL;
+ hostdata->sensing = NULL;
+ INIT_LIST_HEAD(&hostdata->autosense);
+ INIT_LIST_HEAD(&hostdata->unissued);
+ INIT_LIST_HEAD(&hostdata->disconnected);
+
hostdata->flags = flags;
INIT_WORK(&hostdata->main_task, NCR5380_main);
+ hostdata->work_q = alloc_workqueue("ncr5380_%d",
+ WQ_UNBOUND | WQ_MEM_RECLAIM,
+ 1, instance->host_no);
+ if (!hostdata->work_q)
+ return -ENOMEM;
prepare_info(instance);
@@ -797,6 +655,72 @@ static int __init NCR5380_init(struct Scsi_Host *instance, int flags)
NCR5380_write(TARGET_COMMAND_REG, 0);
NCR5380_write(SELECT_ENABLE_REG, 0);
+ /* Calibrate register polling loop */
+ i = 0;
+ deadline = jiffies + 1;
+ do {
+ cpu_relax();
+ } while (time_is_after_jiffies(deadline));
+ deadline += msecs_to_jiffies(256);
+ do {
+ NCR5380_read(STATUS_REG);
+ ++i;
+ cpu_relax();
+ } while (time_is_after_jiffies(deadline));
+ hostdata->accesses_per_ms = i / 256;
+
+ return 0;
+}
+
+/**
+ * NCR5380_maybe_reset_bus - Detect and correct bus wedge problems.
+ * @instance: adapter to check
+ *
+ * If the system crashed, it may have crashed with a connected target and
+ * the SCSI bus busy. Check for BUS FREE phase. If not, try to abort the
+ * currently established nexus, which we know nothing about. Failing that
+ * do a bus reset.
+ *
+ * Note that a bus reset will cause the chip to assert IRQ.
+ *
+ * Returns 0 if successful, otherwise -ENXIO.
+ */
+
+static int NCR5380_maybe_reset_bus(struct Scsi_Host *instance)
+{
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+ int pass;
+
+ for (pass = 1; (NCR5380_read(STATUS_REG) & SR_BSY) && pass <= 6; ++pass) {
+ switch (pass) {
+ case 1:
+ case 3:
+ case 5:
+ shost_printk(KERN_ERR, instance, "SCSI bus busy, waiting up to five seconds\n");
+ NCR5380_poll_politely(instance,
+ STATUS_REG, SR_BSY, 0, 5 * HZ);
+ break;
+ case 2:
+ shost_printk(KERN_ERR, instance, "bus busy, attempting abort\n");
+ do_abort(instance);
+ break;
+ case 4:
+ shost_printk(KERN_ERR, instance, "bus busy, attempting reset\n");
+ do_reset(instance);
+ /* Wait after a reset; the SCSI standard calls for
+ * 250ms, we wait 500ms to be on the safe side.
+ * But some Toshiba CD-ROMs need ten times that.
+ */
+ if (hostdata->flags & FLAG_TOSHIBA_DELAY)
+ msleep(2500);
+ else
+ msleep(500);
+ break;
+ case 6:
+ shost_printk(KERN_ERR, instance, "bus locked solid\n");
+ return -ENXIO;
+ }
+ }
return 0;
}
@@ -812,6 +736,38 @@ static void NCR5380_exit(struct Scsi_Host *instance)
struct NCR5380_hostdata *hostdata = shost_priv(instance);
cancel_work_sync(&hostdata->main_task);
+ destroy_workqueue(hostdata->work_q);
+}
+
+/**
+ * complete_cmd - finish processing a command and return it to the SCSI ML
+ * @instance: the host instance
+ * @cmd: command to complete
+ */
+
+static void complete_cmd(struct Scsi_Host *instance,
+ struct scsi_cmnd *cmd)
+{
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+
+ dsprintk(NDEBUG_QUEUES, instance, "complete_cmd: cmd %p\n", cmd);
+
+ if (hostdata->sensing == cmd) {
+ /* Autosense processing ends here */
+ if ((cmd->result & 0xff) != SAM_STAT_GOOD) {
+ scsi_eh_restore_cmnd(cmd, &hostdata->ses);
+ set_host_byte(cmd, DID_ERROR);
+ } else
+ scsi_eh_restore_cmnd(cmd, &hostdata->ses);
+ hostdata->sensing = NULL;
+ }
+
+#ifdef SUPPORT_TAGS
+ cmd_free_tag(cmd);
+#else
+ hostdata->busy[scmd_id(cmd)] &= ~(1 << cmd->device->lun);
+#endif
+ cmd->scsi_done(cmd);
}
/**
@@ -819,7 +775,7 @@ static void NCR5380_exit(struct Scsi_Host *instance)
* @instance: the relevant SCSI adapter
* @cmd: SCSI command
*
- * cmd is added to the per instance issue_queue, with minor
+ * cmd is added to the per-instance issue queue, with minor
* twiddling done to the host specific fields of cmd. If the
* main coroutine is not running, it is restarted.
*/
@@ -828,44 +784,23 @@ static int NCR5380_queue_command(struct Scsi_Host *instance,
struct scsi_cmnd *cmd)
{
struct NCR5380_hostdata *hostdata = shost_priv(instance);
- struct scsi_cmnd *tmp;
+ struct NCR5380_cmd *ncmd = scsi_cmd_priv(cmd);
unsigned long flags;
#if (NDEBUG & NDEBUG_NO_WRITE)
switch (cmd->cmnd[0]) {
case WRITE_6:
case WRITE_10:
- printk(KERN_NOTICE "scsi%d: WRITE attempted with NO_WRITE debugging flag set\n",
- H_NO(cmd));
+ shost_printk(KERN_DEBUG, instance, "WRITE attempted with NDEBUG_NO_WRITE set\n");
cmd->result = (DID_ERROR << 16);
cmd->scsi_done(cmd);
return 0;
}
#endif /* (NDEBUG & NDEBUG_NO_WRITE) */
- /*
- * We use the host_scribble field as a pointer to the next command
- * in a queue
- */
-
- SET_NEXT(cmd, NULL);
cmd->result = 0;
/*
- * Insert the cmd into the issue queue. Note that REQUEST SENSE
- * commands are added to the head of the queue since any command will
- * clear the contingent allegiance condition that exists and the
- * sense data is only guaranteed to be valid while the condition exists.
- */
-
- /* ++guenther: now that the issue queue is being set up, we can lock ST-DMA.
- * Otherwise a running NCR5380_main may steal the lock.
- * Lock before actually inserting due to fairness reasons explained in
- * atari_scsi.c. If we insert first, then it's impossible for this driver
- * to release the lock.
- * Stop timer for this command while waiting for the lock, or timeouts
- * may happen (and they really do), and it's no good if the command doesn't
- * appear in any of the queues.
* ++roman: Just disabling the NCR interrupt isn't sufficient here,
* because also a timer int can trigger an abort or reset, which would
* alter queues and touch the lock.
@@ -873,7 +808,7 @@ static int NCR5380_queue_command(struct Scsi_Host *instance,
if (!NCR5380_acquire_dma_irq(instance))
return SCSI_MLQUEUE_HOST_BUSY;
- local_irq_save(flags);
+ spin_lock_irqsave(&hostdata->lock, flags);
/*
* Insert the cmd into the issue queue. Note that REQUEST SENSE
@@ -882,33 +817,18 @@ static int NCR5380_queue_command(struct Scsi_Host *instance,
* sense data is only guaranteed to be valid while the condition exists.
*/
- if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) {
- LIST(cmd, hostdata->issue_queue);
- SET_NEXT(cmd, hostdata->issue_queue);
- hostdata->issue_queue = cmd;
- } else {
- for (tmp = (struct scsi_cmnd *)hostdata->issue_queue;
- NEXT(tmp); tmp = NEXT(tmp))
- ;
- LIST(cmd, tmp);
- SET_NEXT(tmp, cmd);
- }
- local_irq_restore(flags);
+ if (cmd->cmnd[0] == REQUEST_SENSE)
+ list_add(&ncmd->list, &hostdata->unissued);
+ else
+ list_add_tail(&ncmd->list, &hostdata->unissued);
- dprintk(NDEBUG_QUEUES, "scsi%d: command added to %s of queue\n", H_NO(cmd),
- (cmd->cmnd[0] == REQUEST_SENSE) ? "head" : "tail");
+ spin_unlock_irqrestore(&hostdata->lock, flags);
- /* If queue_command() is called from an interrupt (real one or bottom
- * half), we let queue_main() do the job of taking care about main. If it
- * is already running, this is a no-op, else main will be queued.
- *
- * If we're not in an interrupt, we can call NCR5380_main()
- * unconditionally, because it cannot be already running.
- */
- if (in_interrupt() || irqs_disabled())
- queue_main(hostdata);
- else
- NCR5380_main(&hostdata->main_task);
+ dsprintk(NDEBUG_QUEUES, instance, "command %p added to %s of queue\n",
+ cmd, (cmd->cmnd[0] == REQUEST_SENSE) ? "head" : "tail");
+
+ /* Kick off command processing */
+ queue_work(hostdata->work_q, &hostdata->main_task);
return 0;
}
@@ -917,22 +837,85 @@ static inline void maybe_release_dma_irq(struct Scsi_Host *instance)
struct NCR5380_hostdata *hostdata = shost_priv(instance);
/* Caller does the locking needed to set & test these data atomically */
- if (!hostdata->disconnected_queue &&
- !hostdata->issue_queue &&
+ if (list_empty(&hostdata->disconnected) &&
+ list_empty(&hostdata->unissued) &&
+ list_empty(&hostdata->autosense) &&
!hostdata->connected &&
- !hostdata->retain_dma_intr)
+ !hostdata->selecting)
NCR5380_release_dma_irq(instance);
}
/**
+ * dequeue_next_cmd - dequeue a command for processing
+ * @instance: the scsi host instance
+ *
+ * Priority is given to commands on the autosense queue. These commands
+ * need autosense because of a CHECK CONDITION result.
+ *
+ * Returns a command pointer if a command is found for a target that is
+ * not already busy. Otherwise returns NULL.
+ */
+
+static struct scsi_cmnd *dequeue_next_cmd(struct Scsi_Host *instance)
+{
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+ struct NCR5380_cmd *ncmd;
+ struct scsi_cmnd *cmd;
+
+ if (list_empty(&hostdata->autosense)) {
+ list_for_each_entry(ncmd, &hostdata->unissued, list) {
+ cmd = NCR5380_to_scmd(ncmd);
+ dsprintk(NDEBUG_QUEUES, instance, "dequeue: cmd=%p target=%d busy=0x%02x lun=%llu\n",
+ cmd, scmd_id(cmd), hostdata->busy[scmd_id(cmd)], cmd->device->lun);
+
+ if (
+#ifdef SUPPORT_TAGS
+ !is_lun_busy(cmd, 1)
+#else
+ !(hostdata->busy[scmd_id(cmd)] & (1 << cmd->device->lun))
+#endif
+ ) {
+ list_del(&ncmd->list);
+ dsprintk(NDEBUG_QUEUES, instance,
+ "dequeue: removed %p from issue queue\n", cmd);
+ return cmd;
+ }
+ }
+ } else {
+ /* Autosense processing begins here */
+ ncmd = list_first_entry(&hostdata->autosense,
+ struct NCR5380_cmd, list);
+ list_del(&ncmd->list);
+ cmd = NCR5380_to_scmd(ncmd);
+ dsprintk(NDEBUG_QUEUES, instance,
+ "dequeue: removed %p from autosense queue\n", cmd);
+ scsi_eh_prep_cmnd(cmd, &hostdata->ses, NULL, 0, ~0);
+ hostdata->sensing = cmd;
+ return cmd;
+ }
+ return NULL;
+}
+
+static void requeue_cmd(struct Scsi_Host *instance, struct scsi_cmnd *cmd)
+{
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+ struct NCR5380_cmd *ncmd = scsi_cmd_priv(cmd);
+
+ if (hostdata->sensing) {
+ scsi_eh_restore_cmnd(cmd, &hostdata->ses);
+ list_add(&ncmd->list, &hostdata->autosense);
+ hostdata->sensing = NULL;
+ } else
+ list_add(&ncmd->list, &hostdata->unissued);
+}
+
+/**
* NCR5380_main - NCR state machines
*
* NCR5380_main is a coroutine that runs as long as more work can
* be done on the NCR5380 host adapters in a system. Both
* NCR5380_queue_command() and NCR5380_intr() will try to start it
* in case it is not running.
- *
- * Locks: called as its own thread with no locks held.
*/
static void NCR5380_main(struct work_struct *work)
@@ -940,154 +923,69 @@ static void NCR5380_main(struct work_struct *work)
struct NCR5380_hostdata *hostdata =
container_of(work, struct NCR5380_hostdata, main_task);
struct Scsi_Host *instance = hostdata->host;
- struct scsi_cmnd *tmp, *prev;
+ struct scsi_cmnd *cmd;
int done;
- unsigned long flags;
/*
- * We run (with interrupts disabled) until we're sure that none of
- * the host adapters have anything that can be done, at which point
- * we set main_running to 0 and exit.
- *
- * Interrupts are enabled before doing various other internal
- * instructions, after we've decided that we need to run through
- * the loop again.
- *
- * this should prevent any race conditions.
- *
* ++roman: Just disabling the NCR interrupt isn't sufficient here,
* because also a timer int can trigger an abort or reset, which can
* alter queues and touch the Falcon lock.