cregit-Linux how code gets into the kernel

Release 4.11 drivers/scsi/NCR5380.c

Directory: drivers/scsi
/*
 * NCR 5380 generic driver routines.  These should make it *trivial*
 * to implement 5380 SCSI drivers under Linux with a non-trantor
 * architecture.
 *
 * 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
 *
 * For more information, please consult
 *
 * NCR 5380 Family
 * SCSI Protocol Controller
 * Databook
 *
 * NCR Microelectronics
 * 1635 Aeroplaza Drive
 * Colorado Springs, CO 80916
 * 1+ (719) 578-3400
 * 1+ (800) 334-5454
 */

/*
 * With contributions from Ray Van Tassle, Ingmar Baumgart,
 * Ronald van Cuijlenborg, Alan Cox and others.
 */

/* Ported to Atari by Roman Hodek and others. */

/* Adapted for the Sun 3 by Sam Creasey. */

/*
 * Design
 *
 * This is a generic 5380 driver.  To use it on a different platform,
 * one simply writes appropriate system specific macros (ie, data
 * transfer - some PC's will use the I/O bus, 68K's must use
 * memory mapped) and drops this file in their 'C' wrapper.
 *
 * As far as command queueing, two queues are maintained for
 * each 5380 in the system - commands that haven't been issued yet,
 * and commands that are currently executing.  This means that an
 * unlimited number of commands may be queued, letting
 * more commands propagate from the higher driver levels giving higher
 * throughput.  Note that both I_T_L and I_T_L_Q nexuses are supported,
 * allowing multiple commands to propagate all the way to a SCSI-II device
 * while a command is already executing.
 *
 *
 * Issues specific to the NCR5380 :
 *
 * When used in a PIO or pseudo-dma mode, the NCR5380 is a braindead
 * 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. [...] These
 * broken devices are the exception rather than the rule and I'd rather
 * spend my time optimizing for the normal case.
 *
 * Architecture :
 *
 * At the heart of the design is a coroutine, NCR5380_main,
 * which is started from a workqueue for each NCR5380 host in the
 * system.  It attempts to establish I_T_L or I_T_L_Q nexuses by
 * removing the commands from the issue queue and calling
 * NCR5380_select() if a nexus is not established.
 *
 * Once a nexus is established, the NCR5380_information_transfer()
 * phase goes through the various phases as instructed by the target.
 * if the target goes into MSG IN and sends a DISCONNECT message,
 * the command structure is placed into the per instance disconnected
 * queue, and NCR5380_main tries to find more work.  If the target is
 * idle for too long, the system will try to sleep.
 *
 * If a command has disconnected, eventually an interrupt will trigger,
 * calling NCR5380_intr()  which will in turn call NCR5380_reselect
 * to reestablish a nexus.  This will run main if necessary.
 *
 * On command termination, the done function will be called as
 * appropriate.
 *
 * SCSI pointers are maintained in the SCp field of SCSI command
 * structures, being initialized after the command is connected
 * in NCR5380_select, and set as appropriate in NCR5380_information_transfer.
 * Note that in violation of the standard, an implicit SAVE POINTERS operation
 * is done, since some BROKEN disks fail to issue an explicit SAVE POINTERS.
 */

/*
 * Using this file :
 * This file a skeleton Linux SCSI driver for the NCR 5380 series
 * of chips.  To use it, you write an architecture specific functions
 * and macros and include this file in your driver.
 *
 * These macros MUST be defined :
 *
 * NCR5380_read(register)  - read from the specified register
 *
 * NCR5380_write(register, value) - write to the specific register
 *
 * NCR5380_implementation_fields  - additional fields needed for this
 * specific implementation of the NCR5380
 *
 * Either real DMA *or* pseudo DMA may be implemented
 *
 * NCR5380_dma_xfer_len   - determine size of DMA/PDMA transfer
 * NCR5380_dma_send_setup - execute DMA/PDMA from memory to 5380
 * NCR5380_dma_recv_setup - execute DMA/PDMA from 5380 to memory
 * NCR5380_dma_residual   - residual byte count
 *
 * The generic driver is initialized by calling NCR5380_init(instance),
 * after setting the appropriate host specific fields and ID.
 */

#ifndef NCR5380_io_delay

#define NCR5380_io_delay(x)
#endif

#ifndef NCR5380_acquire_dma_irq

#define NCR5380_acquire_dma_irq(x)	(1)
#endif

#ifndef NCR5380_release_dma_irq

#define NCR5380_release_dma_irq(x)
#endif

static int do_abort(struct Scsi_Host *);
static void do_reset(struct Scsi_Host *);

/**
 * initialize_SCp - init the scsi pointer field
 * @cmd: command block to set up
 *
 * Set up the internal fields in the SCSI command.
 */


static inline void initialize_SCp(struct scsi_cmnd *cmd) { /* * Initialize the Scsi Pointer field so that all of the commands in the * various queues are valid. */ if (scsi_bufflen(cmd)) { cmd->SCp.buffer = scsi_sglist(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; } else { cmd->SCp.buffer = NULL; cmd->SCp.buffers_residual = 0; cmd->SCp.ptr = NULL; cmd->SCp.this_residual = 0; } cmd->SCp.Status = 0; cmd->SCp.Message = 0; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)8769.05%114.29%
Finn Thain2015.87%342.86%
Boaz Harrosh118.73%114.29%
Geert Uytterhoeven75.56%114.29%
Jens Axboe10.79%114.29%
Total126100.00%7100.00%

/** * NCR5380_poll_politely2 - wait for two chip register values * @hostdata: host private data * @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 NCR5380_hostdata *hostdata, unsigned int reg1, u8 bit1, u8 val1, unsigned int reg2, u8 bit2, u8 val2, unsigned long wait) { unsigned long n = hostdata->poll_loops; unsigned long deadline = jiffies + wait; 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; }

Contributors

PersonTokensPropCommitsCommitProp
Finn Thain10163.92%571.43%
Alan Cox5534.81%114.29%
Andrew Morton21.27%114.29%
Total158100.00%7100.00%

#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_END_DMA_TRANSFER, "END OF DMA"}, {BASR_DRQ, "DRQ"}, {BASR_PARITY_ERROR, "PARITY ERROR"}, {BASR_IRQ, "IRQ"}, {BASR_PHASE_MATCH, "PHASE MATCH"}, {BASR_BUSY_ERROR, "BUSY ERROR"}, {BASR_ATN, "ATN"}, {BASR_ACK, "ACK"}, {0, NULL} }, icrs[] = { {ICR_ASSERT_RST, "ASSERT RST"}, {ICR_ARBITRATION_PROGRESS, "ARB. IN PROGRESS"}, {ICR_ARBITRATION_LOST, "LOST ARB."}, {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, "BLOCK DMA MODE"}, {MR_TARGET, "TARGET"}, {MR_ENABLE_PAR_CHECK, "PARITY CHECK"}, {MR_ENABLE_PAR_INTR, "PARITY INTR"}, {MR_ENABLE_EOP_INTR, "EOP INTR"}, {MR_MONITOR_BSY, "MONITOR BSY"}, {MR_DMA_MODE, "DMA MODE"}, {MR_ARBITRATE, "ARBITRATE"}, {0, NULL} }; /** * NCR5380_print - print scsi bus signals * @instance: adapter state to dump * * Print the SCSI bus signals for debugging purposes */
static void NCR5380_print(struct Scsi_Host *instance) { struct NCR5380_hostdata *hostdata = shost_priv(instance); unsigned char status, data, basr, mr, icr, i; 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); printk(KERN_DEBUG "SR = 0x%02x : ", status); for (i = 0; signals[i].mask; ++i) if (status & signals[i].mask) printk(KERN_CONT "%s, ", signals[i].name); printk(KERN_CONT "\nBASR = 0x%02x : ", basr); for (i = 0; basrs[i].mask; ++i) if (basr & basrs[i].mask) printk(KERN_CONT "%s, ", basrs[i].name); printk(KERN_CONT "\nICR = 0x%02x : ", icr); for (i = 0; icrs[i].mask; ++i) if (icr & icrs[i].mask) printk(KERN_CONT "%s, ", icrs[i].name); printk(KERN_CONT "\nMR = 0x%02x : ", mr); for (i = 0; mrs[i].mask; ++i) if (mr & mrs[i].mask) printk(KERN_CONT "%s, ", mrs[i].name); printk(KERN_CONT "\n"); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)24189.93%250.00%
Finn Thain2710.07%250.00%
Total268100.00%4100.00%

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_UNKNOWN, "UNKNOWN"} }; /** * NCR5380_print_phase - show SCSI phase * @instance: adapter to dump * * Print the current SCSI phase for debugging purposes */
static void NCR5380_print_phase(struct Scsi_Host *instance) { struct NCR5380_hostdata *hostdata = shost_priv(instance); unsigned char status; int i; status = NCR5380_read(STATUS_REG); if (!(status & SR_REQ)) 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) ; shost_printk(KERN_DEBUG, instance, "phase %s\n", phases[i].name); } }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)9083.33%133.33%
Finn Thain1816.67%266.67%
Total108100.00%3100.00%

#endif /** * NCR5380_info - report driver and host information * @instance: relevant scsi host instance * * For use as the host template info() handler. */
static const char *NCR5380_info(struct Scsi_Host *instance) { struct NCR5380_hostdata *hostdata = shost_priv(instance); return hostdata->info; }

Contributors

PersonTokensPropCommitsCommitProp
Finn Thain28100.00%1100.00%
Total28100.00%1100.00%

/** * NCR5380_init - initialise an NCR5380 * @instance: adapter to configure * @flags: control flags * * Initializes *instance and corresponding 5380 chip, * with flags OR'd into the initial flags value. * * Notes : I assume that the host, hostno, and id bits have been * set correctly. I don't care about the irq and other fields. * * Returns 0 for success */
static int NCR5380_init(struct Scsi_Host *instance, int flags) { struct NCR5380_hostdata *hostdata = shost_priv(instance); int i; unsigned long deadline; unsigned long accesses_per_ms; instance->max_lun = 7; hostdata->host = instance; hostdata->id_mask = 1 << instance->this_id; hostdata->id_higher_mask = 0; for (i = hostdata->id_mask; i <= 0x80; i <<= 1) if (i > hostdata->id_mask) hostdata->id_higher_mask |= i; for (i = 0; i < 8; ++i) hostdata->busy[i] = 0; hostdata->dma_len = 0; spin_lock_init(&hostdata->lock); hostdata->connected = 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; snprintf(hostdata->info, sizeof(hostdata->info), "%s, irq %d, io_port 0x%lx, base 0x%lx, can_queue %d, cmd_per_lun %d, sg_tablesize %d, this_id %d, flags { %s%s%s}", instance->hostt->name, instance->irq, hostdata->io_port, hostdata->base, instance->can_queue, instance->cmd_per_lun, instance->sg_tablesize, instance->this_id, hostdata->flags & FLAG_DMA_FIXUP ? "DMA_FIXUP " : "", hostdata->flags & FLAG_NO_PSEUDO_DMA ? "NO_PSEUDO_DMA " : "", hostdata->flags & FLAG_TOSHIBA_DELAY ? "TOSHIBA_DELAY " : ""); NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); NCR5380_write(MODE_REG, MR_BASE); 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)); accesses_per_ms = i / 256; hostdata->poll_loops = NCR5380_REG_POLL_TIME * accesses_per_ms / 2; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Finn Thain25665.31%1578.95%
Linus Torvalds (pre-git)12632.14%210.53%
Alan Cox92.30%15.26%
Dave Jones10.26%15.26%
Total392100.00%19100.00%

/** * 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(hostdata, 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; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)8149.69%222.22%
Finn Thain5936.20%444.44%
James Bottomley127.36%111.11%
Alan Cox116.75%222.22%
Total163100.00%9100.00%

/** * NCR5380_exit - remove an NCR5380 * @instance: adapter to remove * * Assumes that no more work can be queued (e.g. by NCR5380_intr). */
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); }

Contributors

PersonTokensPropCommitsCommitProp
James Bottomley2158.33%116.67%
Finn Thain1233.33%350.00%
Linus Torvalds (pre-git)25.56%116.67%
Dave Jones12.78%116.67%
Total36100.00%6100.00%

/** * 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; } hostdata->busy[scmd_id(cmd)] &= ~(1 << cmd->device->lun); cmd->scsi_done(cmd); }

Contributors

PersonTokensPropCommitsCommitProp
Finn Thain124100.00%2100.00%
Total124100.00%2100.00%

/** * NCR5380_queue_command - queue a command * @instance: the relevant SCSI adapter * @cmd: SCSI command * * 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. */
static int NCR5380_queue_command(struct Scsi_Host *instance, struct scsi_cmnd *cmd) { struct NCR5380_hostdata *hostdata = shost_priv(instance); 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: 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) */ cmd->result = 0; if (!NCR5380_acquire_dma_irq(instance)) return SCSI_MLQUEUE_HOST_BUSY; spin_lock_irqsave(&hostdata->lock, flags); /* * 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. */ if (cmd->cmnd[0] == REQUEST_SENSE) list_add(&ncmd->list, &hostdata->unissued); else list_add_tail(&ncmd->list, &hostdata->unissued); spin_unlock_irqrestore(&hostdata->lock, flags); 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; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)11352.80%318.75%
Finn Thain9242.99%1062.50%
Alan Cox83.74%212.50%
Dave Jones10.47%16.25%
Total214100.00%16100.00%


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 (list_empty(&hostdata->disconnected) && list_empty(&hostdata->unissued) && list_empty(&hostdata->autosense) && !hostdata->connected && !hostdata->selecting) { NCR5380_release_dma_irq(instance); } }

Contributors

PersonTokensPropCommitsCommitProp
Finn Thain66100.00%2100.00%
Total66100.00%2100.00%

/** * 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 (hostdata->sensing || 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 (!(hostdata->busy[scmd_id(cmd)] & (1 << cmd->device->lun))) { 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; }

Contributors

PersonTokensPropCommitsCommitProp
Finn Thain19789.14%753.85%
Linus Torvalds (pre-git)188.14%323.08%
Alan Cox41.81%17.69%
James Bottomley10.45%17.69%
David Howells10.45%17.69%
Total221100.00%13100.00%


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 == cmd) { scsi_eh_restore_cmnd(cmd, &hostdata->ses); list_add(&ncmd->list, &hostdata->autosense); hostdata->sensing = NULL; } else list_add(&ncmd->list, &hostdata->unissued); }

Contributors

PersonTokensPropCommitsCommitProp
Finn Thain8797.75%480.00%
Linus Torvalds (pre-git)22.25%120.00%
Total89100.00%5100.00%

/** * 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. */
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; int done; do { done = 1; spin_lock_irq(&hostdata->lock); while (!hostdata->connected && !hostdata->selecting) { struct scsi_cmnd *cmd = dequeue_next_cmd(instance); if (!cmd) break; dsprintk(NDEBUG_MAIN, instance, "main: dequeued %p\n", cmd); /* * Attempt to establish an I_T_L nexus here. * On success, instance->hostdata->connected is set. * On failure, we must add the command back to the * issue queue so we can keep trying. */ /* * REQUEST SENSE commands are issued without tagged * queueing, even on SCSI-II devices because the * contingent allegiance condition exists for the * entire unit. */ if (!NCR5380_select(instance, cmd)) { dsprintk(NDEBUG_MAIN, instance, "main: select complete\n"); maybe_release_dma_irq(instance); } else { dsprintk(NDEBUG_MAIN | NDEBUG_QUEUES, instance, "main: select failed, returning %p to queue\n", cmd); requeue_cmd(instance, cmd); } } if (hostdata->connected && !hostdata->dma_len) { dsprintk(NDEBUG_MAIN, instance, "main: performing information transfer\n"); NCR5380_information_transfer(instance); done = 0; } spin_unlock_irq(&hostdata->lock); if (!done) cond_resched(); } while (!done); }

Contributors

PersonTokensPropCommitsCommitProp
Finn Thain14171.21%1359.09%
Linus Torvalds (pre-git)5226.26%522.73%
Hannes Reinecke21.01%14.55%
Dave Jones10.51%14.55%
Alan Cox10.51%14.55%
James Bottomley10.51%14.55%
Total198100.00%22100.00%

/* * NCR5380_dma_complete - finish DMA transfer * @instance: the scsi host instance * * Called by the interrupt handler when DMA finishes or a phase * mismatch occurs (which would end the DMA transfer). */
static void NCR5380_dma_complete(struct Scsi_Host *instance) { struct NCR5380_hostdata *hostdata = shost_priv(instance); int transferred; unsigned char **data; int *count; int saved_data = 0, overrun = 0; unsigned char p; if (hostdata->read_overruns) { p = hostdata->connected->SCp.phase; if (p & SR_IO) { udelay(10); if ((NCR5380_read(BUS_AND_STATUS_REG) & (BASR_PHASE_MATCH | BASR_ACK)) == (BASR_PHASE_MATCH | BASR_ACK)) { saved_data = NCR5380_read(INPUT_DATA_REG); overrun = 1; dsprintk(NDEBUG_DMA, instance, "read overrun handled\n"); } } } #ifdef CONFIG_SUN3 if ((sun3scsi_dma_finish(rq_data_dir(hostdata->connected->request)))) { pr_err("scsi%d: overrun in UDC counter -- not prepared to deal with this!\n", instance->host_no); BUG(); } if ((NCR5380_read(BUS_AND_STATUS_REG) & (BASR_PHASE_MATCH | BASR_ACK)) == (BASR_PHASE_MATCH | BASR_ACK)) { pr_err("scsi%d: BASR %02x\n", instance->host_no, NCR5380_read(BUS_AND_STATUS_REG)); pr_err("scsi%d: bus stuck in data phase -- probably a single byte overrun!\n", instance->host_no); BUG(); } #endif NCR5380_write(MODE_REG, MR_BASE); NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); NCR5380_read(RESET_PARITY_INTERRUPT_REG); transferred = hostdata->dma_len - NCR5380_dma_residual(hostdata); hostdata->dma_len = 0; data = (unsigned char **)&hostdata->connected->SCp.ptr; count = &hostdata->connected->SCp.this_residual; *data += transferred; *count -= transferred; if (hostdata->read_overruns) { int cnt, toPIO; if ((NCR5380_read(STATUS_REG) & PHASE_MASK) == p && (p & SR_IO)) { cnt = toPIO = hostdata->read_overruns; if (overrun) { dsprintk(NDEBUG_DMA, instance, "Got an input overrun, using saved byte\n"); *(*data)++ = saved_data; (*count)--; cnt--; toPIO--; } if (toPIO > 0) { dsprintk(NDEBUG_DMA, instance, "Doing %d byte PIO to 0x%p\n", cnt, *data); NCR5380_transfer_pio(instance, &p, &cnt, data); *count -= toPIO - cnt; } } } }

Contributors

PersonTokensPropCommitsCommitProp
Finn Thain399100.00%3100.00%
Total399100.00%3100.00%

/** * NCR5380_intr - generic NCR5380 irq handler * @irq: interrupt number * @dev_id: device info * * Handle interrupts, reestablishing I_T_L or I_T_L_Q nexuses * from the disconnected queue, and restarting NCR5380_main() * as required. * * The chip can assert IRQ in any of six different conditions. The IRQ flag * is then cleared by reading the Reset Parity/Interrupt Register (RPIR). * Three of these six conditions are latched in the Bus and Status Register: * - End of DMA (cleared by ending DMA Mode) * - Parity error (cleared by reading RPIR) * - Loss of BSY (cleared by reading RPIR) * Two conditions have flag bits that are not latched: * - Bus phase mismatch (non-maskable in DMA Mode, cleared by ending DMA Mode) * - Bus reset (non-maskable) * The remaining condition has no flag bit at all: * - Selection/reselection * * Hence, establishing the cause(s) of any interrupt is partly guesswork. * In "The DP8490 and DP5380 Comparison Guide", National Semiconductor * claimed that "the design of the [DP8490] interrupt logic ensures * interrupts will not be lost (they can be on the DP5380)." * The L5380/53C80 datasheet from LOGIC Devices has more details. * * Checking for bus reset by reading RST is futile because of interrupt * latency, but a bus reset will reset chip logic. Checking for parity error * is unnecessary because that interrupt is never enabled. A Loss of BSY * condition will clear DMA Mode. We can tell when this occurs because the * the Busy Monitor interrupt is enabled together with DMA Mode. */
static irqreturn_t __maybe_unused NCR5380_intr(int irq, void *dev_id) { struct Scsi_Host *instance = dev_id; struct NCR5380_hostdata *hostdata = shost_priv(instance); int handled = 0; unsigned char basr; unsigned long flags; spin_lock_irqsave(&hostdata->lock, flags); basr = NCR5380_read(BUS_AND_STATUS_REG); if (basr & BASR_IRQ) { unsigned char mr = NCR5380_read(MODE_REG); unsigned char sr = NCR5380_read(STATUS_REG); dsprintk(NDEBUG_INTR, instance, "IRQ %d, BASR 0x%02x, SR 0x%02x, MR 0x%02x\n", irq, basr, sr, mr); if ((mr & MR_DMA_MODE) ||