/* * Initio A100 device driver for Linux. * * Copyright (c) 1994-1998 Initio Corporation * Copyright (c) 2003-2004 Christoph Hellwig * All rights reserved. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE.
*/
/* * Revision History: * 07/02/98 hl - v.91n Initial drivers. * 09/14/98 hl - v1.01 Support new Kernel. * 09/22/98 hl - v1.01a Support reset. * 09/24/98 hl - v1.01b Fixed reset. * 10/05/98 hl - v1.02 split the source code and release. * 12/19/98 bv - v1.02a Use spinlocks for 2.1.95 and up * 01/31/99 bv - v1.02b Use mdelay instead of waitForPause * 08/08/99 bv - v1.02c Use waitForPause again. * 06/25/02 Doug Ledford <dledford@redhat.com> - v1.02d * - Remove limit on number of controllers * - Port to DMA mapping API * - Clean up interrupt handler registration * - Fix memory leaks * - Fix allocation of scsi host structs and private data * 11/18/03 Christoph Hellwig <hch@lst.de> * - Port to new probing API * - Fix some more leaks in init failure cases * 9/28/04 Christoph Hellwig <hch@lst.de> * - merge the two source files * - remove internal queueing code * 14/06/07 Alan Cox <alan@lxorguk.ukuu.org.uk> * - Grand cleanup and Linuxisation
*/
static u8 default_nvram[64] =
{ /*----------header -------------*/
0x01, /* 0x00: Sub System Vendor ID 0 */
0x11, /* 0x01: Sub System Vendor ID 1 */
0x60, /* 0x02: Sub System ID 0 */
0x10, /* 0x03: Sub System ID 1 */
0x00, /* 0x04: SubClass */
0x01, /* 0x05: Vendor ID 0 */
0x11, /* 0x06: Vendor ID 1 */
0x60, /* 0x07: Device ID 0 */
0x10, /* 0x08: Device ID 1 */
0x00, /* 0x09: Reserved */
0x00, /* 0x0A: Reserved */
0x01, /* 0x0B: Revision of Data Structure */ /* -- Host Adapter Structure --- */
0x01, /* 0x0C: Number Of SCSI Channel */
0x01, /* 0x0D: BIOS Configuration 1 */
0x00, /* 0x0E: BIOS Configuration 2 */
0x00, /* 0x0F: BIOS Configuration 3 */ /* --- SCSI Channel 0 Configuration --- */
0x07, /* 0x10: H/A ID */
0x83, /* 0x11: Channel Configuration */
0x20, /* 0x12: MAX TAG per target */
0x0A, /* 0x13: SCSI Reset Recovering time */
0x00, /* 0x14: Channel Configuration4 */
0x00, /* 0x15: Channel Configuration5 */ /* SCSI Channel 0 Target Configuration */ /* 0x16-0x25 */
0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8,
0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, /* --- SCSI Channel 1 Configuration --- */
0x07, /* 0x26: H/A ID */
0x83, /* 0x27: Channel Configuration */
0x20, /* 0x28: MAX TAG per target */
0x0A, /* 0x29: SCSI Reset Recovering time */
0x00, /* 0x2A: Channel Configuration4 */
0x00, /* 0x2B: Channel Configuration5 */ /* SCSI Channel 1 Target Configuration */ /* 0x2C-0x3B */
0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8,
0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8,
0x00, /* 0x3C: Reserved */
0x00, /* 0x3D: Reserved */
0x00, /* 0x3E: Reserved */
0x00 /* 0x3F: Checksum */
};
static u8 wait_chip_ready(struct orc_host * host)
{ int i;
for (i = 0; i < 10; i++) { /* Wait 1 second for report timeout */ if (inb(host->base + ORC_HCTRL) & HOSTSTOP) /* Wait HOSTSTOP set */ return 1;
msleep(100);
} return 0;
}
static u8 wait_firmware_ready(struct orc_host * host)
{ int i;
for (i = 0; i < 10; i++) { /* Wait 1 second for report timeout */ if (inb(host->base + ORC_HSTUS) & RREADY) /* Wait READY set */ return 1;
msleep(100); /* wait 100ms before try again */
} return 0;
}
/***************************************************************************/ static u8 wait_scsi_reset_done(struct orc_host * host)
{ int i;
for (i = 0; i < 10; i++) { /* Wait 1 second for report timeout */ if (!(inb(host->base + ORC_HCTRL) & SCSIRST)) /* Wait SCSIRST done */ return 1;
mdelay(100); /* wait 100ms before try again */
} return 0;
}
/***************************************************************************/ static u8 wait_HDO_off(struct orc_host * host)
{ int i;
for (i = 0; i < 10; i++) { /* Wait 1 second for report timeout */ if (!(inb(host->base + ORC_HCTRL) & HDO)) /* Wait HDO off */ return 1;
mdelay(100); /* wait 100ms before try again */
} return 0;
}
for (i = 0; i < 10; i++) { /* Wait 1 second for report timeout */ if ((*data = inb(host->base + ORC_HSTUS)) & HDI) return 1; /* Wait HDI set */
mdelay(100); /* wait 100ms before try again */
} return 0;
}
/* Calculate checksum first */
np = (u8 *) default_nvram; for (i = 0; i < 63; i++)
chksum += *np++;
*np = chksum;
np = (u8 *) default_nvram;
np1 = (u8 *) nvramp; for (i = 0; i < 64; i++, np++, np1++) { if (*np != *np1)
orc_nv_write(host, (u8) i, *np);
}
}
/** * read_eeprom - load EEPROM * @host: Host EEPROM to read * * Read the EEPROM for a given host. If it is invalid or fails * the restore the defaults and use them.
*/
/** * orc_load_firmware - initialise firmware * @host: Host to set up * * Load the firmware from the EEPROM into controller SRAM. This * is basically a 4K block copy and then a 4K block read to check * correctness. The rest is convulted by the indirect interfaces * in the hardware
*/
/** * init_alloc_map - initialise allocation map * @host: host map to configure * * Initialise the allocation maps for this device. If the device * is not quiescent the caller must hold the allocation lock
*/
staticvoid init_alloc_map(struct orc_host * host)
{
u8 i, j;
for (i = 0; i < MAX_CHANNELS; i++) { for (j = 0; j < 8; j++) {
host->allocation_map[i][j] = 0xffffffff;
}
}
}
/** * init_orchid - initialise the host adapter * @host:host adapter to initialise * * Initialise the controller and if necessary load the firmware. * * Returns -1 if the initialisation fails.
*/
if (nvramp->SCSI0Config & NCC_BUSRESET)
host->flags |= HCF_SCSI_RESET;
outb(0xFB, host->base + ORC_GIMSK); /* enable RP FIFO interrupt */ return 0;
}
/** * orc_reset_scsi_bus - perform bus reset * @host: host being reset * * Perform a full bus reset on the adapter.
*/
staticint orc_reset_scsi_bus(struct orc_host * host)
{ /* I need Host Control Block Information */ unsignedlong flags;
spin_lock_irqsave(&host->allocation_lock, flags);
init_alloc_map(host); /* reset scsi bus */
outb(SCSIRST, host->base + ORC_HCTRL); /* FIXME: We can spend up to a second with the lock held and
interrupts off here */ if (wait_scsi_reset_done(host) == 0) {
spin_unlock_irqrestore(&host->allocation_lock, flags); return FAILED;
} else {
spin_unlock_irqrestore(&host->allocation_lock, flags); return SUCCESS;
}
}
/** * orc_device_reset - device reset handler * @host: host to reset * @cmd: command causing the reset * @target: target device * * Reset registers, reset a hanging bus and kill active and disconnected * commands for target w/o soft reset
*/
staticint orc_device_reset(struct orc_host * host, struct scsi_cmnd *cmd, unsignedint target)
{ /* I need Host Control Block Information */ struct orc_scb *scb; struct orc_extended_scb *escb; struct orc_scb *host_scb;
u8 i; unsignedlong flags;
/* setup scatter list address with one buffer */
host_scb = host->scb_virt;
/* FIXME: is this safe if we then fail to issue the reset or race
a completion ? */
init_alloc_map(host);
/* Find the scb corresponding to the command */ for (i = 0; i < ORC_MAXQUEUE; i++) {
escb = host_scb->escb; if (host_scb->status && escb->srb == cmd) break;
host_scb++;
}
if (i == ORC_MAXQUEUE) {
printk(KERN_ERR "Unable to Reset - No SCB Found\n");
spin_unlock_irqrestore(&(host->allocation_lock), flags); return FAILED;
}
/* Allocate a new SCB for the reset command to the firmware */ if ((scb = __orc_alloc_scb(host)) == NULL) { /* Can't happen.. */
spin_unlock_irqrestore(&(host->allocation_lock), flags); return FAILED;
}
/* Reset device is handled by the firmware, we fill in an SCB and
fire it at the controller, it does the rest */
scb->opcode = ORC_BUSDEVRST;
scb->target = target;
scb->hastat = 0;
scb->tastat = 0;
scb->status = 0x0;
scb->link = 0xFF;
scb->reserved0 = 0;
scb->reserved1 = 0;
scb->xferlen = cpu_to_le32(0);
scb->sg_len = cpu_to_le32(0);
/** * __orc_alloc_scb - allocate an SCB * @host: host to allocate from * * Allocate an SCB and return a pointer to the SCB object. NULL * is returned if no SCB is free. The caller must already hold * the allocator lock at this point.
*/
channel = host->index; for (i = 0; i < 8; i++) { for (index = 0; index < 32; index++) { if ((host->allocation_map[channel][i] >> index) & 0x01) {
host->allocation_map[channel][i] &= ~(1 << index);
idx = index + 32 * i; /* * Translate the index to a structure instance
*/ return host->scb_virt + idx;
}
}
} return NULL;
}
/** * orc_alloc_scb - allocate an SCB * @host: host to allocate from * * Allocate an SCB and return a pointer to the SCB object. NULL * is returned if no SCB is free.
*/
/** * orc_release_scb - release an SCB * @host: host owning the SCB * @scb: SCB that is now free * * Called to return a completed SCB to the allocation pool. Before * calling the SCB must be out of use on both the host and the HA.
*/
spin_lock_irqsave(&(host->allocation_lock), flags);
channel = host->index; /* Channel */
index = scb->scbidx;
i = index / 32;
index %= 32;
host->allocation_map[channel][i] |= (1 << index);
spin_unlock_irqrestore(&(host->allocation_lock), flags);
}
/* * orchid_abort_scb - abort a command * * Abort a queued command that has been passed to the firmware layer * if possible. This is all handled by the firmware. We aks the firmware * and it either aborts the command or fails
*/
/* Walk the queue until we find the SCB that belongs to the command block. This isn't a performance critical path so a walk in the park
here does no harm */
for (i = 0; i < ORC_MAXQUEUE; i++, scb++) {
escb = scb->escb; if (scb->status && escb->srb == cmd) { if (scb->tag_msg == 0) { goto out;
} else { /* Issue an ABORT to the firmware */ if (orchid_abort_scb(host, scb)) {
escb->srb = NULL;
spin_unlock_irqrestore(&host->allocation_lock, flags); return SUCCESS;
} else goto out;
}
}
}
out:
spin_unlock_irqrestore(&host->allocation_lock, flags); return FAILED;
}
/** * orc_interrupt - IRQ processing * @host: Host causing the interrupt * * This function is called from the IRQ handler and protected * by the host lock. While the controller reports that there are * scb's for processing we pull them off the controller, turn the * index into a host address pointer to the scb and call the scb * handler. * * Returns IRQ_HANDLED if any SCBs were processed, IRQ_NONE otherwise
*/
/* Check if we have an SCB queued for servicing */ if (inb(host->base + ORC_RQUEUECNT) == 0) return IRQ_NONE;
do { /* Get the SCB index of the SCB to service */
scb_index = inb(host->base + ORC_RQUEUE);
/* Translate it back to a host pointer */
scb = (struct orc_scb *) ((unsignedlong) host->scb_virt + (unsignedlong) (sizeof(struct orc_scb) * scb_index));
scb->status = 0x0; /* Process the SCB */
inia100_scb_handler(host, scb);
} while (inb(host->base + ORC_RQUEUECNT)); return IRQ_HANDLED;
} /* End of I1060Interrupt() */
/** * inia100_build_scb - build SCB * @host: host owing the control block * @scb: control block to use * @cmd: Mid layer command * * Build a host adapter control block from the SCSI mid layer command
*/
staticint inia100_build_scb(struct orc_host * host, struct orc_scb * scb, struct scsi_cmnd * cmd)
{ /* Create corresponding SCB */ struct scatterlist *sg; struct orc_sgent *sgent; /* Pointer to SG list */ int i, count_sg; struct orc_extended_scb *escb;
/* Links between the escb, scb and Linux scsi midlayer cmd */
escb = scb->escb;
escb->srb = cmd;
sgent = NULL;
/* Set up the SCB to do a SCSI command block */
scb->opcode = ORC_EXECSCSI;
scb->flags = SCF_NO_DCHK; /* Clear done bit */
scb->target = cmd->device->id;
scb->lun = cmd->device->lun;
scb->reserved0 = 0;
scb->reserved1 = 0;
scb->sg_len = cpu_to_le32(0);
/* Build the scatter gather lists */ if (count_sg) {
scb->sg_len = cpu_to_le32((u32) (count_sg * 8));
scsi_for_each_sg(cmd, sg, count_sg, i) {
sgent->base = cpu_to_le32((u32) sg_dma_address(sg));
sgent->length = cpu_to_le32((u32) sg_dma_len(sg));
sgent++;
}
} else {
scb->sg_len = cpu_to_le32(0);
sgent->base = cpu_to_le32(0);
sgent->length = cpu_to_le32(0);
}
scb->sg_addr = (u32) scb->sense_addr; /* sense_addr is already little endian */
scb->hastat = 0;
scb->tastat = 0;
scb->link = 0xFF;
scb->sense_len = SENSE_SIZE;
scb->cdb_len = cmd->cmd_len; if (scb->cdb_len >= IMAX_CDB) {
printk("max cdb length= %x\n", cmd->cmd_len);
scb->cdb_len = IMAX_CDB;
}
scb->ident = (u8)(cmd->device->lun & 0xff) | DISC_ALLOW; if (cmd->device->tagged_supported) { /* Tag Support */
scb->tag_msg = SIMPLE_QUEUE_TAG; /* Do simple tag only */
} else {
scb->tag_msg = 0; /* No tag support */
}
memcpy(scb->cdb, cmd->cmnd, scb->cdb_len); return 0;
}
/** * inia100_queue_lck - queue command with host * @cmd: Command block * * Called by the mid layer to queue a command. Process the command * block, build the host specific scb structures and if there is room * queue the command down to the controller
*/ staticint inia100_queue_lck(struct scsi_cmnd *cmd)
{ struct orc_scb *scb; struct orc_host *host; /* Point to Host adapter control block */
host = (struct orc_host *) cmd->device->host->hostdata; /* Get free SCSI control block */ if ((scb = orc_alloc_scb(host)) == NULL) return SCSI_MLQUEUE_HOST_BUSY;
/***************************************************************************** Function name : inia100_abort Description : Abort a queued command. (commands that are on the bus can't be aborted easily) Input : host - Pointer to host adapter structure Output : None. Return : pSRB - Pointer to SCSI request block.
*****************************************************************************/ staticint inia100_abort(struct scsi_cmnd * cmd)
{ struct orc_host *host;
/***************************************************************************** Function name : inia100_reset Description : Reset registers, reset a hanging bus and kill active and disconnected commands for target w/o soft reset Input : host - Pointer to host adapter structure Output : None. Return : pSRB - Pointer to SCSI request block.
*****************************************************************************/ staticint inia100_bus_reset(struct scsi_cmnd * cmd)
{ /* I need Host Control Block Information */ struct orc_host *host;
host = (struct orc_host *) cmd->device->host->hostdata; return orc_reset_scsi_bus(host);
}
/***************************************************************************** Function name : inia100_device_reset Description : Reset the device Input : host - Pointer to host adapter structure Output : None. Return : pSRB - Pointer to SCSI request block.
*****************************************************************************/ staticint inia100_device_reset(struct scsi_cmnd * cmd)
{ /* I need Host Control Block Information */ struct orc_host *host;
host = (struct orc_host *) cmd->device->host->hostdata; return orc_device_reset(host, cmd, scmd_id(cmd));
}
/** * inia100_scb_handler - interrupt callback * @host: Host causing the interrupt * @scb: SCB the controller returned as needing processing * * Perform completion processing on a control block. Do the conversions * from host to SCSI midlayer error coding, save any sense data and * the complete with the midlayer and recycle the scb.
*/
escb = scb->escb; if ((cmd = (struct scsi_cmnd *) escb->srb) == NULL) {
printk(KERN_ERR "inia100_scb_handler: SRB pointer is empty\n");
orc_release_scb(host, scb); /* Release SCB for current channel */ return;
}
escb->srb = NULL;
switch (scb->hastat) { case 0x0: case 0xa: /* Linked command complete without error and linked normally */ case 0xb: /* Linked command complete without error interrupt generated */
scb->hastat = 0; break;
case 0x11: /* Selection time out-The initiator selection or target
reselection was not complete within the SCSI Time out period */
scb->hastat = DID_TIME_OUT; break;
case 0x14: /* Target bus phase sequence failure-An invalid bus phase or bus phase sequence was requested by the target. The host adapter will generate a SCSI Reset Condition, notifying the host with
a SCRD interrupt */
scb->hastat = DID_RESET; break;
case 0x12: /* Data overrun/underrun-The target attempted to transfer more data than was allocated by the Data Length field or the sum of the
Scatter / Gather Data Length fields. */ case 0x13: /* Unexpected bus free-The target dropped the SCSI BSY at an unexpected time. */ case 0x16: /* Invalid CCB Operation Code-The first byte of the CCB was invalid. */
if (pci_enable_device(pdev)) goto out; if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) {
printk(KERN_WARNING "Unable to set 32bit DMA " "on inia100 adapter, ignoring.\n"); goto out_disable_device;
}
pci_set_master(pdev);
port = pci_resource_start(pdev, 0); if (!request_region(port, 256, "inia100")) {
printk(KERN_WARNING "inia100: io port 0x%lx, is busy.\n", port); goto out_disable_device;
}
/* <02> read from base address + 0x50 offset to get the bios value. */
bios = inw(port + 0x50);
shost = scsi_host_alloc(&inia100_template, sizeof(struct orc_host)); if (!shost) goto out_release_region;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.