📄 53c700.c
字号:
/* -*- mode: c; c-basic-offset: 8 -*- *//* NCR (or Symbios) 53c700 and 53c700-66 Driver * * Copyright (C) 2001 by James.Bottomley@HansenPartnership.com**-----------------------------------------------------------------------------** ** 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 of the License, 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; if not, write to the Free Software** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.****----------------------------------------------------------------------------- *//* Notes: * * This driver is designed exclusively for these chips (virtually the * earliest of the scripts engine chips). They need their own drivers * because they are missing so many of the scripts and snazzy register * features of their elder brothers (the 710, 720 and 770). * * The 700 is the lowliest of the line, it can only do async SCSI. * The 700-66 can at least do synchronous SCSI up to 10MHz. * * The 700 chip has no host bus interface logic of its own. However, * it is usually mapped to a location with well defined register * offsets. Therefore, if you can determine the base address and the * irq your board incorporating this chip uses, you can probably use * this driver to run it (although you'll probably have to write a * minimal wrapper for the purpose---see the NCR_D700 driver for * details about how to do this). * * * TODO List: * * 1. Better statistics in the proc fs * * 2. Implement message queue (queues SCSI messages like commands) and make * the abort and device reset functions use them. * *//* CHANGELOG * * Version 2.7 * * Fixed scripts problem which caused certain devices (notably CDRWs) * to hang on initial INQUIRY. Updated NCR_700_readl/writel to use * __raw_readl/writel for parisc compatibility (Thomas * Bogendoerfer). Added missing SCp->request_bufflen initialisation * for sense requests (Ryan Bradetich). * * Version 2.6 * * Following test of the 64 bit parisc kernel by Richard Hirst, * several problems have now been corrected. Also adds support for * consistent memory allocation. * * Version 2.5 * * More Compatibility changes for 710 (now actually works). Enhanced * support for odd clock speeds which constrain SDTR negotiations. * correct cacheline separation for scsi messages and status for * incoherent architectures. Use of the pci mapping functions on * buffers to begin support for 64 bit drivers. * * Version 2.4 * * Added support for the 53c710 chip (in 53c700 emulation mode only---no * special 53c710 instructions or registers are used). * * Version 2.3 * * More endianness/cache coherency changes. * * Better bad device handling (handles devices lying about tag * queueing support and devices which fail to provide sense data on * contingent allegiance conditions) * * Many thanks to Richard Hirst <rhirst@linuxcare.com> for patiently * debugging this driver on the parisc architecture and suggesting * many improvements and bug fixes. * * Thanks also go to Linuxcare Inc. for providing several PARISC * machines for me to debug the driver on. * * Version 2.2 * * Made the driver mem or io mapped; added endian invariance; added * dma cache flushing operations for architectures which need it; * added support for more varied clocking speeds. * * Version 2.1 * * Initial modularisation from the D700. See NCR_D700.c for the rest of * the changelog. * */#define NCR_700_VERSION "2.7"#include <linux/config.h>#include <linux/version.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/string.h>#include <linux/ioport.h>#include <linux/delay.h>#include <linux/spinlock.h>#include <linux/sched.h>#include <linux/proc_fs.h>#include <linux/init.h>#include <linux/mca.h>#include <asm/dma.h>#include <asm/system.h>#include <asm/io.h>#include <asm/pgtable.h>#include <asm/byteorder.h>#include <linux/blk.h>#include <linux/module.h>#include <linux/pci.h>#include "scsi.h"#include "hosts.h"#include "constants.h"#include "53c700.h"/* NOTE: For 64 bit drivers there are points in the code where we use * a non dereferenceable pointer to point to a structure in dma-able * memory (which is 32 bits) so that we can use all of the structure * operations but take the address at the end. This macro allows us * to truncate the 64 bit pointer down to 32 bits without the compiler * complaining */#define to32bit(x) ((__u32)((unsigned long)(x)))#ifdef NCR_700_DEBUG#define STATIC#else#define STATIC static#endifMODULE_AUTHOR("James Bottomley");MODULE_DESCRIPTION("53c700 and 53c700-66 Driver");MODULE_LICENSE("GPL");/* This is the script */#include "53c700_d.h"STATIC int NCR_700_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));STATIC int NCR_700_abort(Scsi_Cmnd * SCpnt);STATIC int NCR_700_bus_reset(Scsi_Cmnd * SCpnt);STATIC int NCR_700_dev_reset(Scsi_Cmnd * SCpnt);STATIC int NCR_700_host_reset(Scsi_Cmnd * SCpnt);STATIC int NCR_700_proc_directory_info(char *, char **, off_t, int, int, int);STATIC void NCR_700_chip_setup(struct Scsi_Host *host);STATIC void NCR_700_chip_reset(struct Scsi_Host *host);static char *NCR_700_phase[] = { "", "after selection", "before command phase", "after command phase", "after status phase", "after data in phase", "after data out phase", "during data phase",};static char *NCR_700_condition[] = { "", "NOT MSG_OUT", "UNEXPECTED PHASE", "NOT MSG_IN", "UNEXPECTED MSG", "MSG_IN", "SDTR_MSG RECEIVED", "REJECT_MSG RECEIVED", "DISCONNECT_MSG RECEIVED", "MSG_OUT", "DATA_IN", };static char *NCR_700_fatal_messages[] = { "unexpected message after reselection", "still MSG_OUT after message injection", "not MSG_IN after selection", "Illegal message length received",};static char *NCR_700_SBCL_bits[] = { "IO ", "CD ", "MSG ", "ATN ", "SEL ", "BSY ", "ACK ", "REQ ",};static char *NCR_700_SBCL_to_phase[] = { "DATA_OUT", "DATA_IN", "CMD_OUT", "STATE", "ILLEGAL PHASE", "ILLEGAL PHASE", "MSG OUT", "MSG IN",};static __u8 NCR_700_SDTR_msg[] = { 0x01, /* Extended message */ 0x03, /* Extended message Length */ 0x01, /* SDTR Extended message */ NCR_700_MIN_PERIOD, NCR_700_MAX_OFFSET};struct Scsi_Host * __initNCR_700_detect(Scsi_Host_Template *tpnt, struct NCR_700_Host_Parameters *hostdata){ dma_addr_t pScript, pMemory, pSlots; __u8 *memory; __u32 *script; struct Scsi_Host *host; static int banner = 0; int j;#ifdef CONFIG_53C700_USE_CONSISTENT memory = pci_alloc_consistent(hostdata->pci_dev, TOTAL_MEM_SIZE, &pMemory); hostdata->consistent = 1; if(memory == NULL ) { printk(KERN_WARNING "53c700: consistent memory allocation failed\n");#endif memory = kmalloc(TOTAL_MEM_SIZE, GFP_KERNEL); if(memory == NULL) { printk(KERN_ERR "53c700: Failed to allocate memory for driver, detatching\n"); return NULL; } pMemory = pci_map_single(hostdata->pci_dev, memory, TOTAL_MEM_SIZE, PCI_DMA_BIDIRECTIONAL);#ifdef CONFIG_53C700_USE_CONSISTENT hostdata->consistent = 0; }#endif script = (__u32 *)memory; pScript = pMemory; hostdata->msgin = memory + MSGIN_OFFSET; hostdata->msgout = memory + MSGOUT_OFFSET; hostdata->status = memory + STATUS_OFFSET; hostdata->slots = (struct NCR_700_command_slot *)(memory + SLOTS_OFFSET); pSlots = pMemory + SLOTS_OFFSET; /* Fill in the missing routines from the host template */ tpnt->queuecommand = NCR_700_queuecommand; tpnt->eh_abort_handler = NCR_700_abort; tpnt->eh_device_reset_handler = NCR_700_dev_reset; tpnt->eh_bus_reset_handler = NCR_700_bus_reset; tpnt->eh_host_reset_handler = NCR_700_host_reset; tpnt->can_queue = NCR_700_COMMAND_SLOTS_PER_HOST; tpnt->sg_tablesize = NCR_700_SG_SEGMENTS; tpnt->cmd_per_lun = NCR_700_MAX_TAGS; tpnt->use_clustering = DISABLE_CLUSTERING; tpnt->use_new_eh_code = 1; tpnt->proc_info = NCR_700_proc_directory_info; if(tpnt->name == NULL) tpnt->name = "53c700"; if(tpnt->proc_name == NULL) tpnt->proc_name = "53c700"; if((host = scsi_register(tpnt, 4)) == NULL) return NULL; memset(hostdata->slots, 0, sizeof(struct NCR_700_command_slot) * NCR_700_COMMAND_SLOTS_PER_HOST); for(j = 0; j < NCR_700_COMMAND_SLOTS_PER_HOST; j++) { dma_addr_t offset = (dma_addr_t)((unsigned long)&hostdata->slots[j].SG[0] - (unsigned long)&hostdata->slots[0].SG[0]); hostdata->slots[j].pSG = (struct NCR_700_SG_List *)((unsigned long)(pSlots + offset)); if(j == 0) hostdata->free_list = &hostdata->slots[j]; else hostdata->slots[j-1].ITL_forw = &hostdata->slots[j]; hostdata->slots[j].state = NCR_700_SLOT_FREE; } for(j = 0; j < sizeof(SCRIPT)/sizeof(SCRIPT[0]); j++) { script[j] = bS_to_host(SCRIPT[j]); } /* adjust all labels to be bus physical */ for(j = 0; j < PATCHES; j++) { script[LABELPATCHES[j]] = bS_to_host(pScript + SCRIPT[LABELPATCHES[j]]); } /* now patch up fixed addresses. */ script_patch_32(script, MessageLocation, pScript + MSGOUT_OFFSET); script_patch_32(script, StatusAddress, pScript + STATUS_OFFSET); script_patch_32(script, ReceiveMsgAddress, pScript + MSGIN_OFFSET); hostdata->script = script; hostdata->pScript = pScript; NCR_700_dma_cache_wback((unsigned long)script, sizeof(SCRIPT)); hostdata->state = NCR_700_HOST_FREE; spin_lock_init(&hostdata->lock); hostdata->cmd = NULL; host->max_id = 7; host->max_lun = NCR_700_MAX_LUNS; host->unique_id = hostdata->base; host->base = hostdata->base; host->hostdata[0] = (unsigned long)hostdata; /* kick the chip */ NCR_700_writeb(0xff, host, CTEST9_REG); if(hostdata->chip710) hostdata->rev = (NCR_700_readb(host, CTEST8_REG)>>4) & 0x0f; else hostdata->rev = (NCR_700_readb(host, CTEST7_REG)>>4) & 0x0f; hostdata->fast = (NCR_700_readb(host, CTEST9_REG) == 0); if(banner == 0) { printk(KERN_NOTICE "53c700: Version " NCR_700_VERSION " By James.Bottomley@HansenPartnership.com\n"); banner = 1; } printk(KERN_NOTICE "scsi%d: %s rev %d %s\n", host->host_no, hostdata->chip710 ? "53c710" : (hostdata->fast ? "53c700-66" : "53c700"), hostdata->rev, hostdata->differential ? "(Differential)" : ""); /* reset the chip */ NCR_700_chip_reset(host); return host;}intNCR_700_release(struct Scsi_Host *host){ struct NCR_700_Host_Parameters *hostdata = (struct NCR_700_Host_Parameters *)host->hostdata[0];#ifdef CONFIG_53C700_USE_CONSISTENT if(hostdata->consistent) { pci_free_consistent(hostdata->pci_dev, TOTAL_MEM_SIZE, hostdata->script, hostdata->pScript); } else {#endif pci_unmap_single(hostdata->pci_dev, hostdata->pScript, TOTAL_MEM_SIZE, PCI_DMA_BIDIRECTIONAL); kfree(hostdata->script);#ifdef CONFIG_53C700_USE_CONSISTENT }#endif return 1;}static inline __u8NCR_700_identify(int can_disconnect, __u8 lun){ return IDENTIFY_BASE | ((can_disconnect) ? 0x40 : 0) | (lun & NCR_700_LUN_MASK);}/* * Function : static int data_residual (Scsi_Host *host) * * Purpose : return residual data count of what's in the chip. If you * really want to know what this function is doing, it's almost a * direct transcription of the algorithm described in the 53c710 * guide, except that the DBC and DFIFO registers are only 6 bits * wide on a 53c700. * * Inputs : host - SCSI host */static inline intNCR_700_data_residual (struct Scsi_Host *host) { struct NCR_700_Host_Parameters *hostdata = (struct NCR_700_Host_Parameters *)host->hostdata[0]; int count, synchronous = 0; unsigned int ddir; if(hostdata->chip710) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -