📄 pci2000.c
字号:
/**************************************************************************** * Perceptive Solutions, Inc. PCI-2000 device driver for Linux. * * pci2000.c - Linux Host Driver for PCI-2000 IntelliCache SCSI Adapters * * Copyright (c) 1997-1999 Perceptive Solutions, Inc. * All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that redistributions of source * code retain the above copyright notice and this comment without * modification. * * Technical updates and product information at: * http://www.psidisk.com * * Please send questions, comments, bug reports to: * tech@psidisk.com Technical Support * * * Revisions 1.10 Jan-21-1999 * - Fixed sign on message to reflect proper controller name. * - Added support for RAID status monitoring and control. * * Revisions 1.11 Mar-22-1999 * - Fixed control timeout to not lock up the entire system if * controller goes offline completely. * * Revisions 1.12 Mar-26-1999 * - Fixed spinlock and PCI configuration. * * Revisions 1.20 Mar-27-2000 * - Added support for dynamic DMA * ****************************************************************************/#define PCI2000_VERSION "1.20"#include <linux/module.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/string.h>#include <linux/pci.h>#include <linux/ioport.h>#include <linux/delay.h>#include <linux/sched.h>#include <linux/proc_fs.h>#include <asm/dma.h>#include <asm/system.h>#include <asm/io.h>#include <linux/blk.h>#include "scsi.h"#include "hosts.h"#include <linux/stat.h>#include <linux/spinlock.h>#include "pci2000.h"#include "psi_roy.h"//#define DEBUG 1#ifdef DEBUG#define DEB(x) x#define STOP_HERE {int st;for(st=0;st<100;st++){st=1;}}#else#define DEB(x)#define STOP_HERE#endiftypedef struct { unsigned int address; unsigned int length; } SCATGATH, *PSCATGATH;typedef struct { Scsi_Cmnd *SCpnt; PSCATGATH scatGath; dma_addr_t scatGathDma; UCHAR *cdb; dma_addr_t cdbDma; UCHAR tag; } DEV2000, *PDEV2000;typedef struct { ULONG basePort; ULONG mb0; ULONG mb1; ULONG mb2; ULONG mb3; ULONG mb4; ULONG cmd; ULONG tag; ULONG irqOwned; struct pci_dev *pdev; DEV2000 dev[MAX_BUS][MAX_UNITS]; } ADAPTER2000, *PADAPTER2000;#define HOSTDATA(host) ((PADAPTER2000)&host->hostdata)#define consistentLen (MAX_BUS * MAX_UNITS * (16 * sizeof (SCATGATH) + MAX_COMMAND_SIZE))static struct Scsi_Host *PsiHost[MAXADAPTER] = {NULL,}; // One for each adapterstatic int NumAdapters = 0;/**************************************************************** * Name: WaitReady :LOCAL * * Description: Wait for controller ready. * * Parameters: padapter - Pointer adapter data structure. * * Returns: TRUE on not ready. * ****************************************************************/static int WaitReady (PADAPTER2000 padapter) { ULONG z; for ( z = 0; z < (TIMEOUT_COMMAND * 4); z++ ) { if ( !inb_p (padapter->cmd) ) return FALSE; udelay (250); }; return TRUE; }/**************************************************************** * Name: WaitReadyLong :LOCAL * * Description: Wait for controller ready. * * Parameters: padapter - Pointer adapter data structure. * * Returns: TRUE on not ready. * ****************************************************************/static int WaitReadyLong (PADAPTER2000 padapter) { ULONG z; for ( z = 0; z < (5000 * 4); z++ ) { if ( !inb_p (padapter->cmd) ) return FALSE; udelay (250); }; return TRUE; }/**************************************************************** * Name: OpDone :LOCAL * * Description: Clean up operation and issue done to caller. * * Parameters: SCpnt - Pointer to SCSI command structure. * status - Caller status. * * Returns: Nothing. * ****************************************************************/static void OpDone (Scsi_Cmnd *SCpnt, ULONG status) { SCpnt->result = status; SCpnt->scsi_done (SCpnt); }/**************************************************************** * Name: Command :LOCAL * * Description: Issue queued command to the PCI-2000. * * Parameters: padapter - Pointer to adapter information structure. * cmd - PCI-2000 command byte. * * Returns: Non-zero command tag if operation is accepted. * ****************************************************************/static UCHAR Command (PADAPTER2000 padapter, UCHAR cmd) { outb_p (cmd, padapter->cmd); if ( WaitReady (padapter) ) return 0; if ( inw_p (padapter->mb0) ) return 0; return inb_p (padapter->mb1); }/**************************************************************** * Name: BuildSgList :LOCAL * * Description: Build the scatter gather list for controller. * * Parameters: SCpnt - Pointer to SCSI command structure. * padapter - Pointer to adapter information structure. * pdev - Pointer to adapter device structure. * * Returns: Non-zero in not scatter gather. * ****************************************************************/static int BuildSgList (Scsi_Cmnd *SCpnt, PADAPTER2000 padapter, PDEV2000 pdev) { int z; int zc; struct scatterlist *sg; if ( SCpnt->use_sg ) { sg = (struct scatterlist *)SCpnt->request_buffer; zc = pci_map_sg (padapter->pdev, sg, SCpnt->use_sg, scsi_to_pci_dma_dir (SCpnt->sc_data_direction)); for ( z = 0; z < zc; z++ ) { pdev->scatGath[z].address = cpu_to_le32 (sg_dma_address (sg)); pdev->scatGath[z].length = cpu_to_le32 (sg_dma_len (sg++)); } outl (pdev->scatGathDma, padapter->mb2); outl ((zc << 24) | SCpnt->request_bufflen, padapter->mb3); return FALSE; } if ( !SCpnt->request_bufflen) { outl (0, padapter->mb2); outl (0, padapter->mb3); return TRUE; } SCpnt->SCp.have_data_in = pci_map_single (padapter->pdev, SCpnt->request_buffer, SCpnt->request_bufflen, scsi_to_pci_dma_dir (SCpnt->sc_data_direction)); outl (SCpnt->SCp.have_data_in, padapter->mb2); outl (SCpnt->request_bufflen, padapter->mb3); return TRUE; }/********************************************************************* * Name: PsiRaidCmd * * Description: Execute a simple command. * * Parameters: padapter - Pointer to adapter control structure. * cmd - Roy command byte. * * Returns: Return error status. * ********************************************************************/static int PsiRaidCmd (PADAPTER2000 padapter, char cmd) { if ( WaitReady (padapter) ) // test for command register ready return DID_TIME_OUT; outb_p (cmd, padapter->cmd); // issue command if ( WaitReadyLong (padapter) ) // wait for adapter ready return DID_TIME_OUT; return DID_OK; }/**************************************************************** * Name: Irq_Handler :LOCAL * * Description: Interrupt handler. * * Parameters: irq - Hardware IRQ number. * dev_id - * regs - * * Returns: TRUE if drive is not ready in time. * ****************************************************************/static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs) { struct Scsi_Host *shost = NULL; // Pointer to host data block PADAPTER2000 padapter; // Pointer to adapter control structure PDEV2000 pdev; Scsi_Cmnd *SCpnt; UCHAR tag = 0; UCHAR tag0; ULONG error; int pun; int bus; int z; unsigned long flags; /* * Disable interrupts, if they aren't already disabled and acquire * the I/O spinlock. */ spin_lock_irqsave (&io_request_lock, flags); DEB(printk ("\npci2000 received interrupt ")); for ( z = 0; z < NumAdapters; z++ ) // scan for interrupt to process { if ( PsiHost[z]->irq == (UCHAR)(irq & 0xFF) ) { tag = inb_p (HOSTDATA(PsiHost[z])->tag); if ( tag ) { shost = PsiHost[z]; break; } } } if ( !shost ) { DEB (printk ("\npci2000: not my interrupt")); goto irq_return; } padapter = HOSTDATA(shost); tag0 = tag & 0x7F; // mask off the error bit for ( bus = 0; bus < MAX_BUS; bus++ ) // scan the busses { for ( pun = 0; pun < MAX_UNITS; pun++ ) // scan the targets { pdev = &padapter->dev[bus][pun]; if ( !pdev->tag ) continue; if ( pdev->tag == tag0 ) // is this it? { pdev->tag = 0; SCpnt = pdev->SCpnt; goto unmapProceed; } } } outb_p (0xFF, padapter->tag); // clear the op interrupt outb_p (CMD_DONE, padapter->cmd); // complete the op goto irq_return;; // done, but, with what?unmapProceed:; if ( !bus ) { switch ( SCpnt->cmnd[0] ) { case SCSIOP_TEST_UNIT_READY: pci_unmap_single (padapter->pdev, SCpnt->SCp.have_data_in, sizeof (SCpnt->sense_buffer), PCI_DMA_FROMDEVICE); goto irqProceed; case SCSIOP_READ_CAPACITY: pci_unmap_single (padapter->pdev, SCpnt->SCp.have_data_in, 8, PCI_DMA_FROMDEVICE); goto irqProceed; case SCSIOP_VERIFY: case SCSIOP_START_STOP_UNIT: case SCSIOP_MEDIUM_REMOVAL: goto irqProceed; } } if ( SCpnt->SCp.have_data_in ) pci_unmap_single (padapter->pdev, SCpnt->SCp.have_data_in, SCpnt->request_bufflen, scsi_to_pci_dma_dir(SCpnt->sc_data_direction)); else { if ( SCpnt->use_sg ) pci_unmap_sg (padapter->pdev, (struct scatterlist *)SCpnt->request_buffer, SCpnt->use_sg, scsi_to_pci_dma_dir(SCpnt->sc_data_direction)); }irqProceed:; if ( tag & ERR08_TAGGED ) // is there an error here? { if ( WaitReady (padapter) ) { OpDone (SCpnt, DID_TIME_OUT << 16); goto irq_return;; } outb_p (tag0, padapter->mb0); // get real error code outb_p (CMD_ERROR, padapter->cmd); if ( WaitReady (padapter) ) // wait for controller to suck up the op { OpDone (SCpnt, DID_TIME_OUT << 16); goto irq_return;; } error = inl (padapter->mb0); // get error data outb_p (0xFF, padapter->tag); // clear the op interrupt outb_p (CMD_DONE, padapter->cmd); // complete the op DEB (printk ("status: %lX ", error)); if ( error == 0x00020002 ) // is this error a check condition? { if ( bus ) // are we doint SCSI commands? { OpDone (SCpnt, (DID_OK << 16) | 2); goto irq_return;; } if ( *SCpnt->cmnd == SCSIOP_TEST_UNIT_READY ) OpDone (SCpnt, (DRIVER_SENSE << 24) | (DID_OK << 16) | 2); // test caller we have sense data too else OpDone (SCpnt, DID_ERROR << 16); goto irq_return;; } OpDone (SCpnt, DID_ERROR << 16); goto irq_return;; } outb_p (0xFF, padapter->tag); // clear the op interrupt outb_p (CMD_DONE, padapter->cmd); // complete the op OpDone (SCpnt, DID_OK << 16);irq_return:; /* * Release the I/O spinlock and restore the original flags * which will enable interrupts if and only if they were * enabled on entry. */ spin_unlock_irqrestore (&io_request_lock, flags); }/**************************************************************** * Name: Pci2000_QueueCommand * * Description: Process a queued command from the SCSI manager. * * Parameters: SCpnt - Pointer to SCSI command structure. * done - Pointer to done function to call. * * Returns: Status code. * ****************************************************************/int Pci2000_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) { UCHAR *cdb = (UCHAR *)SCpnt->cmnd; // Pointer to SCSI CDB PADAPTER2000 padapter = HOSTDATA(SCpnt->host); // Pointer to adapter control structure int rc = -1; // command return code UCHAR bus = SCpnt->channel; UCHAR pun = SCpnt->target; UCHAR lun = SCpnt->lun; UCHAR cmd; PDEV2000 pdev = &padapter->dev[bus][pun]; if ( !done ) { printk("pci2000_queuecommand: %02X: done can't be NULL\n", *cdb); return 0; } SCpnt->scsi_done = done; SCpnt->SCp.have_data_in = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -