162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci/* 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci Linux Driver for BusLogic MultiMaster and FlashPoint SCSI Host Adapters 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci Copyright 1995-1998 by Leonard N. Zubkoff <lnz@dandelion.com> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci The author respectfully requests that any modifications to this software be 1162306a36Sopenharmony_ci sent directly to him for evaluation and testing. 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci Special thanks to Wayne Yen, Jin-Lon Hon, and Alex Win of BusLogic, whose 1462306a36Sopenharmony_ci advice has been invaluable, to David Gentzel, for writing the original Linux 1562306a36Sopenharmony_ci BusLogic driver, and to Paul Gortmaker, for being such a dedicated test site. 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci Finally, special thanks to Mylex/BusLogic for making the FlashPoint SCCB 1862306a36Sopenharmony_ci Manager available as freely redistributable source code. 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci*/ 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define blogic_drvr_version "2.1.17" 2362306a36Sopenharmony_ci#define blogic_drvr_date "12 September 2013" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <linux/module.h> 2662306a36Sopenharmony_ci#include <linux/init.h> 2762306a36Sopenharmony_ci#include <linux/interrupt.h> 2862306a36Sopenharmony_ci#include <linux/types.h> 2962306a36Sopenharmony_ci#include <linux/blkdev.h> 3062306a36Sopenharmony_ci#include <linux/delay.h> 3162306a36Sopenharmony_ci#include <linux/ioport.h> 3262306a36Sopenharmony_ci#include <linux/mm.h> 3362306a36Sopenharmony_ci#include <linux/stat.h> 3462306a36Sopenharmony_ci#include <linux/pci.h> 3562306a36Sopenharmony_ci#include <linux/spinlock.h> 3662306a36Sopenharmony_ci#include <linux/jiffies.h> 3762306a36Sopenharmony_ci#include <linux/dma-mapping.h> 3862306a36Sopenharmony_ci#include <linux/slab.h> 3962306a36Sopenharmony_ci#include <linux/msdos_partition.h> 4062306a36Sopenharmony_ci#include <scsi/scsicam.h> 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#include <asm/dma.h> 4362306a36Sopenharmony_ci#include <asm/io.h> 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#include <scsi/scsi.h> 4662306a36Sopenharmony_ci#include <scsi/scsi_cmnd.h> 4762306a36Sopenharmony_ci#include <scsi/scsi_device.h> 4862306a36Sopenharmony_ci#include <scsi/scsi_host.h> 4962306a36Sopenharmony_ci#include <scsi/scsi_tcq.h> 5062306a36Sopenharmony_ci#include "BusLogic.h" 5162306a36Sopenharmony_ci#include "FlashPoint.c" 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#ifndef FAILURE 5462306a36Sopenharmony_ci#define FAILURE (-1) 5562306a36Sopenharmony_ci#endif 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic const struct scsi_host_template blogic_template; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* 6062306a36Sopenharmony_ci blogic_drvr_options_count is a count of the number of BusLogic Driver 6162306a36Sopenharmony_ci Options specifications provided via the Linux Kernel Command Line or via 6262306a36Sopenharmony_ci the Loadable Kernel Module Installation Facility. 6362306a36Sopenharmony_ci*/ 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic int blogic_drvr_options_count; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/* 6962306a36Sopenharmony_ci blogic_drvr_options is an array of Driver Options structures representing 7062306a36Sopenharmony_ci BusLogic Driver Options specifications provided via the Linux Kernel Command 7162306a36Sopenharmony_ci Line or via the Loadable Kernel Module Installation Facility. 7262306a36Sopenharmony_ci*/ 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic struct blogic_drvr_options blogic_drvr_options[BLOGIC_MAX_ADAPTERS]; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci/* 7862306a36Sopenharmony_ci BusLogic can be assigned a string by insmod. 7962306a36Sopenharmony_ci*/ 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 8262306a36Sopenharmony_ci#ifdef MODULE 8362306a36Sopenharmony_cistatic char *BusLogic; 8462306a36Sopenharmony_cimodule_param(BusLogic, charp, 0); 8562306a36Sopenharmony_ci#endif 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/* 8962306a36Sopenharmony_ci blogic_probe_options is a set of Probe Options to be applied across 9062306a36Sopenharmony_ci all BusLogic Host Adapters. 9162306a36Sopenharmony_ci*/ 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic struct blogic_probe_options blogic_probe_options; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/* 9762306a36Sopenharmony_ci blogic_global_options is a set of Global Options to be applied across 9862306a36Sopenharmony_ci all BusLogic Host Adapters. 9962306a36Sopenharmony_ci*/ 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic struct blogic_global_options blogic_global_options; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic LIST_HEAD(blogic_host_list); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci/* 10662306a36Sopenharmony_ci blogic_probeinfo_count is the number of entries in blogic_probeinfo_list. 10762306a36Sopenharmony_ci*/ 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic int blogic_probeinfo_count; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci/* 11362306a36Sopenharmony_ci blogic_probeinfo_list is the list of I/O Addresses and Bus Probe Information 11462306a36Sopenharmony_ci to be checked for potential BusLogic Host Adapters. It is initialized by 11562306a36Sopenharmony_ci interrogating the PCI Configuration Space on PCI machines as well as from the 11662306a36Sopenharmony_ci list of standard BusLogic I/O Addresses. 11762306a36Sopenharmony_ci*/ 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic struct blogic_probeinfo *blogic_probeinfo_list; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci/* 12362306a36Sopenharmony_ci blogic_cmd_failure_reason holds a string identifying the reason why a 12462306a36Sopenharmony_ci call to blogic_cmd failed. It is only non-NULL when blogic_cmd 12562306a36Sopenharmony_ci returns a failure code. 12662306a36Sopenharmony_ci*/ 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic char *blogic_cmd_failure_reason; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci/* 13162306a36Sopenharmony_ci blogic_announce_drvr announces the Driver Version and Date, Author's 13262306a36Sopenharmony_ci Name, Copyright Notice, and Electronic Mail Address. 13362306a36Sopenharmony_ci*/ 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic void blogic_announce_drvr(struct blogic_adapter *adapter) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci blogic_announce("***** BusLogic SCSI Driver Version " blogic_drvr_version " of " blogic_drvr_date " *****\n", adapter); 13862306a36Sopenharmony_ci blogic_announce("Copyright 1995-1998 by Leonard N. Zubkoff <lnz@dandelion.com>\n", adapter); 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci/* 14362306a36Sopenharmony_ci blogic_drvr_info returns the Host Adapter Name to identify this SCSI 14462306a36Sopenharmony_ci Driver and Host Adapter. 14562306a36Sopenharmony_ci*/ 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic const char *blogic_drvr_info(struct Scsi_Host *host) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci struct blogic_adapter *adapter = 15062306a36Sopenharmony_ci (struct blogic_adapter *) host->hostdata; 15162306a36Sopenharmony_ci return adapter->full_model; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci/* 15562306a36Sopenharmony_ci blogic_init_ccbs initializes a group of Command Control Blocks (CCBs) 15662306a36Sopenharmony_ci for Host Adapter from the blk_size bytes located at blk_pointer. The newly 15762306a36Sopenharmony_ci created CCBs are added to Host Adapter's free list. 15862306a36Sopenharmony_ci*/ 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic void blogic_init_ccbs(struct blogic_adapter *adapter, void *blk_pointer, 16162306a36Sopenharmony_ci int blk_size, dma_addr_t blkp) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci struct blogic_ccb *ccb = (struct blogic_ccb *) blk_pointer; 16462306a36Sopenharmony_ci unsigned int offset = 0; 16562306a36Sopenharmony_ci memset(blk_pointer, 0, blk_size); 16662306a36Sopenharmony_ci ccb->allocgrp_head = blkp; 16762306a36Sopenharmony_ci ccb->allocgrp_size = blk_size; 16862306a36Sopenharmony_ci while ((blk_size -= sizeof(struct blogic_ccb)) >= 0) { 16962306a36Sopenharmony_ci ccb->status = BLOGIC_CCB_FREE; 17062306a36Sopenharmony_ci ccb->adapter = adapter; 17162306a36Sopenharmony_ci ccb->dma_handle = (u32) blkp + offset; 17262306a36Sopenharmony_ci if (blogic_flashpoint_type(adapter)) { 17362306a36Sopenharmony_ci ccb->callback = blogic_qcompleted_ccb; 17462306a36Sopenharmony_ci ccb->base_addr = adapter->fpinfo.base_addr; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci ccb->next = adapter->free_ccbs; 17762306a36Sopenharmony_ci ccb->next_all = adapter->all_ccbs; 17862306a36Sopenharmony_ci adapter->free_ccbs = ccb; 17962306a36Sopenharmony_ci adapter->all_ccbs = ccb; 18062306a36Sopenharmony_ci adapter->alloc_ccbs++; 18162306a36Sopenharmony_ci ccb++; 18262306a36Sopenharmony_ci offset += sizeof(struct blogic_ccb); 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci/* 18862306a36Sopenharmony_ci blogic_create_initccbs allocates the initial CCBs for Host Adapter. 18962306a36Sopenharmony_ci*/ 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic bool __init blogic_create_initccbs(struct blogic_adapter *adapter) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci int blk_size = BLOGIC_CCB_GRP_ALLOCSIZE * sizeof(struct blogic_ccb); 19462306a36Sopenharmony_ci void *blk_pointer; 19562306a36Sopenharmony_ci dma_addr_t blkp; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci while (adapter->alloc_ccbs < adapter->initccbs) { 19862306a36Sopenharmony_ci blk_pointer = dma_alloc_coherent(&adapter->pci_device->dev, 19962306a36Sopenharmony_ci blk_size, &blkp, GFP_KERNEL); 20062306a36Sopenharmony_ci if (blk_pointer == NULL) { 20162306a36Sopenharmony_ci blogic_err("UNABLE TO ALLOCATE CCB GROUP - DETACHING\n", 20262306a36Sopenharmony_ci adapter); 20362306a36Sopenharmony_ci return false; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci blogic_init_ccbs(adapter, blk_pointer, blk_size, blkp); 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci return true; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci/* 21262306a36Sopenharmony_ci blogic_destroy_ccbs deallocates the CCBs for Host Adapter. 21362306a36Sopenharmony_ci*/ 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic void blogic_destroy_ccbs(struct blogic_adapter *adapter) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci struct blogic_ccb *next_ccb = adapter->all_ccbs, *ccb, *lastccb = NULL; 21862306a36Sopenharmony_ci adapter->all_ccbs = NULL; 21962306a36Sopenharmony_ci adapter->free_ccbs = NULL; 22062306a36Sopenharmony_ci while ((ccb = next_ccb) != NULL) { 22162306a36Sopenharmony_ci next_ccb = ccb->next_all; 22262306a36Sopenharmony_ci if (ccb->allocgrp_head) { 22362306a36Sopenharmony_ci if (lastccb) 22462306a36Sopenharmony_ci dma_free_coherent(&adapter->pci_device->dev, 22562306a36Sopenharmony_ci lastccb->allocgrp_size, lastccb, 22662306a36Sopenharmony_ci lastccb->allocgrp_head); 22762306a36Sopenharmony_ci lastccb = ccb; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci if (lastccb) 23162306a36Sopenharmony_ci dma_free_coherent(&adapter->pci_device->dev, 23262306a36Sopenharmony_ci lastccb->allocgrp_size, lastccb, 23362306a36Sopenharmony_ci lastccb->allocgrp_head); 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci/* 23862306a36Sopenharmony_ci blogic_create_addlccbs allocates Additional CCBs for Host Adapter. If 23962306a36Sopenharmony_ci allocation fails and there are no remaining CCBs available, the Driver Queue 24062306a36Sopenharmony_ci Depth is decreased to a known safe value to avoid potential deadlocks when 24162306a36Sopenharmony_ci multiple host adapters share the same IRQ Channel. 24262306a36Sopenharmony_ci*/ 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic void blogic_create_addlccbs(struct blogic_adapter *adapter, 24562306a36Sopenharmony_ci int addl_ccbs, bool print_success) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci int blk_size = BLOGIC_CCB_GRP_ALLOCSIZE * sizeof(struct blogic_ccb); 24862306a36Sopenharmony_ci int prev_alloc = adapter->alloc_ccbs; 24962306a36Sopenharmony_ci void *blk_pointer; 25062306a36Sopenharmony_ci dma_addr_t blkp; 25162306a36Sopenharmony_ci if (addl_ccbs <= 0) 25262306a36Sopenharmony_ci return; 25362306a36Sopenharmony_ci while (adapter->alloc_ccbs - prev_alloc < addl_ccbs) { 25462306a36Sopenharmony_ci blk_pointer = dma_alloc_coherent(&adapter->pci_device->dev, 25562306a36Sopenharmony_ci blk_size, &blkp, GFP_KERNEL); 25662306a36Sopenharmony_ci if (blk_pointer == NULL) 25762306a36Sopenharmony_ci break; 25862306a36Sopenharmony_ci blogic_init_ccbs(adapter, blk_pointer, blk_size, blkp); 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci if (adapter->alloc_ccbs > prev_alloc) { 26162306a36Sopenharmony_ci if (print_success) 26262306a36Sopenharmony_ci blogic_notice("Allocated %d additional CCBs (total now %d)\n", adapter, adapter->alloc_ccbs - prev_alloc, adapter->alloc_ccbs); 26362306a36Sopenharmony_ci return; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci blogic_notice("Failed to allocate additional CCBs\n", adapter); 26662306a36Sopenharmony_ci if (adapter->drvr_qdepth > adapter->alloc_ccbs - adapter->tgt_count) { 26762306a36Sopenharmony_ci adapter->drvr_qdepth = adapter->alloc_ccbs - adapter->tgt_count; 26862306a36Sopenharmony_ci adapter->scsi_host->can_queue = adapter->drvr_qdepth; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci/* 27362306a36Sopenharmony_ci blogic_alloc_ccb allocates a CCB from Host Adapter's free list, 27462306a36Sopenharmony_ci allocating more memory from the Kernel if necessary. The Host Adapter's 27562306a36Sopenharmony_ci Lock should already have been acquired by the caller. 27662306a36Sopenharmony_ci*/ 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic struct blogic_ccb *blogic_alloc_ccb(struct blogic_adapter *adapter) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci static unsigned long serial; 28162306a36Sopenharmony_ci struct blogic_ccb *ccb; 28262306a36Sopenharmony_ci ccb = adapter->free_ccbs; 28362306a36Sopenharmony_ci if (ccb != NULL) { 28462306a36Sopenharmony_ci ccb->serial = ++serial; 28562306a36Sopenharmony_ci adapter->free_ccbs = ccb->next; 28662306a36Sopenharmony_ci ccb->next = NULL; 28762306a36Sopenharmony_ci if (adapter->free_ccbs == NULL) 28862306a36Sopenharmony_ci blogic_create_addlccbs(adapter, adapter->inc_ccbs, 28962306a36Sopenharmony_ci true); 29062306a36Sopenharmony_ci return ccb; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci blogic_create_addlccbs(adapter, adapter->inc_ccbs, true); 29362306a36Sopenharmony_ci ccb = adapter->free_ccbs; 29462306a36Sopenharmony_ci if (ccb == NULL) 29562306a36Sopenharmony_ci return NULL; 29662306a36Sopenharmony_ci ccb->serial = ++serial; 29762306a36Sopenharmony_ci adapter->free_ccbs = ccb->next; 29862306a36Sopenharmony_ci ccb->next = NULL; 29962306a36Sopenharmony_ci return ccb; 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci/* 30462306a36Sopenharmony_ci blogic_dealloc_ccb deallocates a CCB, returning it to the Host Adapter's 30562306a36Sopenharmony_ci free list. The Host Adapter's Lock should already have been acquired by the 30662306a36Sopenharmony_ci caller. 30762306a36Sopenharmony_ci*/ 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic void blogic_dealloc_ccb(struct blogic_ccb *ccb, int dma_unmap) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct blogic_adapter *adapter = ccb->adapter; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (ccb->command != NULL) 31462306a36Sopenharmony_ci scsi_dma_unmap(ccb->command); 31562306a36Sopenharmony_ci if (dma_unmap) 31662306a36Sopenharmony_ci dma_unmap_single(&adapter->pci_device->dev, ccb->sensedata, 31762306a36Sopenharmony_ci ccb->sense_datalen, DMA_FROM_DEVICE); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci ccb->command = NULL; 32062306a36Sopenharmony_ci ccb->status = BLOGIC_CCB_FREE; 32162306a36Sopenharmony_ci ccb->next = adapter->free_ccbs; 32262306a36Sopenharmony_ci adapter->free_ccbs = ccb; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci/* 32762306a36Sopenharmony_ci blogic_cmd sends the command opcode to adapter, optionally 32862306a36Sopenharmony_ci providing paramlen bytes of param and receiving at most 32962306a36Sopenharmony_ci replylen bytes of reply; any excess reply data is received but 33062306a36Sopenharmony_ci discarded. 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci On success, this function returns the number of reply bytes read from 33362306a36Sopenharmony_ci the Host Adapter (including any discarded data); on failure, it returns 33462306a36Sopenharmony_ci -1 if the command was invalid, or -2 if a timeout occurred. 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci blogic_cmd is called exclusively during host adapter detection and 33762306a36Sopenharmony_ci initialization, so performance and latency are not critical, and exclusive 33862306a36Sopenharmony_ci access to the Host Adapter hardware is assumed. Once the host adapter and 33962306a36Sopenharmony_ci driver are initialized, the only Host Adapter command that is issued is the 34062306a36Sopenharmony_ci single byte Execute Mailbox Command operation code, which does not require 34162306a36Sopenharmony_ci waiting for the Host Adapter Ready bit to be set in the Status Register. 34262306a36Sopenharmony_ci*/ 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic int blogic_cmd(struct blogic_adapter *adapter, enum blogic_opcode opcode, 34562306a36Sopenharmony_ci void *param, int paramlen, void *reply, int replylen) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci unsigned char *param_p = (unsigned char *) param; 34862306a36Sopenharmony_ci unsigned char *reply_p = (unsigned char *) reply; 34962306a36Sopenharmony_ci union blogic_stat_reg statusreg; 35062306a36Sopenharmony_ci union blogic_int_reg intreg; 35162306a36Sopenharmony_ci unsigned long processor_flag = 0; 35262306a36Sopenharmony_ci int reply_b = 0, result; 35362306a36Sopenharmony_ci long timeout; 35462306a36Sopenharmony_ci /* 35562306a36Sopenharmony_ci Clear out the Reply Data if provided. 35662306a36Sopenharmony_ci */ 35762306a36Sopenharmony_ci if (replylen > 0) 35862306a36Sopenharmony_ci memset(reply, 0, replylen); 35962306a36Sopenharmony_ci /* 36062306a36Sopenharmony_ci If the IRQ Channel has not yet been acquired, then interrupts 36162306a36Sopenharmony_ci must be disabled while issuing host adapter commands since a 36262306a36Sopenharmony_ci Command Complete interrupt could occur if the IRQ Channel was 36362306a36Sopenharmony_ci previously enabled by another BusLogic Host Adapter or another 36462306a36Sopenharmony_ci driver sharing the same IRQ Channel. 36562306a36Sopenharmony_ci */ 36662306a36Sopenharmony_ci if (!adapter->irq_acquired) 36762306a36Sopenharmony_ci local_irq_save(processor_flag); 36862306a36Sopenharmony_ci /* 36962306a36Sopenharmony_ci Wait for the Host Adapter Ready bit to be set and the 37062306a36Sopenharmony_ci Command/Parameter Register Busy bit to be reset in the Status 37162306a36Sopenharmony_ci Register. 37262306a36Sopenharmony_ci */ 37362306a36Sopenharmony_ci timeout = 10000; 37462306a36Sopenharmony_ci while (--timeout >= 0) { 37562306a36Sopenharmony_ci statusreg.all = blogic_rdstatus(adapter); 37662306a36Sopenharmony_ci if (statusreg.sr.adapter_ready && !statusreg.sr.cmd_param_busy) 37762306a36Sopenharmony_ci break; 37862306a36Sopenharmony_ci udelay(100); 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci if (timeout < 0) { 38162306a36Sopenharmony_ci blogic_cmd_failure_reason = 38262306a36Sopenharmony_ci "Timeout waiting for Host Adapter Ready"; 38362306a36Sopenharmony_ci result = -2; 38462306a36Sopenharmony_ci goto done; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci /* 38762306a36Sopenharmony_ci Write the opcode to the Command/Parameter Register. 38862306a36Sopenharmony_ci */ 38962306a36Sopenharmony_ci adapter->adapter_cmd_complete = false; 39062306a36Sopenharmony_ci blogic_setcmdparam(adapter, opcode); 39162306a36Sopenharmony_ci /* 39262306a36Sopenharmony_ci Write any additional Parameter Bytes. 39362306a36Sopenharmony_ci */ 39462306a36Sopenharmony_ci timeout = 10000; 39562306a36Sopenharmony_ci while (paramlen > 0 && --timeout >= 0) { 39662306a36Sopenharmony_ci /* 39762306a36Sopenharmony_ci Wait 100 microseconds to give the Host Adapter enough 39862306a36Sopenharmony_ci time to determine whether the last value written to the 39962306a36Sopenharmony_ci Command/Parameter Register was valid or not. If the 40062306a36Sopenharmony_ci Command Complete bit is set in the Interrupt Register, 40162306a36Sopenharmony_ci then the Command Invalid bit in the Status Register will 40262306a36Sopenharmony_ci be reset if the Operation Code or Parameter was valid 40362306a36Sopenharmony_ci and the command has completed, or set if the Operation 40462306a36Sopenharmony_ci Code or Parameter was invalid. If the Data In Register 40562306a36Sopenharmony_ci Ready bit is set in the Status Register, then the 40662306a36Sopenharmony_ci Operation Code was valid, and data is waiting to be read 40762306a36Sopenharmony_ci back from the Host Adapter. Otherwise, wait for the 40862306a36Sopenharmony_ci Command/Parameter Register Busy bit in the Status 40962306a36Sopenharmony_ci Register to be reset. 41062306a36Sopenharmony_ci */ 41162306a36Sopenharmony_ci udelay(100); 41262306a36Sopenharmony_ci intreg.all = blogic_rdint(adapter); 41362306a36Sopenharmony_ci statusreg.all = blogic_rdstatus(adapter); 41462306a36Sopenharmony_ci if (intreg.ir.cmd_complete) 41562306a36Sopenharmony_ci break; 41662306a36Sopenharmony_ci if (adapter->adapter_cmd_complete) 41762306a36Sopenharmony_ci break; 41862306a36Sopenharmony_ci if (statusreg.sr.datain_ready) 41962306a36Sopenharmony_ci break; 42062306a36Sopenharmony_ci if (statusreg.sr.cmd_param_busy) 42162306a36Sopenharmony_ci continue; 42262306a36Sopenharmony_ci blogic_setcmdparam(adapter, *param_p++); 42362306a36Sopenharmony_ci paramlen--; 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci if (timeout < 0) { 42662306a36Sopenharmony_ci blogic_cmd_failure_reason = 42762306a36Sopenharmony_ci "Timeout waiting for Parameter Acceptance"; 42862306a36Sopenharmony_ci result = -2; 42962306a36Sopenharmony_ci goto done; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci /* 43262306a36Sopenharmony_ci The Modify I/O Address command does not cause a Command Complete 43362306a36Sopenharmony_ci Interrupt. 43462306a36Sopenharmony_ci */ 43562306a36Sopenharmony_ci if (opcode == BLOGIC_MOD_IOADDR) { 43662306a36Sopenharmony_ci statusreg.all = blogic_rdstatus(adapter); 43762306a36Sopenharmony_ci if (statusreg.sr.cmd_invalid) { 43862306a36Sopenharmony_ci blogic_cmd_failure_reason = 43962306a36Sopenharmony_ci "Modify I/O Address Invalid"; 44062306a36Sopenharmony_ci result = -1; 44162306a36Sopenharmony_ci goto done; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci if (blogic_global_options.trace_config) 44462306a36Sopenharmony_ci blogic_notice("blogic_cmd(%02X) Status = %02X: (Modify I/O Address)\n", adapter, opcode, statusreg.all); 44562306a36Sopenharmony_ci result = 0; 44662306a36Sopenharmony_ci goto done; 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci /* 44962306a36Sopenharmony_ci Select an appropriate timeout value for awaiting command completion. 45062306a36Sopenharmony_ci */ 45162306a36Sopenharmony_ci switch (opcode) { 45262306a36Sopenharmony_ci case BLOGIC_INQ_DEV0TO7: 45362306a36Sopenharmony_ci case BLOGIC_INQ_DEV8TO15: 45462306a36Sopenharmony_ci case BLOGIC_INQ_DEV: 45562306a36Sopenharmony_ci /* Approximately 60 seconds. */ 45662306a36Sopenharmony_ci timeout = 60 * 10000; 45762306a36Sopenharmony_ci break; 45862306a36Sopenharmony_ci default: 45962306a36Sopenharmony_ci /* Approximately 1 second. */ 46062306a36Sopenharmony_ci timeout = 10000; 46162306a36Sopenharmony_ci break; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci /* 46462306a36Sopenharmony_ci Receive any Reply Bytes, waiting for either the Command 46562306a36Sopenharmony_ci Complete bit to be set in the Interrupt Register, or for the 46662306a36Sopenharmony_ci Interrupt Handler to set the Host Adapter Command Completed 46762306a36Sopenharmony_ci bit in the Host Adapter structure. 46862306a36Sopenharmony_ci */ 46962306a36Sopenharmony_ci while (--timeout >= 0) { 47062306a36Sopenharmony_ci intreg.all = blogic_rdint(adapter); 47162306a36Sopenharmony_ci statusreg.all = blogic_rdstatus(adapter); 47262306a36Sopenharmony_ci if (intreg.ir.cmd_complete) 47362306a36Sopenharmony_ci break; 47462306a36Sopenharmony_ci if (adapter->adapter_cmd_complete) 47562306a36Sopenharmony_ci break; 47662306a36Sopenharmony_ci if (statusreg.sr.datain_ready) { 47762306a36Sopenharmony_ci if (++reply_b <= replylen) 47862306a36Sopenharmony_ci *reply_p++ = blogic_rddatain(adapter); 47962306a36Sopenharmony_ci else 48062306a36Sopenharmony_ci blogic_rddatain(adapter); 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci if (opcode == BLOGIC_FETCH_LOCALRAM && 48362306a36Sopenharmony_ci statusreg.sr.adapter_ready) 48462306a36Sopenharmony_ci break; 48562306a36Sopenharmony_ci udelay(100); 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci if (timeout < 0) { 48862306a36Sopenharmony_ci blogic_cmd_failure_reason = 48962306a36Sopenharmony_ci "Timeout waiting for Command Complete"; 49062306a36Sopenharmony_ci result = -2; 49162306a36Sopenharmony_ci goto done; 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci /* 49462306a36Sopenharmony_ci Clear any pending Command Complete Interrupt. 49562306a36Sopenharmony_ci */ 49662306a36Sopenharmony_ci blogic_intreset(adapter); 49762306a36Sopenharmony_ci /* 49862306a36Sopenharmony_ci Provide tracing information if requested. 49962306a36Sopenharmony_ci */ 50062306a36Sopenharmony_ci if (blogic_global_options.trace_config) { 50162306a36Sopenharmony_ci int i; 50262306a36Sopenharmony_ci blogic_notice("blogic_cmd(%02X) Status = %02X: %2d ==> %2d:", 50362306a36Sopenharmony_ci adapter, opcode, statusreg.all, replylen, 50462306a36Sopenharmony_ci reply_b); 50562306a36Sopenharmony_ci if (replylen > reply_b) 50662306a36Sopenharmony_ci replylen = reply_b; 50762306a36Sopenharmony_ci for (i = 0; i < replylen; i++) 50862306a36Sopenharmony_ci blogic_notice(" %02X", adapter, 50962306a36Sopenharmony_ci ((unsigned char *) reply)[i]); 51062306a36Sopenharmony_ci blogic_notice("\n", adapter); 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci /* 51362306a36Sopenharmony_ci Process Command Invalid conditions. 51462306a36Sopenharmony_ci */ 51562306a36Sopenharmony_ci if (statusreg.sr.cmd_invalid) { 51662306a36Sopenharmony_ci /* 51762306a36Sopenharmony_ci Some early BusLogic Host Adapters may not recover 51862306a36Sopenharmony_ci properly from a Command Invalid condition, so if this 51962306a36Sopenharmony_ci appears to be the case, a Soft Reset is issued to the 52062306a36Sopenharmony_ci Host Adapter. Potentially invalid commands are never 52162306a36Sopenharmony_ci attempted after Mailbox Initialization is performed, 52262306a36Sopenharmony_ci so there should be no Host Adapter state lost by a 52362306a36Sopenharmony_ci Soft Reset in response to a Command Invalid condition. 52462306a36Sopenharmony_ci */ 52562306a36Sopenharmony_ci udelay(1000); 52662306a36Sopenharmony_ci statusreg.all = blogic_rdstatus(adapter); 52762306a36Sopenharmony_ci if (statusreg.sr.cmd_invalid || statusreg.sr.rsvd || 52862306a36Sopenharmony_ci statusreg.sr.datain_ready || 52962306a36Sopenharmony_ci statusreg.sr.cmd_param_busy || 53062306a36Sopenharmony_ci !statusreg.sr.adapter_ready || 53162306a36Sopenharmony_ci !statusreg.sr.init_reqd || 53262306a36Sopenharmony_ci statusreg.sr.diag_active || 53362306a36Sopenharmony_ci statusreg.sr.diag_failed) { 53462306a36Sopenharmony_ci blogic_softreset(adapter); 53562306a36Sopenharmony_ci udelay(1000); 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci blogic_cmd_failure_reason = "Command Invalid"; 53862306a36Sopenharmony_ci result = -1; 53962306a36Sopenharmony_ci goto done; 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci /* 54262306a36Sopenharmony_ci Handle Excess Parameters Supplied conditions. 54362306a36Sopenharmony_ci */ 54462306a36Sopenharmony_ci if (paramlen > 0) { 54562306a36Sopenharmony_ci blogic_cmd_failure_reason = "Excess Parameters Supplied"; 54662306a36Sopenharmony_ci result = -1; 54762306a36Sopenharmony_ci goto done; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci /* 55062306a36Sopenharmony_ci Indicate the command completed successfully. 55162306a36Sopenharmony_ci */ 55262306a36Sopenharmony_ci blogic_cmd_failure_reason = NULL; 55362306a36Sopenharmony_ci result = reply_b; 55462306a36Sopenharmony_ci /* 55562306a36Sopenharmony_ci Restore the interrupt status if necessary and return. 55662306a36Sopenharmony_ci */ 55762306a36Sopenharmony_cidone: 55862306a36Sopenharmony_ci if (!adapter->irq_acquired) 55962306a36Sopenharmony_ci local_irq_restore(processor_flag); 56062306a36Sopenharmony_ci return result; 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci/* 56562306a36Sopenharmony_ci blogic_sort_probeinfo sorts a section of blogic_probeinfo_list in order 56662306a36Sopenharmony_ci of increasing PCI Bus and Device Number. 56762306a36Sopenharmony_ci*/ 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_cistatic void __init blogic_sort_probeinfo(struct blogic_probeinfo 57062306a36Sopenharmony_ci *probeinfo_list, int probeinfo_cnt) 57162306a36Sopenharmony_ci{ 57262306a36Sopenharmony_ci int last_exchange = probeinfo_cnt - 1, bound, j; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci while (last_exchange > 0) { 57562306a36Sopenharmony_ci bound = last_exchange; 57662306a36Sopenharmony_ci last_exchange = 0; 57762306a36Sopenharmony_ci for (j = 0; j < bound; j++) { 57862306a36Sopenharmony_ci struct blogic_probeinfo *probeinfo1 = 57962306a36Sopenharmony_ci &probeinfo_list[j]; 58062306a36Sopenharmony_ci struct blogic_probeinfo *probeinfo2 = 58162306a36Sopenharmony_ci &probeinfo_list[j + 1]; 58262306a36Sopenharmony_ci if (probeinfo1->bus > probeinfo2->bus || 58362306a36Sopenharmony_ci (probeinfo1->bus == probeinfo2->bus && 58462306a36Sopenharmony_ci (probeinfo1->dev > probeinfo2->dev))) { 58562306a36Sopenharmony_ci struct blogic_probeinfo tmp_probeinfo; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci memcpy(&tmp_probeinfo, probeinfo1, 58862306a36Sopenharmony_ci sizeof(struct blogic_probeinfo)); 58962306a36Sopenharmony_ci memcpy(probeinfo1, probeinfo2, 59062306a36Sopenharmony_ci sizeof(struct blogic_probeinfo)); 59162306a36Sopenharmony_ci memcpy(probeinfo2, &tmp_probeinfo, 59262306a36Sopenharmony_ci sizeof(struct blogic_probeinfo)); 59362306a36Sopenharmony_ci last_exchange = j; 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci/* 60162306a36Sopenharmony_ci blogic_init_mm_probeinfo initializes the list of I/O Address 60262306a36Sopenharmony_ci and Bus Probe Information to be checked for potential BusLogic MultiMaster 60362306a36Sopenharmony_ci SCSI Host Adapters by interrogating the PCI Configuration Space on PCI 60462306a36Sopenharmony_ci machines as well as from the list of standard BusLogic MultiMaster ISA 60562306a36Sopenharmony_ci I/O Addresses. It returns the number of PCI MultiMaster Host Adapters found. 60662306a36Sopenharmony_ci*/ 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_cistatic int __init blogic_init_mm_probeinfo(struct blogic_adapter *adapter) 60962306a36Sopenharmony_ci{ 61062306a36Sopenharmony_ci struct blogic_probeinfo *pr_probeinfo = 61162306a36Sopenharmony_ci &blogic_probeinfo_list[blogic_probeinfo_count]; 61262306a36Sopenharmony_ci int nonpr_mmindex = blogic_probeinfo_count + 1; 61362306a36Sopenharmony_ci int nonpr_mmcount = 0, mmcount = 0; 61462306a36Sopenharmony_ci bool force_scan_order = false; 61562306a36Sopenharmony_ci bool force_scan_order_checked = false; 61662306a36Sopenharmony_ci struct pci_dev *pci_device = NULL; 61762306a36Sopenharmony_ci int i; 61862306a36Sopenharmony_ci if (blogic_probeinfo_count >= BLOGIC_MAX_ADAPTERS) 61962306a36Sopenharmony_ci return 0; 62062306a36Sopenharmony_ci blogic_probeinfo_count++; 62162306a36Sopenharmony_ci /* 62262306a36Sopenharmony_ci Iterate over the MultiMaster PCI Host Adapters. For each 62362306a36Sopenharmony_ci enumerated host adapter, determine whether its ISA Compatible 62462306a36Sopenharmony_ci I/O Port is enabled and if so, whether it is assigned the 62562306a36Sopenharmony_ci Primary I/O Address. A host adapter that is assigned the 62662306a36Sopenharmony_ci Primary I/O Address will always be the preferred boot device. 62762306a36Sopenharmony_ci The MultiMaster BIOS will first recognize a host adapter at 62862306a36Sopenharmony_ci the Primary I/O Address, then any other PCI host adapters, 62962306a36Sopenharmony_ci and finally any host adapters located at the remaining 63062306a36Sopenharmony_ci standard ISA I/O Addresses. When a PCI host adapter is found 63162306a36Sopenharmony_ci with its ISA Compatible I/O Port enabled, a command is issued 63262306a36Sopenharmony_ci to disable the ISA Compatible I/O Port, and it is noted that the 63362306a36Sopenharmony_ci particular standard ISA I/O Address need not be probed. 63462306a36Sopenharmony_ci */ 63562306a36Sopenharmony_ci pr_probeinfo->io_addr = 0; 63662306a36Sopenharmony_ci while ((pci_device = pci_get_device(PCI_VENDOR_ID_BUSLOGIC, 63762306a36Sopenharmony_ci PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER, 63862306a36Sopenharmony_ci pci_device)) != NULL) { 63962306a36Sopenharmony_ci struct blogic_adapter *host_adapter = adapter; 64062306a36Sopenharmony_ci struct blogic_adapter_info adapter_info; 64162306a36Sopenharmony_ci enum blogic_isa_ioport mod_ioaddr_req; 64262306a36Sopenharmony_ci unsigned char bus; 64362306a36Sopenharmony_ci unsigned char device; 64462306a36Sopenharmony_ci unsigned int irq_ch; 64562306a36Sopenharmony_ci unsigned long base_addr0; 64662306a36Sopenharmony_ci unsigned long base_addr1; 64762306a36Sopenharmony_ci unsigned long io_addr; 64862306a36Sopenharmony_ci unsigned long pci_addr; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci if (pci_enable_device(pci_device)) 65162306a36Sopenharmony_ci continue; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci if (dma_set_mask(&pci_device->dev, DMA_BIT_MASK(32))) 65462306a36Sopenharmony_ci continue; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci bus = pci_device->bus->number; 65762306a36Sopenharmony_ci device = pci_device->devfn >> 3; 65862306a36Sopenharmony_ci irq_ch = pci_device->irq; 65962306a36Sopenharmony_ci io_addr = base_addr0 = pci_resource_start(pci_device, 0); 66062306a36Sopenharmony_ci pci_addr = base_addr1 = pci_resource_start(pci_device, 1); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci if (pci_resource_flags(pci_device, 0) & IORESOURCE_MEM) { 66362306a36Sopenharmony_ci blogic_err("BusLogic: Base Address0 0x%lX not I/O for MultiMaster Host Adapter\n", NULL, base_addr0); 66462306a36Sopenharmony_ci blogic_err("at PCI Bus %d Device %d I/O Address 0x%lX\n", NULL, bus, device, io_addr); 66562306a36Sopenharmony_ci continue; 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci if (pci_resource_flags(pci_device, 1) & IORESOURCE_IO) { 66862306a36Sopenharmony_ci blogic_err("BusLogic: Base Address1 0x%lX not Memory for MultiMaster Host Adapter\n", NULL, base_addr1); 66962306a36Sopenharmony_ci blogic_err("at PCI Bus %d Device %d PCI Address 0x%lX\n", NULL, bus, device, pci_addr); 67062306a36Sopenharmony_ci continue; 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci if (irq_ch == 0) { 67362306a36Sopenharmony_ci blogic_err("BusLogic: IRQ Channel %d invalid for MultiMaster Host Adapter\n", NULL, irq_ch); 67462306a36Sopenharmony_ci blogic_err("at PCI Bus %d Device %d I/O Address 0x%lX\n", NULL, bus, device, io_addr); 67562306a36Sopenharmony_ci continue; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci if (blogic_global_options.trace_probe) { 67862306a36Sopenharmony_ci blogic_notice("BusLogic: PCI MultiMaster Host Adapter detected at\n", NULL); 67962306a36Sopenharmony_ci blogic_notice("BusLogic: PCI Bus %d Device %d I/O Address 0x%lX PCI Address 0x%lX\n", NULL, bus, device, io_addr, pci_addr); 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci /* 68262306a36Sopenharmony_ci Issue the Inquire PCI Host Adapter Information command to determine 68362306a36Sopenharmony_ci the ISA Compatible I/O Port. If the ISA Compatible I/O Port is 68462306a36Sopenharmony_ci known and enabled, note that the particular Standard ISA I/O 68562306a36Sopenharmony_ci Address should not be probed. 68662306a36Sopenharmony_ci */ 68762306a36Sopenharmony_ci host_adapter->io_addr = io_addr; 68862306a36Sopenharmony_ci blogic_intreset(host_adapter); 68962306a36Sopenharmony_ci if (blogic_cmd(host_adapter, BLOGIC_INQ_PCI_INFO, NULL, 0, 69062306a36Sopenharmony_ci &adapter_info, sizeof(adapter_info)) != 69162306a36Sopenharmony_ci sizeof(adapter_info)) 69262306a36Sopenharmony_ci adapter_info.isa_port = BLOGIC_IO_DISABLE; 69362306a36Sopenharmony_ci /* 69462306a36Sopenharmony_ci Issue the Modify I/O Address command to disable the 69562306a36Sopenharmony_ci ISA Compatible I/O Port. On PCI Host Adapters, the 69662306a36Sopenharmony_ci Modify I/O Address command allows modification of the 69762306a36Sopenharmony_ci ISA compatible I/O Address that the Host Adapter 69862306a36Sopenharmony_ci responds to; it does not affect the PCI compliant 69962306a36Sopenharmony_ci I/O Address assigned at system initialization. 70062306a36Sopenharmony_ci */ 70162306a36Sopenharmony_ci mod_ioaddr_req = BLOGIC_IO_DISABLE; 70262306a36Sopenharmony_ci blogic_cmd(host_adapter, BLOGIC_MOD_IOADDR, &mod_ioaddr_req, 70362306a36Sopenharmony_ci sizeof(mod_ioaddr_req), NULL, 0); 70462306a36Sopenharmony_ci /* 70562306a36Sopenharmony_ci For the first MultiMaster Host Adapter enumerated, 70662306a36Sopenharmony_ci issue the Fetch Host Adapter Local RAM command to read 70762306a36Sopenharmony_ci byte 45 of the AutoSCSI area, for the setting of the 70862306a36Sopenharmony_ci "Use Bus And Device # For PCI Scanning Seq." option. 70962306a36Sopenharmony_ci Issue the Inquire Board ID command since this option is 71062306a36Sopenharmony_ci only valid for the BT-948/958/958D. 71162306a36Sopenharmony_ci */ 71262306a36Sopenharmony_ci if (!force_scan_order_checked) { 71362306a36Sopenharmony_ci struct blogic_fetch_localram fetch_localram; 71462306a36Sopenharmony_ci struct blogic_autoscsi_byte45 autoscsi_byte45; 71562306a36Sopenharmony_ci struct blogic_board_id id; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci fetch_localram.offset = BLOGIC_AUTOSCSI_BASE + 45; 71862306a36Sopenharmony_ci fetch_localram.count = sizeof(autoscsi_byte45); 71962306a36Sopenharmony_ci blogic_cmd(host_adapter, BLOGIC_FETCH_LOCALRAM, 72062306a36Sopenharmony_ci &fetch_localram, sizeof(fetch_localram), 72162306a36Sopenharmony_ci &autoscsi_byte45, 72262306a36Sopenharmony_ci sizeof(autoscsi_byte45)); 72362306a36Sopenharmony_ci blogic_cmd(host_adapter, BLOGIC_GET_BOARD_ID, NULL, 0, 72462306a36Sopenharmony_ci &id, sizeof(id)); 72562306a36Sopenharmony_ci if (id.fw_ver_digit1 == '5') 72662306a36Sopenharmony_ci force_scan_order = 72762306a36Sopenharmony_ci autoscsi_byte45.force_scan_order; 72862306a36Sopenharmony_ci force_scan_order_checked = true; 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci /* 73162306a36Sopenharmony_ci Determine whether this MultiMaster Host Adapter has its 73262306a36Sopenharmony_ci ISA Compatible I/O Port enabled and is assigned the 73362306a36Sopenharmony_ci Primary I/O Address. If it does, then it is the Primary 73462306a36Sopenharmony_ci MultiMaster Host Adapter and must be recognized first. 73562306a36Sopenharmony_ci If it does not, then it is added to the list for probing 73662306a36Sopenharmony_ci after any Primary MultiMaster Host Adapter is probed. 73762306a36Sopenharmony_ci */ 73862306a36Sopenharmony_ci if (adapter_info.isa_port == BLOGIC_IO_330) { 73962306a36Sopenharmony_ci pr_probeinfo->adapter_type = BLOGIC_MULTIMASTER; 74062306a36Sopenharmony_ci pr_probeinfo->adapter_bus_type = BLOGIC_PCI_BUS; 74162306a36Sopenharmony_ci pr_probeinfo->io_addr = io_addr; 74262306a36Sopenharmony_ci pr_probeinfo->pci_addr = pci_addr; 74362306a36Sopenharmony_ci pr_probeinfo->bus = bus; 74462306a36Sopenharmony_ci pr_probeinfo->dev = device; 74562306a36Sopenharmony_ci pr_probeinfo->irq_ch = irq_ch; 74662306a36Sopenharmony_ci pr_probeinfo->pci_device = pci_dev_get(pci_device); 74762306a36Sopenharmony_ci mmcount++; 74862306a36Sopenharmony_ci } else if (blogic_probeinfo_count < BLOGIC_MAX_ADAPTERS) { 74962306a36Sopenharmony_ci struct blogic_probeinfo *probeinfo = 75062306a36Sopenharmony_ci &blogic_probeinfo_list[blogic_probeinfo_count++]; 75162306a36Sopenharmony_ci probeinfo->adapter_type = BLOGIC_MULTIMASTER; 75262306a36Sopenharmony_ci probeinfo->adapter_bus_type = BLOGIC_PCI_BUS; 75362306a36Sopenharmony_ci probeinfo->io_addr = io_addr; 75462306a36Sopenharmony_ci probeinfo->pci_addr = pci_addr; 75562306a36Sopenharmony_ci probeinfo->bus = bus; 75662306a36Sopenharmony_ci probeinfo->dev = device; 75762306a36Sopenharmony_ci probeinfo->irq_ch = irq_ch; 75862306a36Sopenharmony_ci probeinfo->pci_device = pci_dev_get(pci_device); 75962306a36Sopenharmony_ci nonpr_mmcount++; 76062306a36Sopenharmony_ci mmcount++; 76162306a36Sopenharmony_ci } else 76262306a36Sopenharmony_ci blogic_warn("BusLogic: Too many Host Adapters detected\n", NULL); 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci /* 76562306a36Sopenharmony_ci If the AutoSCSI "Use Bus And Device # For PCI Scanning Seq." 76662306a36Sopenharmony_ci option is ON for the first enumerated MultiMaster Host Adapter, 76762306a36Sopenharmony_ci and if that host adapter is a BT-948/958/958D, then the 76862306a36Sopenharmony_ci MultiMaster BIOS will recognize MultiMaster Host Adapters in 76962306a36Sopenharmony_ci the order of increasing PCI Bus and Device Number. In that case, 77062306a36Sopenharmony_ci sort the probe information into the same order the BIOS uses. 77162306a36Sopenharmony_ci If this option is OFF, then the MultiMaster BIOS will recognize 77262306a36Sopenharmony_ci MultiMaster Host Adapters in the order they are enumerated by 77362306a36Sopenharmony_ci the PCI BIOS, and hence no sorting is necessary. 77462306a36Sopenharmony_ci */ 77562306a36Sopenharmony_ci if (force_scan_order) 77662306a36Sopenharmony_ci blogic_sort_probeinfo(&blogic_probeinfo_list[nonpr_mmindex], 77762306a36Sopenharmony_ci nonpr_mmcount); 77862306a36Sopenharmony_ci /* 77962306a36Sopenharmony_ci Iterate over the older non-compliant MultiMaster PCI Host Adapters, 78062306a36Sopenharmony_ci noting the PCI bus location and assigned IRQ Channel. 78162306a36Sopenharmony_ci */ 78262306a36Sopenharmony_ci pci_device = NULL; 78362306a36Sopenharmony_ci while ((pci_device = pci_get_device(PCI_VENDOR_ID_BUSLOGIC, 78462306a36Sopenharmony_ci PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER_NC, 78562306a36Sopenharmony_ci pci_device)) != NULL) { 78662306a36Sopenharmony_ci unsigned char bus; 78762306a36Sopenharmony_ci unsigned char device; 78862306a36Sopenharmony_ci unsigned int irq_ch; 78962306a36Sopenharmony_ci unsigned long io_addr; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci if (pci_enable_device(pci_device)) 79262306a36Sopenharmony_ci continue; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci if (dma_set_mask(&pci_device->dev, DMA_BIT_MASK(32))) 79562306a36Sopenharmony_ci continue; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci bus = pci_device->bus->number; 79862306a36Sopenharmony_ci device = pci_device->devfn >> 3; 79962306a36Sopenharmony_ci irq_ch = pci_device->irq; 80062306a36Sopenharmony_ci io_addr = pci_resource_start(pci_device, 0); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci if (io_addr == 0 || irq_ch == 0) 80362306a36Sopenharmony_ci continue; 80462306a36Sopenharmony_ci for (i = 0; i < blogic_probeinfo_count; i++) { 80562306a36Sopenharmony_ci struct blogic_probeinfo *probeinfo = 80662306a36Sopenharmony_ci &blogic_probeinfo_list[i]; 80762306a36Sopenharmony_ci if (probeinfo->io_addr == io_addr && 80862306a36Sopenharmony_ci probeinfo->adapter_type == BLOGIC_MULTIMASTER) { 80962306a36Sopenharmony_ci probeinfo->adapter_bus_type = BLOGIC_PCI_BUS; 81062306a36Sopenharmony_ci probeinfo->pci_addr = 0; 81162306a36Sopenharmony_ci probeinfo->bus = bus; 81262306a36Sopenharmony_ci probeinfo->dev = device; 81362306a36Sopenharmony_ci probeinfo->irq_ch = irq_ch; 81462306a36Sopenharmony_ci probeinfo->pci_device = pci_dev_get(pci_device); 81562306a36Sopenharmony_ci break; 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci } 81962306a36Sopenharmony_ci return mmcount; 82062306a36Sopenharmony_ci} 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci/* 82462306a36Sopenharmony_ci blogic_init_fp_probeinfo initializes the list of I/O Address 82562306a36Sopenharmony_ci and Bus Probe Information to be checked for potential BusLogic FlashPoint 82662306a36Sopenharmony_ci Host Adapters by interrogating the PCI Configuration Space. It returns the 82762306a36Sopenharmony_ci number of FlashPoint Host Adapters found. 82862306a36Sopenharmony_ci*/ 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_cistatic int __init blogic_init_fp_probeinfo(struct blogic_adapter *adapter) 83162306a36Sopenharmony_ci{ 83262306a36Sopenharmony_ci int fpindex = blogic_probeinfo_count, fpcount = 0; 83362306a36Sopenharmony_ci struct pci_dev *pci_device = NULL; 83462306a36Sopenharmony_ci /* 83562306a36Sopenharmony_ci Interrogate PCI Configuration Space for any FlashPoint Host Adapters. 83662306a36Sopenharmony_ci */ 83762306a36Sopenharmony_ci while ((pci_device = pci_get_device(PCI_VENDOR_ID_BUSLOGIC, 83862306a36Sopenharmony_ci PCI_DEVICE_ID_BUSLOGIC_FLASHPOINT, 83962306a36Sopenharmony_ci pci_device)) != NULL) { 84062306a36Sopenharmony_ci unsigned char bus; 84162306a36Sopenharmony_ci unsigned char device; 84262306a36Sopenharmony_ci unsigned int irq_ch; 84362306a36Sopenharmony_ci unsigned long base_addr0; 84462306a36Sopenharmony_ci unsigned long base_addr1; 84562306a36Sopenharmony_ci unsigned long io_addr; 84662306a36Sopenharmony_ci unsigned long pci_addr; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci if (pci_enable_device(pci_device)) 84962306a36Sopenharmony_ci continue; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci if (dma_set_mask(&pci_device->dev, DMA_BIT_MASK(32))) 85262306a36Sopenharmony_ci continue; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci bus = pci_device->bus->number; 85562306a36Sopenharmony_ci device = pci_device->devfn >> 3; 85662306a36Sopenharmony_ci irq_ch = pci_device->irq; 85762306a36Sopenharmony_ci io_addr = base_addr0 = pci_resource_start(pci_device, 0); 85862306a36Sopenharmony_ci pci_addr = base_addr1 = pci_resource_start(pci_device, 1); 85962306a36Sopenharmony_ci#ifdef CONFIG_SCSI_FLASHPOINT 86062306a36Sopenharmony_ci if (pci_resource_flags(pci_device, 0) & IORESOURCE_MEM) { 86162306a36Sopenharmony_ci blogic_err("BusLogic: Base Address0 0x%lX not I/O for FlashPoint Host Adapter\n", NULL, base_addr0); 86262306a36Sopenharmony_ci blogic_err("at PCI Bus %d Device %d I/O Address 0x%lX\n", NULL, bus, device, io_addr); 86362306a36Sopenharmony_ci continue; 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci if (pci_resource_flags(pci_device, 1) & IORESOURCE_IO) { 86662306a36Sopenharmony_ci blogic_err("BusLogic: Base Address1 0x%lX not Memory for FlashPoint Host Adapter\n", NULL, base_addr1); 86762306a36Sopenharmony_ci blogic_err("at PCI Bus %d Device %d PCI Address 0x%lX\n", NULL, bus, device, pci_addr); 86862306a36Sopenharmony_ci continue; 86962306a36Sopenharmony_ci } 87062306a36Sopenharmony_ci if (irq_ch == 0) { 87162306a36Sopenharmony_ci blogic_err("BusLogic: IRQ Channel %d invalid for FlashPoint Host Adapter\n", NULL, irq_ch); 87262306a36Sopenharmony_ci blogic_err("at PCI Bus %d Device %d I/O Address 0x%lX\n", NULL, bus, device, io_addr); 87362306a36Sopenharmony_ci continue; 87462306a36Sopenharmony_ci } 87562306a36Sopenharmony_ci if (blogic_global_options.trace_probe) { 87662306a36Sopenharmony_ci blogic_notice("BusLogic: FlashPoint Host Adapter detected at\n", NULL); 87762306a36Sopenharmony_ci blogic_notice("BusLogic: PCI Bus %d Device %d I/O Address 0x%lX PCI Address 0x%lX\n", NULL, bus, device, io_addr, pci_addr); 87862306a36Sopenharmony_ci } 87962306a36Sopenharmony_ci if (blogic_probeinfo_count < BLOGIC_MAX_ADAPTERS) { 88062306a36Sopenharmony_ci struct blogic_probeinfo *probeinfo = 88162306a36Sopenharmony_ci &blogic_probeinfo_list[blogic_probeinfo_count++]; 88262306a36Sopenharmony_ci probeinfo->adapter_type = BLOGIC_FLASHPOINT; 88362306a36Sopenharmony_ci probeinfo->adapter_bus_type = BLOGIC_PCI_BUS; 88462306a36Sopenharmony_ci probeinfo->io_addr = io_addr; 88562306a36Sopenharmony_ci probeinfo->pci_addr = pci_addr; 88662306a36Sopenharmony_ci probeinfo->bus = bus; 88762306a36Sopenharmony_ci probeinfo->dev = device; 88862306a36Sopenharmony_ci probeinfo->irq_ch = irq_ch; 88962306a36Sopenharmony_ci probeinfo->pci_device = pci_dev_get(pci_device); 89062306a36Sopenharmony_ci fpcount++; 89162306a36Sopenharmony_ci } else 89262306a36Sopenharmony_ci blogic_warn("BusLogic: Too many Host Adapters detected\n", NULL); 89362306a36Sopenharmony_ci#else 89462306a36Sopenharmony_ci blogic_err("BusLogic: FlashPoint Host Adapter detected at PCI Bus %d Device %d\n", NULL, bus, device); 89562306a36Sopenharmony_ci blogic_err("BusLogic: I/O Address 0x%lX PCI Address 0x%lX, irq %d, but FlashPoint\n", NULL, io_addr, pci_addr, irq_ch); 89662306a36Sopenharmony_ci blogic_err("BusLogic: support was omitted in this kernel configuration.\n", NULL); 89762306a36Sopenharmony_ci#endif 89862306a36Sopenharmony_ci } 89962306a36Sopenharmony_ci /* 90062306a36Sopenharmony_ci The FlashPoint BIOS will scan for FlashPoint Host Adapters in the order of 90162306a36Sopenharmony_ci increasing PCI Bus and Device Number, so sort the probe information into 90262306a36Sopenharmony_ci the same order the BIOS uses. 90362306a36Sopenharmony_ci */ 90462306a36Sopenharmony_ci blogic_sort_probeinfo(&blogic_probeinfo_list[fpindex], fpcount); 90562306a36Sopenharmony_ci return fpcount; 90662306a36Sopenharmony_ci} 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci/* 91062306a36Sopenharmony_ci blogic_init_probeinfo_list initializes the list of I/O Address and Bus 91162306a36Sopenharmony_ci Probe Information to be checked for potential BusLogic SCSI Host Adapters by 91262306a36Sopenharmony_ci interrogating the PCI Configuration Space on PCI machines as well as from the 91362306a36Sopenharmony_ci list of standard BusLogic MultiMaster ISA I/O Addresses. By default, if both 91462306a36Sopenharmony_ci FlashPoint and PCI MultiMaster Host Adapters are present, this driver will 91562306a36Sopenharmony_ci probe for FlashPoint Host Adapters first unless the BIOS primary disk is 91662306a36Sopenharmony_ci controlled by the first PCI MultiMaster Host Adapter, in which case 91762306a36Sopenharmony_ci MultiMaster Host Adapters will be probed first. The BusLogic Driver Options 91862306a36Sopenharmony_ci specifications "MultiMasterFirst" and "FlashPointFirst" can be used to force 91962306a36Sopenharmony_ci a particular probe order. 92062306a36Sopenharmony_ci*/ 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_cistatic void __init blogic_init_probeinfo_list(struct blogic_adapter *adapter) 92362306a36Sopenharmony_ci{ 92462306a36Sopenharmony_ci /* 92562306a36Sopenharmony_ci If a PCI BIOS is present, interrogate it for MultiMaster and 92662306a36Sopenharmony_ci FlashPoint Host Adapters; otherwise, default to the standard 92762306a36Sopenharmony_ci ISA MultiMaster probe. 92862306a36Sopenharmony_ci */ 92962306a36Sopenharmony_ci if (!blogic_probe_options.noprobe_pci) { 93062306a36Sopenharmony_ci if (blogic_probe_options.multimaster_first) { 93162306a36Sopenharmony_ci blogic_init_mm_probeinfo(adapter); 93262306a36Sopenharmony_ci blogic_init_fp_probeinfo(adapter); 93362306a36Sopenharmony_ci } else if (blogic_probe_options.flashpoint_first) { 93462306a36Sopenharmony_ci blogic_init_fp_probeinfo(adapter); 93562306a36Sopenharmony_ci blogic_init_mm_probeinfo(adapter); 93662306a36Sopenharmony_ci } else { 93762306a36Sopenharmony_ci int fpcount = blogic_init_fp_probeinfo(adapter); 93862306a36Sopenharmony_ci int mmcount = blogic_init_mm_probeinfo(adapter); 93962306a36Sopenharmony_ci if (fpcount > 0 && mmcount > 0) { 94062306a36Sopenharmony_ci struct blogic_probeinfo *probeinfo = 94162306a36Sopenharmony_ci &blogic_probeinfo_list[fpcount]; 94262306a36Sopenharmony_ci struct blogic_adapter *myadapter = adapter; 94362306a36Sopenharmony_ci struct blogic_fetch_localram fetch_localram; 94462306a36Sopenharmony_ci struct blogic_bios_drvmap d0_mapbyte; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci while (probeinfo->adapter_bus_type != 94762306a36Sopenharmony_ci BLOGIC_PCI_BUS) 94862306a36Sopenharmony_ci probeinfo++; 94962306a36Sopenharmony_ci myadapter->io_addr = probeinfo->io_addr; 95062306a36Sopenharmony_ci fetch_localram.offset = 95162306a36Sopenharmony_ci BLOGIC_BIOS_BASE + BLOGIC_BIOS_DRVMAP; 95262306a36Sopenharmony_ci fetch_localram.count = sizeof(d0_mapbyte); 95362306a36Sopenharmony_ci blogic_cmd(myadapter, BLOGIC_FETCH_LOCALRAM, 95462306a36Sopenharmony_ci &fetch_localram, 95562306a36Sopenharmony_ci sizeof(fetch_localram), 95662306a36Sopenharmony_ci &d0_mapbyte, 95762306a36Sopenharmony_ci sizeof(d0_mapbyte)); 95862306a36Sopenharmony_ci /* 95962306a36Sopenharmony_ci If the Map Byte for BIOS Drive 0 indicates 96062306a36Sopenharmony_ci that BIOS Drive 0 is controlled by this 96162306a36Sopenharmony_ci PCI MultiMaster Host Adapter, then reverse 96262306a36Sopenharmony_ci the probe order so that MultiMaster Host 96362306a36Sopenharmony_ci Adapters are probed before FlashPoint Host 96462306a36Sopenharmony_ci Adapters. 96562306a36Sopenharmony_ci */ 96662306a36Sopenharmony_ci if (d0_mapbyte.diskgeom != BLOGIC_BIOS_NODISK) { 96762306a36Sopenharmony_ci struct blogic_probeinfo saved_probeinfo[BLOGIC_MAX_ADAPTERS]; 96862306a36Sopenharmony_ci int mmcount = blogic_probeinfo_count - fpcount; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci memcpy(saved_probeinfo, 97162306a36Sopenharmony_ci blogic_probeinfo_list, 97262306a36Sopenharmony_ci blogic_probeinfo_count * sizeof(struct blogic_probeinfo)); 97362306a36Sopenharmony_ci memcpy(&blogic_probeinfo_list[0], 97462306a36Sopenharmony_ci &saved_probeinfo[fpcount], 97562306a36Sopenharmony_ci mmcount * sizeof(struct blogic_probeinfo)); 97662306a36Sopenharmony_ci memcpy(&blogic_probeinfo_list[mmcount], 97762306a36Sopenharmony_ci &saved_probeinfo[0], 97862306a36Sopenharmony_ci fpcount * sizeof(struct blogic_probeinfo)); 97962306a36Sopenharmony_ci } 98062306a36Sopenharmony_ci } 98162306a36Sopenharmony_ci } 98262306a36Sopenharmony_ci } 98362306a36Sopenharmony_ci} 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci/* 98762306a36Sopenharmony_ci blogic_failure prints a standardized error message, and then returns false. 98862306a36Sopenharmony_ci*/ 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_cistatic bool blogic_failure(struct blogic_adapter *adapter, char *msg) 99162306a36Sopenharmony_ci{ 99262306a36Sopenharmony_ci blogic_announce_drvr(adapter); 99362306a36Sopenharmony_ci if (adapter->adapter_bus_type == BLOGIC_PCI_BUS) { 99462306a36Sopenharmony_ci blogic_err("While configuring BusLogic PCI Host Adapter at\n", 99562306a36Sopenharmony_ci adapter); 99662306a36Sopenharmony_ci blogic_err("Bus %d Device %d I/O Address 0x%lX PCI Address 0x%lX:\n", adapter, adapter->bus, adapter->dev, adapter->io_addr, adapter->pci_addr); 99762306a36Sopenharmony_ci } else 99862306a36Sopenharmony_ci blogic_err("While configuring BusLogic Host Adapter at I/O Address 0x%lX:\n", adapter, adapter->io_addr); 99962306a36Sopenharmony_ci blogic_err("%s FAILED - DETACHING\n", adapter, msg); 100062306a36Sopenharmony_ci if (blogic_cmd_failure_reason != NULL) 100162306a36Sopenharmony_ci blogic_err("ADDITIONAL FAILURE INFO - %s\n", adapter, 100262306a36Sopenharmony_ci blogic_cmd_failure_reason); 100362306a36Sopenharmony_ci return false; 100462306a36Sopenharmony_ci} 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci/* 100862306a36Sopenharmony_ci blogic_probe probes for a BusLogic Host Adapter. 100962306a36Sopenharmony_ci*/ 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_cistatic bool __init blogic_probe(struct blogic_adapter *adapter) 101262306a36Sopenharmony_ci{ 101362306a36Sopenharmony_ci union blogic_stat_reg statusreg; 101462306a36Sopenharmony_ci union blogic_int_reg intreg; 101562306a36Sopenharmony_ci union blogic_geo_reg georeg; 101662306a36Sopenharmony_ci /* 101762306a36Sopenharmony_ci FlashPoint Host Adapters are Probed by the FlashPoint SCCB Manager. 101862306a36Sopenharmony_ci */ 101962306a36Sopenharmony_ci if (blogic_flashpoint_type(adapter)) { 102062306a36Sopenharmony_ci struct fpoint_info *fpinfo = &adapter->fpinfo; 102162306a36Sopenharmony_ci fpinfo->base_addr = (u32) adapter->io_addr; 102262306a36Sopenharmony_ci fpinfo->irq_ch = adapter->irq_ch; 102362306a36Sopenharmony_ci fpinfo->present = false; 102462306a36Sopenharmony_ci if (!(FlashPoint_ProbeHostAdapter(fpinfo) == 0 && 102562306a36Sopenharmony_ci fpinfo->present)) { 102662306a36Sopenharmony_ci blogic_err("BusLogic: FlashPoint Host Adapter detected at PCI Bus %d Device %d\n", adapter, adapter->bus, adapter->dev); 102762306a36Sopenharmony_ci blogic_err("BusLogic: I/O Address 0x%lX PCI Address 0x%lX, but FlashPoint\n", adapter, adapter->io_addr, adapter->pci_addr); 102862306a36Sopenharmony_ci blogic_err("BusLogic: Probe Function failed to validate it.\n", adapter); 102962306a36Sopenharmony_ci return false; 103062306a36Sopenharmony_ci } 103162306a36Sopenharmony_ci if (blogic_global_options.trace_probe) 103262306a36Sopenharmony_ci blogic_notice("BusLogic_Probe(0x%lX): FlashPoint Found\n", adapter, adapter->io_addr); 103362306a36Sopenharmony_ci /* 103462306a36Sopenharmony_ci Indicate the Host Adapter Probe completed successfully. 103562306a36Sopenharmony_ci */ 103662306a36Sopenharmony_ci return true; 103762306a36Sopenharmony_ci } 103862306a36Sopenharmony_ci /* 103962306a36Sopenharmony_ci Read the Status, Interrupt, and Geometry Registers to test if there are I/O 104062306a36Sopenharmony_ci ports that respond, and to check the values to determine if they are from a 104162306a36Sopenharmony_ci BusLogic Host Adapter. A nonexistent I/O port will return 0xFF, in which 104262306a36Sopenharmony_ci case there is definitely no BusLogic Host Adapter at this base I/O Address. 104362306a36Sopenharmony_ci The test here is a subset of that used by the BusLogic Host Adapter BIOS. 104462306a36Sopenharmony_ci */ 104562306a36Sopenharmony_ci statusreg.all = blogic_rdstatus(adapter); 104662306a36Sopenharmony_ci intreg.all = blogic_rdint(adapter); 104762306a36Sopenharmony_ci georeg.all = blogic_rdgeom(adapter); 104862306a36Sopenharmony_ci if (blogic_global_options.trace_probe) 104962306a36Sopenharmony_ci blogic_notice("BusLogic_Probe(0x%lX): Status 0x%02X, Interrupt 0x%02X, Geometry 0x%02X\n", adapter, adapter->io_addr, statusreg.all, intreg.all, georeg.all); 105062306a36Sopenharmony_ci if (statusreg.all == 0 || statusreg.sr.diag_active || 105162306a36Sopenharmony_ci statusreg.sr.cmd_param_busy || statusreg.sr.rsvd || 105262306a36Sopenharmony_ci statusreg.sr.cmd_invalid || intreg.ir.rsvd != 0) 105362306a36Sopenharmony_ci return false; 105462306a36Sopenharmony_ci /* 105562306a36Sopenharmony_ci Check the undocumented Geometry Register to test if there is 105662306a36Sopenharmony_ci an I/O port that responded. Adaptec Host Adapters do not 105762306a36Sopenharmony_ci implement the Geometry Register, so this test helps serve to 105862306a36Sopenharmony_ci avoid incorrectly recognizing an Adaptec 1542A or 1542B as a 105962306a36Sopenharmony_ci BusLogic. Unfortunately, the Adaptec 1542C series does respond 106062306a36Sopenharmony_ci to the Geometry Register I/O port, but it will be rejected 106162306a36Sopenharmony_ci later when the Inquire Extended Setup Information command is 106262306a36Sopenharmony_ci issued in blogic_checkadapter. The AMI FastDisk Host Adapter 106362306a36Sopenharmony_ci is a BusLogic clone that implements the same interface as 106462306a36Sopenharmony_ci earlier BusLogic Host Adapters, including the undocumented 106562306a36Sopenharmony_ci commands, and is therefore supported by this driver. However, 106662306a36Sopenharmony_ci the AMI FastDisk always returns 0x00 upon reading the Geometry 106762306a36Sopenharmony_ci Register, so the extended translation option should always be 106862306a36Sopenharmony_ci left disabled on the AMI FastDisk. 106962306a36Sopenharmony_ci */ 107062306a36Sopenharmony_ci if (georeg.all == 0xFF) 107162306a36Sopenharmony_ci return false; 107262306a36Sopenharmony_ci /* 107362306a36Sopenharmony_ci Indicate the Host Adapter Probe completed successfully. 107462306a36Sopenharmony_ci */ 107562306a36Sopenharmony_ci return true; 107662306a36Sopenharmony_ci} 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci/* 108062306a36Sopenharmony_ci blogic_hwreset issues a Hardware Reset to the Host Adapter 108162306a36Sopenharmony_ci and waits for Host Adapter Diagnostics to complete. If hard_reset is true, a 108262306a36Sopenharmony_ci Hard Reset is performed which also initiates a SCSI Bus Reset. Otherwise, a 108362306a36Sopenharmony_ci Soft Reset is performed which only resets the Host Adapter without forcing a 108462306a36Sopenharmony_ci SCSI Bus Reset. 108562306a36Sopenharmony_ci*/ 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_cistatic bool blogic_hwreset(struct blogic_adapter *adapter, bool hard_reset) 108862306a36Sopenharmony_ci{ 108962306a36Sopenharmony_ci union blogic_stat_reg statusreg; 109062306a36Sopenharmony_ci int timeout; 109162306a36Sopenharmony_ci /* 109262306a36Sopenharmony_ci FlashPoint Host Adapters are Hard Reset by the FlashPoint 109362306a36Sopenharmony_ci SCCB Manager. 109462306a36Sopenharmony_ci */ 109562306a36Sopenharmony_ci if (blogic_flashpoint_type(adapter)) { 109662306a36Sopenharmony_ci struct fpoint_info *fpinfo = &adapter->fpinfo; 109762306a36Sopenharmony_ci fpinfo->softreset = !hard_reset; 109862306a36Sopenharmony_ci fpinfo->report_underrun = true; 109962306a36Sopenharmony_ci adapter->cardhandle = 110062306a36Sopenharmony_ci FlashPoint_HardwareResetHostAdapter(fpinfo); 110162306a36Sopenharmony_ci if (adapter->cardhandle == (void *)FPOINT_BADCARD_HANDLE) 110262306a36Sopenharmony_ci return false; 110362306a36Sopenharmony_ci /* 110462306a36Sopenharmony_ci Indicate the Host Adapter Hard Reset completed successfully. 110562306a36Sopenharmony_ci */ 110662306a36Sopenharmony_ci return true; 110762306a36Sopenharmony_ci } 110862306a36Sopenharmony_ci /* 110962306a36Sopenharmony_ci Issue a Hard Reset or Soft Reset Command to the Host Adapter. 111062306a36Sopenharmony_ci The Host Adapter should respond by setting Diagnostic Active in 111162306a36Sopenharmony_ci the Status Register. 111262306a36Sopenharmony_ci */ 111362306a36Sopenharmony_ci if (hard_reset) 111462306a36Sopenharmony_ci blogic_hardreset(adapter); 111562306a36Sopenharmony_ci else 111662306a36Sopenharmony_ci blogic_softreset(adapter); 111762306a36Sopenharmony_ci /* 111862306a36Sopenharmony_ci Wait until Diagnostic Active is set in the Status Register. 111962306a36Sopenharmony_ci */ 112062306a36Sopenharmony_ci timeout = 5 * 10000; 112162306a36Sopenharmony_ci while (--timeout >= 0) { 112262306a36Sopenharmony_ci statusreg.all = blogic_rdstatus(adapter); 112362306a36Sopenharmony_ci if (statusreg.sr.diag_active) 112462306a36Sopenharmony_ci break; 112562306a36Sopenharmony_ci udelay(100); 112662306a36Sopenharmony_ci } 112762306a36Sopenharmony_ci if (blogic_global_options.trace_hw_reset) 112862306a36Sopenharmony_ci blogic_notice("BusLogic_HardwareReset(0x%lX): Diagnostic Active, Status 0x%02X\n", adapter, adapter->io_addr, statusreg.all); 112962306a36Sopenharmony_ci if (timeout < 0) 113062306a36Sopenharmony_ci return false; 113162306a36Sopenharmony_ci /* 113262306a36Sopenharmony_ci Wait 100 microseconds to allow completion of any initial diagnostic 113362306a36Sopenharmony_ci activity which might leave the contents of the Status Register 113462306a36Sopenharmony_ci unpredictable. 113562306a36Sopenharmony_ci */ 113662306a36Sopenharmony_ci udelay(100); 113762306a36Sopenharmony_ci /* 113862306a36Sopenharmony_ci Wait until Diagnostic Active is reset in the Status Register. 113962306a36Sopenharmony_ci */ 114062306a36Sopenharmony_ci timeout = 10 * 10000; 114162306a36Sopenharmony_ci while (--timeout >= 0) { 114262306a36Sopenharmony_ci statusreg.all = blogic_rdstatus(adapter); 114362306a36Sopenharmony_ci if (!statusreg.sr.diag_active) 114462306a36Sopenharmony_ci break; 114562306a36Sopenharmony_ci udelay(100); 114662306a36Sopenharmony_ci } 114762306a36Sopenharmony_ci if (blogic_global_options.trace_hw_reset) 114862306a36Sopenharmony_ci blogic_notice("BusLogic_HardwareReset(0x%lX): Diagnostic Completed, Status 0x%02X\n", adapter, adapter->io_addr, statusreg.all); 114962306a36Sopenharmony_ci if (timeout < 0) 115062306a36Sopenharmony_ci return false; 115162306a36Sopenharmony_ci /* 115262306a36Sopenharmony_ci Wait until at least one of the Diagnostic Failure, Host Adapter 115362306a36Sopenharmony_ci Ready, or Data In Register Ready bits is set in the Status Register. 115462306a36Sopenharmony_ci */ 115562306a36Sopenharmony_ci timeout = 10000; 115662306a36Sopenharmony_ci while (--timeout >= 0) { 115762306a36Sopenharmony_ci statusreg.all = blogic_rdstatus(adapter); 115862306a36Sopenharmony_ci if (statusreg.sr.diag_failed || statusreg.sr.adapter_ready || 115962306a36Sopenharmony_ci statusreg.sr.datain_ready) 116062306a36Sopenharmony_ci break; 116162306a36Sopenharmony_ci udelay(100); 116262306a36Sopenharmony_ci } 116362306a36Sopenharmony_ci if (blogic_global_options.trace_hw_reset) 116462306a36Sopenharmony_ci blogic_notice("BusLogic_HardwareReset(0x%lX): Host Adapter Ready, Status 0x%02X\n", adapter, adapter->io_addr, statusreg.all); 116562306a36Sopenharmony_ci if (timeout < 0) 116662306a36Sopenharmony_ci return false; 116762306a36Sopenharmony_ci /* 116862306a36Sopenharmony_ci If Diagnostic Failure is set or Host Adapter Ready is reset, 116962306a36Sopenharmony_ci then an error occurred during the Host Adapter diagnostics. 117062306a36Sopenharmony_ci If Data In Register Ready is set, then there is an Error Code 117162306a36Sopenharmony_ci available. 117262306a36Sopenharmony_ci */ 117362306a36Sopenharmony_ci if (statusreg.sr.diag_failed || !statusreg.sr.adapter_ready) { 117462306a36Sopenharmony_ci blogic_cmd_failure_reason = NULL; 117562306a36Sopenharmony_ci blogic_failure(adapter, "HARD RESET DIAGNOSTICS"); 117662306a36Sopenharmony_ci blogic_err("HOST ADAPTER STATUS REGISTER = %02X\n", adapter, 117762306a36Sopenharmony_ci statusreg.all); 117862306a36Sopenharmony_ci if (statusreg.sr.datain_ready) 117962306a36Sopenharmony_ci blogic_err("HOST ADAPTER ERROR CODE = %d\n", adapter, 118062306a36Sopenharmony_ci blogic_rddatain(adapter)); 118162306a36Sopenharmony_ci return false; 118262306a36Sopenharmony_ci } 118362306a36Sopenharmony_ci /* 118462306a36Sopenharmony_ci Indicate the Host Adapter Hard Reset completed successfully. 118562306a36Sopenharmony_ci */ 118662306a36Sopenharmony_ci return true; 118762306a36Sopenharmony_ci} 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci/* 119162306a36Sopenharmony_ci blogic_checkadapter checks to be sure this really is a BusLogic 119262306a36Sopenharmony_ci Host Adapter. 119362306a36Sopenharmony_ci*/ 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_cistatic bool __init blogic_checkadapter(struct blogic_adapter *adapter) 119662306a36Sopenharmony_ci{ 119762306a36Sopenharmony_ci struct blogic_ext_setup ext_setupinfo; 119862306a36Sopenharmony_ci unsigned char req_replylen; 119962306a36Sopenharmony_ci bool result = true; 120062306a36Sopenharmony_ci /* 120162306a36Sopenharmony_ci FlashPoint Host Adapters do not require this protection. 120262306a36Sopenharmony_ci */ 120362306a36Sopenharmony_ci if (blogic_flashpoint_type(adapter)) 120462306a36Sopenharmony_ci return true; 120562306a36Sopenharmony_ci /* 120662306a36Sopenharmony_ci Issue the Inquire Extended Setup Information command. Only genuine 120762306a36Sopenharmony_ci BusLogic Host Adapters and true clones support this command. 120862306a36Sopenharmony_ci Adaptec 1542C series Host Adapters that respond to the Geometry 120962306a36Sopenharmony_ci Register I/O port will fail this command. 121062306a36Sopenharmony_ci */ 121162306a36Sopenharmony_ci req_replylen = sizeof(ext_setupinfo); 121262306a36Sopenharmony_ci if (blogic_cmd(adapter, BLOGIC_INQ_EXTSETUP, &req_replylen, 121362306a36Sopenharmony_ci sizeof(req_replylen), &ext_setupinfo, 121462306a36Sopenharmony_ci sizeof(ext_setupinfo)) != sizeof(ext_setupinfo)) 121562306a36Sopenharmony_ci result = false; 121662306a36Sopenharmony_ci /* 121762306a36Sopenharmony_ci Provide tracing information if requested and return. 121862306a36Sopenharmony_ci */ 121962306a36Sopenharmony_ci if (blogic_global_options.trace_probe) 122062306a36Sopenharmony_ci blogic_notice("BusLogic_Check(0x%lX): MultiMaster %s\n", adapter, 122162306a36Sopenharmony_ci adapter->io_addr, 122262306a36Sopenharmony_ci (result ? "Found" : "Not Found")); 122362306a36Sopenharmony_ci return result; 122462306a36Sopenharmony_ci} 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci/* 122862306a36Sopenharmony_ci blogic_rdconfig reads the Configuration Information 122962306a36Sopenharmony_ci from Host Adapter and initializes the Host Adapter structure. 123062306a36Sopenharmony_ci*/ 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_cistatic bool __init blogic_rdconfig(struct blogic_adapter *adapter) 123362306a36Sopenharmony_ci{ 123462306a36Sopenharmony_ci struct blogic_board_id id; 123562306a36Sopenharmony_ci struct blogic_config config; 123662306a36Sopenharmony_ci struct blogic_setup_info setupinfo; 123762306a36Sopenharmony_ci struct blogic_ext_setup ext_setupinfo; 123862306a36Sopenharmony_ci unsigned char model[5]; 123962306a36Sopenharmony_ci unsigned char fw_ver_digit3; 124062306a36Sopenharmony_ci unsigned char fw_ver_letter; 124162306a36Sopenharmony_ci struct blogic_adapter_info adapter_info; 124262306a36Sopenharmony_ci struct blogic_fetch_localram fetch_localram; 124362306a36Sopenharmony_ci struct blogic_autoscsi autoscsi; 124462306a36Sopenharmony_ci union blogic_geo_reg georeg; 124562306a36Sopenharmony_ci unsigned char req_replylen; 124662306a36Sopenharmony_ci unsigned char *tgt, ch; 124762306a36Sopenharmony_ci int tgt_id, i; 124862306a36Sopenharmony_ci /* 124962306a36Sopenharmony_ci Configuration Information for FlashPoint Host Adapters is 125062306a36Sopenharmony_ci provided in the fpoint_info structure by the FlashPoint 125162306a36Sopenharmony_ci SCCB Manager's Probe Function. Initialize fields in the 125262306a36Sopenharmony_ci Host Adapter structure from the fpoint_info structure. 125362306a36Sopenharmony_ci */ 125462306a36Sopenharmony_ci if (blogic_flashpoint_type(adapter)) { 125562306a36Sopenharmony_ci struct fpoint_info *fpinfo = &adapter->fpinfo; 125662306a36Sopenharmony_ci tgt = adapter->model; 125762306a36Sopenharmony_ci *tgt++ = 'B'; 125862306a36Sopenharmony_ci *tgt++ = 'T'; 125962306a36Sopenharmony_ci *tgt++ = '-'; 126062306a36Sopenharmony_ci for (i = 0; i < sizeof(fpinfo->model); i++) 126162306a36Sopenharmony_ci *tgt++ = fpinfo->model[i]; 126262306a36Sopenharmony_ci *tgt++ = '\0'; 126362306a36Sopenharmony_ci strcpy(adapter->fw_ver, FLASHPOINT_FW_VER); 126462306a36Sopenharmony_ci adapter->scsi_id = fpinfo->scsi_id; 126562306a36Sopenharmony_ci adapter->ext_trans_enable = fpinfo->ext_trans_enable; 126662306a36Sopenharmony_ci adapter->parity = fpinfo->parity; 126762306a36Sopenharmony_ci adapter->reset_enabled = !fpinfo->softreset; 126862306a36Sopenharmony_ci adapter->level_int = true; 126962306a36Sopenharmony_ci adapter->wide = fpinfo->wide; 127062306a36Sopenharmony_ci adapter->differential = false; 127162306a36Sopenharmony_ci adapter->scam = true; 127262306a36Sopenharmony_ci adapter->ultra = true; 127362306a36Sopenharmony_ci adapter->ext_lun = true; 127462306a36Sopenharmony_ci adapter->terminfo_valid = true; 127562306a36Sopenharmony_ci adapter->low_term = fpinfo->low_term; 127662306a36Sopenharmony_ci adapter->high_term = fpinfo->high_term; 127762306a36Sopenharmony_ci adapter->scam_enabled = fpinfo->scam_enabled; 127862306a36Sopenharmony_ci adapter->scam_lev2 = fpinfo->scam_lev2; 127962306a36Sopenharmony_ci adapter->drvr_sglimit = BLOGIC_SG_LIMIT; 128062306a36Sopenharmony_ci adapter->maxdev = (adapter->wide ? 16 : 8); 128162306a36Sopenharmony_ci adapter->maxlun = 32; 128262306a36Sopenharmony_ci adapter->initccbs = 4 * BLOGIC_CCB_GRP_ALLOCSIZE; 128362306a36Sopenharmony_ci adapter->inc_ccbs = BLOGIC_CCB_GRP_ALLOCSIZE; 128462306a36Sopenharmony_ci adapter->drvr_qdepth = 255; 128562306a36Sopenharmony_ci adapter->adapter_qdepth = adapter->drvr_qdepth; 128662306a36Sopenharmony_ci adapter->sync_ok = fpinfo->sync_ok; 128762306a36Sopenharmony_ci adapter->fast_ok = fpinfo->fast_ok; 128862306a36Sopenharmony_ci adapter->ultra_ok = fpinfo->ultra_ok; 128962306a36Sopenharmony_ci adapter->wide_ok = fpinfo->wide_ok; 129062306a36Sopenharmony_ci adapter->discon_ok = fpinfo->discon_ok; 129162306a36Sopenharmony_ci adapter->tagq_ok = 0xFFFF; 129262306a36Sopenharmony_ci goto common; 129362306a36Sopenharmony_ci } 129462306a36Sopenharmony_ci /* 129562306a36Sopenharmony_ci Issue the Inquire Board ID command. 129662306a36Sopenharmony_ci */ 129762306a36Sopenharmony_ci if (blogic_cmd(adapter, BLOGIC_GET_BOARD_ID, NULL, 0, &id, 129862306a36Sopenharmony_ci sizeof(id)) != sizeof(id)) 129962306a36Sopenharmony_ci return blogic_failure(adapter, "INQUIRE BOARD ID"); 130062306a36Sopenharmony_ci /* 130162306a36Sopenharmony_ci Issue the Inquire Configuration command. 130262306a36Sopenharmony_ci */ 130362306a36Sopenharmony_ci if (blogic_cmd(adapter, BLOGIC_INQ_CONFIG, NULL, 0, &config, 130462306a36Sopenharmony_ci sizeof(config)) 130562306a36Sopenharmony_ci != sizeof(config)) 130662306a36Sopenharmony_ci return blogic_failure(adapter, "INQUIRE CONFIGURATION"); 130762306a36Sopenharmony_ci /* 130862306a36Sopenharmony_ci Issue the Inquire Setup Information command. 130962306a36Sopenharmony_ci */ 131062306a36Sopenharmony_ci req_replylen = sizeof(setupinfo); 131162306a36Sopenharmony_ci if (blogic_cmd(adapter, BLOGIC_INQ_SETUPINFO, &req_replylen, 131262306a36Sopenharmony_ci sizeof(req_replylen), &setupinfo, 131362306a36Sopenharmony_ci sizeof(setupinfo)) != sizeof(setupinfo)) 131462306a36Sopenharmony_ci return blogic_failure(adapter, "INQUIRE SETUP INFORMATION"); 131562306a36Sopenharmony_ci /* 131662306a36Sopenharmony_ci Issue the Inquire Extended Setup Information command. 131762306a36Sopenharmony_ci */ 131862306a36Sopenharmony_ci req_replylen = sizeof(ext_setupinfo); 131962306a36Sopenharmony_ci if (blogic_cmd(adapter, BLOGIC_INQ_EXTSETUP, &req_replylen, 132062306a36Sopenharmony_ci sizeof(req_replylen), &ext_setupinfo, 132162306a36Sopenharmony_ci sizeof(ext_setupinfo)) != sizeof(ext_setupinfo)) 132262306a36Sopenharmony_ci return blogic_failure(adapter, 132362306a36Sopenharmony_ci "INQUIRE EXTENDED SETUP INFORMATION"); 132462306a36Sopenharmony_ci /* 132562306a36Sopenharmony_ci Issue the Inquire Firmware Version 3rd Digit command. 132662306a36Sopenharmony_ci */ 132762306a36Sopenharmony_ci fw_ver_digit3 = '\0'; 132862306a36Sopenharmony_ci if (id.fw_ver_digit1 > '0') 132962306a36Sopenharmony_ci if (blogic_cmd(adapter, BLOGIC_INQ_FWVER_D3, NULL, 0, 133062306a36Sopenharmony_ci &fw_ver_digit3, 133162306a36Sopenharmony_ci sizeof(fw_ver_digit3)) != sizeof(fw_ver_digit3)) 133262306a36Sopenharmony_ci return blogic_failure(adapter, 133362306a36Sopenharmony_ci "INQUIRE FIRMWARE 3RD DIGIT"); 133462306a36Sopenharmony_ci /* 133562306a36Sopenharmony_ci Issue the Inquire Host Adapter Model Number command. 133662306a36Sopenharmony_ci */ 133762306a36Sopenharmony_ci if (ext_setupinfo.bus_type == 'A' && id.fw_ver_digit1 == '2') 133862306a36Sopenharmony_ci /* BusLogic BT-542B ISA 2.xx */ 133962306a36Sopenharmony_ci strcpy(model, "542B"); 134062306a36Sopenharmony_ci else if (ext_setupinfo.bus_type == 'E' && id.fw_ver_digit1 == '2' && 134162306a36Sopenharmony_ci (id.fw_ver_digit2 <= '1' || (id.fw_ver_digit2 == '2' && 134262306a36Sopenharmony_ci fw_ver_digit3 == '0'))) 134362306a36Sopenharmony_ci /* BusLogic BT-742A EISA 2.1x or 2.20 */ 134462306a36Sopenharmony_ci strcpy(model, "742A"); 134562306a36Sopenharmony_ci else if (ext_setupinfo.bus_type == 'E' && id.fw_ver_digit1 == '0') 134662306a36Sopenharmony_ci /* AMI FastDisk EISA Series 441 0.x */ 134762306a36Sopenharmony_ci strcpy(model, "747A"); 134862306a36Sopenharmony_ci else { 134962306a36Sopenharmony_ci req_replylen = sizeof(model); 135062306a36Sopenharmony_ci if (blogic_cmd(adapter, BLOGIC_INQ_MODELNO, &req_replylen, 135162306a36Sopenharmony_ci sizeof(req_replylen), &model, 135262306a36Sopenharmony_ci sizeof(model)) != sizeof(model)) 135362306a36Sopenharmony_ci return blogic_failure(adapter, 135462306a36Sopenharmony_ci "INQUIRE HOST ADAPTER MODEL NUMBER"); 135562306a36Sopenharmony_ci } 135662306a36Sopenharmony_ci /* 135762306a36Sopenharmony_ci BusLogic MultiMaster Host Adapters can be identified by their 135862306a36Sopenharmony_ci model number and the major version number of their firmware 135962306a36Sopenharmony_ci as follows: 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci 5.xx BusLogic "W" Series Host Adapters: 136262306a36Sopenharmony_ci BT-948/958/958D 136362306a36Sopenharmony_ci 4.xx BusLogic "C" Series Host Adapters: 136462306a36Sopenharmony_ci BT-946C/956C/956CD/747C/757C/757CD/445C/545C/540CF 136562306a36Sopenharmony_ci 3.xx BusLogic "S" Series Host Adapters: 136662306a36Sopenharmony_ci BT-747S/747D/757S/757D/445S/545S/542D 136762306a36Sopenharmony_ci BT-542B/742A (revision H) 136862306a36Sopenharmony_ci 2.xx BusLogic "A" Series Host Adapters: 136962306a36Sopenharmony_ci BT-542B/742A (revision G and below) 137062306a36Sopenharmony_ci 0.xx AMI FastDisk VLB/EISA BusLogic Clone Host Adapter 137162306a36Sopenharmony_ci */ 137262306a36Sopenharmony_ci /* 137362306a36Sopenharmony_ci Save the Model Name and Host Adapter Name in the Host Adapter 137462306a36Sopenharmony_ci structure. 137562306a36Sopenharmony_ci */ 137662306a36Sopenharmony_ci tgt = adapter->model; 137762306a36Sopenharmony_ci *tgt++ = 'B'; 137862306a36Sopenharmony_ci *tgt++ = 'T'; 137962306a36Sopenharmony_ci *tgt++ = '-'; 138062306a36Sopenharmony_ci for (i = 0; i < sizeof(model); i++) { 138162306a36Sopenharmony_ci ch = model[i]; 138262306a36Sopenharmony_ci if (ch == ' ' || ch == '\0') 138362306a36Sopenharmony_ci break; 138462306a36Sopenharmony_ci *tgt++ = ch; 138562306a36Sopenharmony_ci } 138662306a36Sopenharmony_ci *tgt++ = '\0'; 138762306a36Sopenharmony_ci /* 138862306a36Sopenharmony_ci Save the Firmware Version in the Host Adapter structure. 138962306a36Sopenharmony_ci */ 139062306a36Sopenharmony_ci tgt = adapter->fw_ver; 139162306a36Sopenharmony_ci *tgt++ = id.fw_ver_digit1; 139262306a36Sopenharmony_ci *tgt++ = '.'; 139362306a36Sopenharmony_ci *tgt++ = id.fw_ver_digit2; 139462306a36Sopenharmony_ci if (fw_ver_digit3 != ' ' && fw_ver_digit3 != '\0') 139562306a36Sopenharmony_ci *tgt++ = fw_ver_digit3; 139662306a36Sopenharmony_ci *tgt = '\0'; 139762306a36Sopenharmony_ci /* 139862306a36Sopenharmony_ci Issue the Inquire Firmware Version Letter command. 139962306a36Sopenharmony_ci */ 140062306a36Sopenharmony_ci if (strcmp(adapter->fw_ver, "3.3") >= 0) { 140162306a36Sopenharmony_ci if (blogic_cmd(adapter, BLOGIC_INQ_FWVER_LETTER, NULL, 0, 140262306a36Sopenharmony_ci &fw_ver_letter, 140362306a36Sopenharmony_ci sizeof(fw_ver_letter)) != sizeof(fw_ver_letter)) 140462306a36Sopenharmony_ci return blogic_failure(adapter, 140562306a36Sopenharmony_ci "INQUIRE FIRMWARE VERSION LETTER"); 140662306a36Sopenharmony_ci if (fw_ver_letter != ' ' && fw_ver_letter != '\0') 140762306a36Sopenharmony_ci *tgt++ = fw_ver_letter; 140862306a36Sopenharmony_ci *tgt = '\0'; 140962306a36Sopenharmony_ci } 141062306a36Sopenharmony_ci /* 141162306a36Sopenharmony_ci Save the Host Adapter SCSI ID in the Host Adapter structure. 141262306a36Sopenharmony_ci */ 141362306a36Sopenharmony_ci adapter->scsi_id = config.id; 141462306a36Sopenharmony_ci /* 141562306a36Sopenharmony_ci Determine the Bus Type and save it in the Host Adapter structure, 141662306a36Sopenharmony_ci determine and save the IRQ Channel if necessary, and determine 141762306a36Sopenharmony_ci and save the DMA Channel for ISA Host Adapters. 141862306a36Sopenharmony_ci */ 141962306a36Sopenharmony_ci adapter->adapter_bus_type = 142062306a36Sopenharmony_ci blogic_adater_bus_types[adapter->model[3] - '4']; 142162306a36Sopenharmony_ci if (adapter->irq_ch == 0) { 142262306a36Sopenharmony_ci if (config.irq_ch9) 142362306a36Sopenharmony_ci adapter->irq_ch = 9; 142462306a36Sopenharmony_ci else if (config.irq_ch10) 142562306a36Sopenharmony_ci adapter->irq_ch = 10; 142662306a36Sopenharmony_ci else if (config.irq_ch11) 142762306a36Sopenharmony_ci adapter->irq_ch = 11; 142862306a36Sopenharmony_ci else if (config.irq_ch12) 142962306a36Sopenharmony_ci adapter->irq_ch = 12; 143062306a36Sopenharmony_ci else if (config.irq_ch14) 143162306a36Sopenharmony_ci adapter->irq_ch = 14; 143262306a36Sopenharmony_ci else if (config.irq_ch15) 143362306a36Sopenharmony_ci adapter->irq_ch = 15; 143462306a36Sopenharmony_ci } 143562306a36Sopenharmony_ci /* 143662306a36Sopenharmony_ci Determine whether Extended Translation is enabled and save it in 143762306a36Sopenharmony_ci the Host Adapter structure. 143862306a36Sopenharmony_ci */ 143962306a36Sopenharmony_ci georeg.all = blogic_rdgeom(adapter); 144062306a36Sopenharmony_ci adapter->ext_trans_enable = georeg.gr.ext_trans_enable; 144162306a36Sopenharmony_ci /* 144262306a36Sopenharmony_ci Save the Scatter Gather Limits, Level Sensitive Interrupt flag, Wide 144362306a36Sopenharmony_ci SCSI flag, Differential SCSI flag, SCAM Supported flag, and 144462306a36Sopenharmony_ci Ultra SCSI flag in the Host Adapter structure. 144562306a36Sopenharmony_ci */ 144662306a36Sopenharmony_ci adapter->adapter_sglimit = ext_setupinfo.sg_limit; 144762306a36Sopenharmony_ci adapter->drvr_sglimit = adapter->adapter_sglimit; 144862306a36Sopenharmony_ci if (adapter->adapter_sglimit > BLOGIC_SG_LIMIT) 144962306a36Sopenharmony_ci adapter->drvr_sglimit = BLOGIC_SG_LIMIT; 145062306a36Sopenharmony_ci if (ext_setupinfo.misc.level_int) 145162306a36Sopenharmony_ci adapter->level_int = true; 145262306a36Sopenharmony_ci adapter->wide = ext_setupinfo.wide; 145362306a36Sopenharmony_ci adapter->differential = ext_setupinfo.differential; 145462306a36Sopenharmony_ci adapter->scam = ext_setupinfo.scam; 145562306a36Sopenharmony_ci adapter->ultra = ext_setupinfo.ultra; 145662306a36Sopenharmony_ci /* 145762306a36Sopenharmony_ci Determine whether Extended LUN Format CCBs are supported and save the 145862306a36Sopenharmony_ci information in the Host Adapter structure. 145962306a36Sopenharmony_ci */ 146062306a36Sopenharmony_ci if (adapter->fw_ver[0] == '5' || (adapter->fw_ver[0] == '4' && 146162306a36Sopenharmony_ci adapter->wide)) 146262306a36Sopenharmony_ci adapter->ext_lun = true; 146362306a36Sopenharmony_ci /* 146462306a36Sopenharmony_ci Issue the Inquire PCI Host Adapter Information command to read the 146562306a36Sopenharmony_ci Termination Information from "W" series MultiMaster Host Adapters. 146662306a36Sopenharmony_ci */ 146762306a36Sopenharmony_ci if (adapter->fw_ver[0] == '5') { 146862306a36Sopenharmony_ci if (blogic_cmd(adapter, BLOGIC_INQ_PCI_INFO, NULL, 0, 146962306a36Sopenharmony_ci &adapter_info, 147062306a36Sopenharmony_ci sizeof(adapter_info)) != sizeof(adapter_info)) 147162306a36Sopenharmony_ci return blogic_failure(adapter, 147262306a36Sopenharmony_ci "INQUIRE PCI HOST ADAPTER INFORMATION"); 147362306a36Sopenharmony_ci /* 147462306a36Sopenharmony_ci Save the Termination Information in the Host Adapter 147562306a36Sopenharmony_ci structure. 147662306a36Sopenharmony_ci */ 147762306a36Sopenharmony_ci if (adapter_info.genericinfo_valid) { 147862306a36Sopenharmony_ci adapter->terminfo_valid = true; 147962306a36Sopenharmony_ci adapter->low_term = adapter_info.low_term; 148062306a36Sopenharmony_ci adapter->high_term = adapter_info.high_term; 148162306a36Sopenharmony_ci } 148262306a36Sopenharmony_ci } 148362306a36Sopenharmony_ci /* 148462306a36Sopenharmony_ci Issue the Fetch Host Adapter Local RAM command to read the 148562306a36Sopenharmony_ci AutoSCSI data from "W" and "C" series MultiMaster Host Adapters. 148662306a36Sopenharmony_ci */ 148762306a36Sopenharmony_ci if (adapter->fw_ver[0] >= '4') { 148862306a36Sopenharmony_ci fetch_localram.offset = BLOGIC_AUTOSCSI_BASE; 148962306a36Sopenharmony_ci fetch_localram.count = sizeof(autoscsi); 149062306a36Sopenharmony_ci if (blogic_cmd(adapter, BLOGIC_FETCH_LOCALRAM, &fetch_localram, 149162306a36Sopenharmony_ci sizeof(fetch_localram), &autoscsi, 149262306a36Sopenharmony_ci sizeof(autoscsi)) != sizeof(autoscsi)) 149362306a36Sopenharmony_ci return blogic_failure(adapter, 149462306a36Sopenharmony_ci "FETCH HOST ADAPTER LOCAL RAM"); 149562306a36Sopenharmony_ci /* 149662306a36Sopenharmony_ci Save the Parity Checking Enabled, Bus Reset Enabled, 149762306a36Sopenharmony_ci and Termination Information in the Host Adapter structure. 149862306a36Sopenharmony_ci */ 149962306a36Sopenharmony_ci adapter->parity = autoscsi.parity; 150062306a36Sopenharmony_ci adapter->reset_enabled = autoscsi.reset_enabled; 150162306a36Sopenharmony_ci if (adapter->fw_ver[0] == '4') { 150262306a36Sopenharmony_ci adapter->terminfo_valid = true; 150362306a36Sopenharmony_ci adapter->low_term = autoscsi.low_term; 150462306a36Sopenharmony_ci adapter->high_term = autoscsi.high_term; 150562306a36Sopenharmony_ci } 150662306a36Sopenharmony_ci /* 150762306a36Sopenharmony_ci Save the Wide Permitted, Fast Permitted, Synchronous 150862306a36Sopenharmony_ci Permitted, Disconnect Permitted, Ultra Permitted, and 150962306a36Sopenharmony_ci SCAM Information in the Host Adapter structure. 151062306a36Sopenharmony_ci */ 151162306a36Sopenharmony_ci adapter->wide_ok = autoscsi.wide_ok; 151262306a36Sopenharmony_ci adapter->fast_ok = autoscsi.fast_ok; 151362306a36Sopenharmony_ci adapter->sync_ok = autoscsi.sync_ok; 151462306a36Sopenharmony_ci adapter->discon_ok = autoscsi.discon_ok; 151562306a36Sopenharmony_ci if (adapter->ultra) 151662306a36Sopenharmony_ci adapter->ultra_ok = autoscsi.ultra_ok; 151762306a36Sopenharmony_ci if (adapter->scam) { 151862306a36Sopenharmony_ci adapter->scam_enabled = autoscsi.scam_enabled; 151962306a36Sopenharmony_ci adapter->scam_lev2 = autoscsi.scam_lev2; 152062306a36Sopenharmony_ci } 152162306a36Sopenharmony_ci } 152262306a36Sopenharmony_ci /* 152362306a36Sopenharmony_ci Initialize fields in the Host Adapter structure for "S" and "A" 152462306a36Sopenharmony_ci series MultiMaster Host Adapters. 152562306a36Sopenharmony_ci */ 152662306a36Sopenharmony_ci if (adapter->fw_ver[0] < '4') { 152762306a36Sopenharmony_ci if (setupinfo.sync) { 152862306a36Sopenharmony_ci adapter->sync_ok = 0xFF; 152962306a36Sopenharmony_ci if (adapter->adapter_bus_type == BLOGIC_EISA_BUS) { 153062306a36Sopenharmony_ci if (ext_setupinfo.misc.fast_on_eisa) 153162306a36Sopenharmony_ci adapter->fast_ok = 0xFF; 153262306a36Sopenharmony_ci if (strcmp(adapter->model, "BT-757") == 0) 153362306a36Sopenharmony_ci adapter->wide_ok = 0xFF; 153462306a36Sopenharmony_ci } 153562306a36Sopenharmony_ci } 153662306a36Sopenharmony_ci adapter->discon_ok = 0xFF; 153762306a36Sopenharmony_ci adapter->parity = setupinfo.parity; 153862306a36Sopenharmony_ci adapter->reset_enabled = true; 153962306a36Sopenharmony_ci } 154062306a36Sopenharmony_ci /* 154162306a36Sopenharmony_ci Determine the maximum number of Target IDs and Logical Units 154262306a36Sopenharmony_ci supported by this driver for Wide and Narrow Host Adapters. 154362306a36Sopenharmony_ci */ 154462306a36Sopenharmony_ci adapter->maxdev = (adapter->wide ? 16 : 8); 154562306a36Sopenharmony_ci adapter->maxlun = (adapter->ext_lun ? 32 : 8); 154662306a36Sopenharmony_ci /* 154762306a36Sopenharmony_ci Select appropriate values for the Mailbox Count, Driver Queue Depth, 154862306a36Sopenharmony_ci Initial CCBs, and Incremental CCBs variables based on whether 154962306a36Sopenharmony_ci or not Strict Round Robin Mode is supported. If Strict Round 155062306a36Sopenharmony_ci Robin Mode is supported, then there is no performance degradation 155162306a36Sopenharmony_ci in using the maximum possible number of Outgoing and Incoming 155262306a36Sopenharmony_ci Mailboxes and allowing the Tagged and Untagged Queue Depths to 155362306a36Sopenharmony_ci determine the actual utilization. If Strict Round Robin Mode is 155462306a36Sopenharmony_ci not supported, then the Host Adapter must scan all the Outgoing 155562306a36Sopenharmony_ci Mailboxes whenever an Outgoing Mailbox entry is made, which can 155662306a36Sopenharmony_ci cause a substantial performance penalty. The host adapters 155762306a36Sopenharmony_ci actually have room to store the following number of CCBs 155862306a36Sopenharmony_ci internally; that is, they can internally queue and manage this 155962306a36Sopenharmony_ci many active commands on the SCSI bus simultaneously. Performance 156062306a36Sopenharmony_ci measurements demonstrate that the Driver Queue Depth should be 156162306a36Sopenharmony_ci set to the Mailbox Count, rather than the Host Adapter Queue 156262306a36Sopenharmony_ci Depth (internal CCB capacity), as it is more efficient to have the 156362306a36Sopenharmony_ci queued commands waiting in Outgoing Mailboxes if necessary than 156462306a36Sopenharmony_ci to block the process in the higher levels of the SCSI Subsystem. 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci 192 BT-948/958/958D 156762306a36Sopenharmony_ci 100 BT-946C/956C/956CD/747C/757C/757CD/445C 156862306a36Sopenharmony_ci 50 BT-545C/540CF 156962306a36Sopenharmony_ci 30 BT-747S/747D/757S/757D/445S/545S/542D/542B/742A 157062306a36Sopenharmony_ci */ 157162306a36Sopenharmony_ci if (adapter->fw_ver[0] == '5') 157262306a36Sopenharmony_ci adapter->adapter_qdepth = 192; 157362306a36Sopenharmony_ci else if (adapter->fw_ver[0] == '4') 157462306a36Sopenharmony_ci adapter->adapter_qdepth = 100; 157562306a36Sopenharmony_ci else 157662306a36Sopenharmony_ci adapter->adapter_qdepth = 30; 157762306a36Sopenharmony_ci if (strcmp(adapter->fw_ver, "3.31") >= 0) { 157862306a36Sopenharmony_ci adapter->strict_rr = true; 157962306a36Sopenharmony_ci adapter->mbox_count = BLOGIC_MAX_MAILBOX; 158062306a36Sopenharmony_ci } else { 158162306a36Sopenharmony_ci adapter->strict_rr = false; 158262306a36Sopenharmony_ci adapter->mbox_count = 32; 158362306a36Sopenharmony_ci } 158462306a36Sopenharmony_ci adapter->drvr_qdepth = adapter->mbox_count; 158562306a36Sopenharmony_ci adapter->initccbs = 4 * BLOGIC_CCB_GRP_ALLOCSIZE; 158662306a36Sopenharmony_ci adapter->inc_ccbs = BLOGIC_CCB_GRP_ALLOCSIZE; 158762306a36Sopenharmony_ci /* 158862306a36Sopenharmony_ci Tagged Queuing support is available and operates properly on 158962306a36Sopenharmony_ci all "W" series MultiMaster Host Adapters, on "C" series 159062306a36Sopenharmony_ci MultiMaster Host Adapters with firmware version 4.22 and above, 159162306a36Sopenharmony_ci and on "S" series MultiMaster Host Adapters with firmware version 159262306a36Sopenharmony_ci 3.35 and above. 159362306a36Sopenharmony_ci */ 159462306a36Sopenharmony_ci adapter->tagq_ok = 0; 159562306a36Sopenharmony_ci switch (adapter->fw_ver[0]) { 159662306a36Sopenharmony_ci case '5': 159762306a36Sopenharmony_ci adapter->tagq_ok = 0xFFFF; 159862306a36Sopenharmony_ci break; 159962306a36Sopenharmony_ci case '4': 160062306a36Sopenharmony_ci if (strcmp(adapter->fw_ver, "4.22") >= 0) 160162306a36Sopenharmony_ci adapter->tagq_ok = 0xFFFF; 160262306a36Sopenharmony_ci break; 160362306a36Sopenharmony_ci case '3': 160462306a36Sopenharmony_ci if (strcmp(adapter->fw_ver, "3.35") >= 0) 160562306a36Sopenharmony_ci adapter->tagq_ok = 0xFFFF; 160662306a36Sopenharmony_ci break; 160762306a36Sopenharmony_ci } 160862306a36Sopenharmony_ci /* 160962306a36Sopenharmony_ci Determine the Host Adapter BIOS Address if the BIOS is enabled and 161062306a36Sopenharmony_ci save it in the Host Adapter structure. The BIOS is disabled if the 161162306a36Sopenharmony_ci bios_addr is 0. 161262306a36Sopenharmony_ci */ 161362306a36Sopenharmony_ci adapter->bios_addr = ext_setupinfo.bios_addr << 12; 161462306a36Sopenharmony_ci /* 161562306a36Sopenharmony_ci BusLogic BT-445S Host Adapters prior to board revision E have a 161662306a36Sopenharmony_ci hardware bug whereby when the BIOS is enabled, transfers to/from 161762306a36Sopenharmony_ci the same address range the BIOS occupies modulo 16MB are handled 161862306a36Sopenharmony_ci incorrectly. Only properly functioning BT-445S Host Adapters 161962306a36Sopenharmony_ci have firmware version 3.37. 162062306a36Sopenharmony_ci */ 162162306a36Sopenharmony_ci if (adapter->bios_addr > 0 && 162262306a36Sopenharmony_ci strcmp(adapter->model, "BT-445S") == 0 && 162362306a36Sopenharmony_ci strcmp(adapter->fw_ver, "3.37") < 0) 162462306a36Sopenharmony_ci return blogic_failure(adapter, "Too old firmware"); 162562306a36Sopenharmony_ci /* 162662306a36Sopenharmony_ci Initialize parameters common to MultiMaster and FlashPoint 162762306a36Sopenharmony_ci Host Adapters. 162862306a36Sopenharmony_ci */ 162962306a36Sopenharmony_cicommon: 163062306a36Sopenharmony_ci /* 163162306a36Sopenharmony_ci Initialize the Host Adapter Full Model Name from the Model Name. 163262306a36Sopenharmony_ci */ 163362306a36Sopenharmony_ci strcpy(adapter->full_model, "BusLogic "); 163462306a36Sopenharmony_ci strcat(adapter->full_model, adapter->model); 163562306a36Sopenharmony_ci /* 163662306a36Sopenharmony_ci Select an appropriate value for the Tagged Queue Depth either from a 163762306a36Sopenharmony_ci BusLogic Driver Options specification, or based on whether this Host 163862306a36Sopenharmony_ci Adapter requires that ISA Bounce Buffers be used. The Tagged Queue 163962306a36Sopenharmony_ci Depth is left at 0 for automatic determination in 164062306a36Sopenharmony_ci BusLogic_SelectQueueDepths. Initialize the Untagged Queue Depth. 164162306a36Sopenharmony_ci */ 164262306a36Sopenharmony_ci for (tgt_id = 0; tgt_id < BLOGIC_MAXDEV; tgt_id++) { 164362306a36Sopenharmony_ci unsigned char qdepth = 0; 164462306a36Sopenharmony_ci if (adapter->drvr_opts != NULL && 164562306a36Sopenharmony_ci adapter->drvr_opts->qdepth[tgt_id] > 0) 164662306a36Sopenharmony_ci qdepth = adapter->drvr_opts->qdepth[tgt_id]; 164762306a36Sopenharmony_ci adapter->qdepth[tgt_id] = qdepth; 164862306a36Sopenharmony_ci } 164962306a36Sopenharmony_ci adapter->untag_qdepth = BLOGIC_UNTAG_DEPTH; 165062306a36Sopenharmony_ci if (adapter->drvr_opts != NULL) 165162306a36Sopenharmony_ci adapter->common_qdepth = adapter->drvr_opts->common_qdepth; 165262306a36Sopenharmony_ci if (adapter->common_qdepth > 0 && 165362306a36Sopenharmony_ci adapter->common_qdepth < adapter->untag_qdepth) 165462306a36Sopenharmony_ci adapter->untag_qdepth = adapter->common_qdepth; 165562306a36Sopenharmony_ci /* 165662306a36Sopenharmony_ci Tagged Queuing is only allowed if Disconnect/Reconnect is permitted. 165762306a36Sopenharmony_ci Therefore, mask the Tagged Queuing Permitted Default bits with the 165862306a36Sopenharmony_ci Disconnect/Reconnect Permitted bits. 165962306a36Sopenharmony_ci */ 166062306a36Sopenharmony_ci adapter->tagq_ok &= adapter->discon_ok; 166162306a36Sopenharmony_ci /* 166262306a36Sopenharmony_ci Combine the default Tagged Queuing Permitted bits with any 166362306a36Sopenharmony_ci BusLogic Driver Options Tagged Queuing specification. 166462306a36Sopenharmony_ci */ 166562306a36Sopenharmony_ci if (adapter->drvr_opts != NULL) 166662306a36Sopenharmony_ci adapter->tagq_ok = (adapter->drvr_opts->tagq_ok & 166762306a36Sopenharmony_ci adapter->drvr_opts->tagq_ok_mask) | 166862306a36Sopenharmony_ci (adapter->tagq_ok & ~adapter->drvr_opts->tagq_ok_mask); 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci /* 167162306a36Sopenharmony_ci Select an appropriate value for Bus Settle Time either from a 167262306a36Sopenharmony_ci BusLogic Driver Options specification, or from 167362306a36Sopenharmony_ci BLOGIC_BUS_SETTLE_TIME. 167462306a36Sopenharmony_ci */ 167562306a36Sopenharmony_ci if (adapter->drvr_opts != NULL && 167662306a36Sopenharmony_ci adapter->drvr_opts->bus_settle_time > 0) 167762306a36Sopenharmony_ci adapter->bus_settle_time = adapter->drvr_opts->bus_settle_time; 167862306a36Sopenharmony_ci else 167962306a36Sopenharmony_ci adapter->bus_settle_time = BLOGIC_BUS_SETTLE_TIME; 168062306a36Sopenharmony_ci /* 168162306a36Sopenharmony_ci Indicate reading the Host Adapter Configuration completed 168262306a36Sopenharmony_ci successfully. 168362306a36Sopenharmony_ci */ 168462306a36Sopenharmony_ci return true; 168562306a36Sopenharmony_ci} 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ci/* 168962306a36Sopenharmony_ci blogic_reportconfig reports the configuration of Host Adapter. 169062306a36Sopenharmony_ci*/ 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_cistatic bool __init blogic_reportconfig(struct blogic_adapter *adapter) 169362306a36Sopenharmony_ci{ 169462306a36Sopenharmony_ci unsigned short alltgt_mask = (1 << adapter->maxdev) - 1; 169562306a36Sopenharmony_ci unsigned short sync_ok, fast_ok; 169662306a36Sopenharmony_ci unsigned short ultra_ok, wide_ok; 169762306a36Sopenharmony_ci unsigned short discon_ok, tagq_ok; 169862306a36Sopenharmony_ci bool common_syncneg, common_tagq_depth; 169962306a36Sopenharmony_ci char syncstr[BLOGIC_MAXDEV + 1]; 170062306a36Sopenharmony_ci char widestr[BLOGIC_MAXDEV + 1]; 170162306a36Sopenharmony_ci char discon_str[BLOGIC_MAXDEV + 1]; 170262306a36Sopenharmony_ci char tagq_str[BLOGIC_MAXDEV + 1]; 170362306a36Sopenharmony_ci char *syncmsg = syncstr; 170462306a36Sopenharmony_ci char *widemsg = widestr; 170562306a36Sopenharmony_ci char *discon_msg = discon_str; 170662306a36Sopenharmony_ci char *tagq_msg = tagq_str; 170762306a36Sopenharmony_ci int tgt_id; 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ci blogic_info("Configuring BusLogic Model %s %s%s%s%s SCSI Host Adapter\n", adapter, adapter->model, blogic_adapter_busnames[adapter->adapter_bus_type], (adapter->wide ? " Wide" : ""), (adapter->differential ? " Differential" : ""), (adapter->ultra ? " Ultra" : "")); 171062306a36Sopenharmony_ci blogic_info(" Firmware Version: %s, I/O Address: 0x%lX, IRQ Channel: %d/%s\n", adapter, adapter->fw_ver, adapter->io_addr, adapter->irq_ch, (adapter->level_int ? "Level" : "Edge")); 171162306a36Sopenharmony_ci if (adapter->adapter_bus_type != BLOGIC_PCI_BUS) { 171262306a36Sopenharmony_ci blogic_info(" DMA Channel: None, ", adapter); 171362306a36Sopenharmony_ci if (adapter->bios_addr > 0) 171462306a36Sopenharmony_ci blogic_info("BIOS Address: 0x%X, ", adapter, 171562306a36Sopenharmony_ci adapter->bios_addr); 171662306a36Sopenharmony_ci else 171762306a36Sopenharmony_ci blogic_info("BIOS Address: None, ", adapter); 171862306a36Sopenharmony_ci } else { 171962306a36Sopenharmony_ci blogic_info(" PCI Bus: %d, Device: %d, Address: ", adapter, 172062306a36Sopenharmony_ci adapter->bus, adapter->dev); 172162306a36Sopenharmony_ci if (adapter->pci_addr > 0) 172262306a36Sopenharmony_ci blogic_info("0x%lX, ", adapter, adapter->pci_addr); 172362306a36Sopenharmony_ci else 172462306a36Sopenharmony_ci blogic_info("Unassigned, ", adapter); 172562306a36Sopenharmony_ci } 172662306a36Sopenharmony_ci blogic_info("Host Adapter SCSI ID: %d\n", adapter, adapter->scsi_id); 172762306a36Sopenharmony_ci blogic_info(" Parity Checking: %s, Extended Translation: %s\n", 172862306a36Sopenharmony_ci adapter, (adapter->parity ? "Enabled" : "Disabled"), 172962306a36Sopenharmony_ci (adapter->ext_trans_enable ? "Enabled" : "Disabled")); 173062306a36Sopenharmony_ci alltgt_mask &= ~(1 << adapter->scsi_id); 173162306a36Sopenharmony_ci sync_ok = adapter->sync_ok & alltgt_mask; 173262306a36Sopenharmony_ci fast_ok = adapter->fast_ok & alltgt_mask; 173362306a36Sopenharmony_ci ultra_ok = adapter->ultra_ok & alltgt_mask; 173462306a36Sopenharmony_ci if ((blogic_multimaster_type(adapter) && 173562306a36Sopenharmony_ci (adapter->fw_ver[0] >= '4' || 173662306a36Sopenharmony_ci adapter->adapter_bus_type == BLOGIC_EISA_BUS)) || 173762306a36Sopenharmony_ci blogic_flashpoint_type(adapter)) { 173862306a36Sopenharmony_ci common_syncneg = false; 173962306a36Sopenharmony_ci if (sync_ok == 0) { 174062306a36Sopenharmony_ci syncmsg = "Disabled"; 174162306a36Sopenharmony_ci common_syncneg = true; 174262306a36Sopenharmony_ci } else if (sync_ok == alltgt_mask) { 174362306a36Sopenharmony_ci if (fast_ok == 0) { 174462306a36Sopenharmony_ci syncmsg = "Slow"; 174562306a36Sopenharmony_ci common_syncneg = true; 174662306a36Sopenharmony_ci } else if (fast_ok == alltgt_mask) { 174762306a36Sopenharmony_ci if (ultra_ok == 0) { 174862306a36Sopenharmony_ci syncmsg = "Fast"; 174962306a36Sopenharmony_ci common_syncneg = true; 175062306a36Sopenharmony_ci } else if (ultra_ok == alltgt_mask) { 175162306a36Sopenharmony_ci syncmsg = "Ultra"; 175262306a36Sopenharmony_ci common_syncneg = true; 175362306a36Sopenharmony_ci } 175462306a36Sopenharmony_ci } 175562306a36Sopenharmony_ci } 175662306a36Sopenharmony_ci if (!common_syncneg) { 175762306a36Sopenharmony_ci for (tgt_id = 0; tgt_id < adapter->maxdev; tgt_id++) 175862306a36Sopenharmony_ci syncstr[tgt_id] = ((!(sync_ok & (1 << tgt_id))) ? 'N' : (!(fast_ok & (1 << tgt_id)) ? 'S' : (!(ultra_ok & (1 << tgt_id)) ? 'F' : 'U'))); 175962306a36Sopenharmony_ci syncstr[adapter->scsi_id] = '#'; 176062306a36Sopenharmony_ci syncstr[adapter->maxdev] = '\0'; 176162306a36Sopenharmony_ci } 176262306a36Sopenharmony_ci } else 176362306a36Sopenharmony_ci syncmsg = (sync_ok == 0 ? "Disabled" : "Enabled"); 176462306a36Sopenharmony_ci wide_ok = adapter->wide_ok & alltgt_mask; 176562306a36Sopenharmony_ci if (wide_ok == 0) 176662306a36Sopenharmony_ci widemsg = "Disabled"; 176762306a36Sopenharmony_ci else if (wide_ok == alltgt_mask) 176862306a36Sopenharmony_ci widemsg = "Enabled"; 176962306a36Sopenharmony_ci else { 177062306a36Sopenharmony_ci for (tgt_id = 0; tgt_id < adapter->maxdev; tgt_id++) 177162306a36Sopenharmony_ci widestr[tgt_id] = ((wide_ok & (1 << tgt_id)) ? 'Y' : 'N'); 177262306a36Sopenharmony_ci widestr[adapter->scsi_id] = '#'; 177362306a36Sopenharmony_ci widestr[adapter->maxdev] = '\0'; 177462306a36Sopenharmony_ci } 177562306a36Sopenharmony_ci discon_ok = adapter->discon_ok & alltgt_mask; 177662306a36Sopenharmony_ci if (discon_ok == 0) 177762306a36Sopenharmony_ci discon_msg = "Disabled"; 177862306a36Sopenharmony_ci else if (discon_ok == alltgt_mask) 177962306a36Sopenharmony_ci discon_msg = "Enabled"; 178062306a36Sopenharmony_ci else { 178162306a36Sopenharmony_ci for (tgt_id = 0; tgt_id < adapter->maxdev; tgt_id++) 178262306a36Sopenharmony_ci discon_str[tgt_id] = ((discon_ok & (1 << tgt_id)) ? 'Y' : 'N'); 178362306a36Sopenharmony_ci discon_str[adapter->scsi_id] = '#'; 178462306a36Sopenharmony_ci discon_str[adapter->maxdev] = '\0'; 178562306a36Sopenharmony_ci } 178662306a36Sopenharmony_ci tagq_ok = adapter->tagq_ok & alltgt_mask; 178762306a36Sopenharmony_ci if (tagq_ok == 0) 178862306a36Sopenharmony_ci tagq_msg = "Disabled"; 178962306a36Sopenharmony_ci else if (tagq_ok == alltgt_mask) 179062306a36Sopenharmony_ci tagq_msg = "Enabled"; 179162306a36Sopenharmony_ci else { 179262306a36Sopenharmony_ci for (tgt_id = 0; tgt_id < adapter->maxdev; tgt_id++) 179362306a36Sopenharmony_ci tagq_str[tgt_id] = ((tagq_ok & (1 << tgt_id)) ? 'Y' : 'N'); 179462306a36Sopenharmony_ci tagq_str[adapter->scsi_id] = '#'; 179562306a36Sopenharmony_ci tagq_str[adapter->maxdev] = '\0'; 179662306a36Sopenharmony_ci } 179762306a36Sopenharmony_ci blogic_info(" Synchronous Negotiation: %s, Wide Negotiation: %s\n", 179862306a36Sopenharmony_ci adapter, syncmsg, widemsg); 179962306a36Sopenharmony_ci blogic_info(" Disconnect/Reconnect: %s, Tagged Queuing: %s\n", adapter, 180062306a36Sopenharmony_ci discon_msg, tagq_msg); 180162306a36Sopenharmony_ci if (blogic_multimaster_type(adapter)) { 180262306a36Sopenharmony_ci blogic_info(" Scatter/Gather Limit: %d of %d segments, Mailboxes: %d\n", adapter, adapter->drvr_sglimit, adapter->adapter_sglimit, adapter->mbox_count); 180362306a36Sopenharmony_ci blogic_info(" Driver Queue Depth: %d, Host Adapter Queue Depth: %d\n", adapter, adapter->drvr_qdepth, adapter->adapter_qdepth); 180462306a36Sopenharmony_ci } else 180562306a36Sopenharmony_ci blogic_info(" Driver Queue Depth: %d, Scatter/Gather Limit: %d segments\n", adapter, adapter->drvr_qdepth, adapter->drvr_sglimit); 180662306a36Sopenharmony_ci blogic_info(" Tagged Queue Depth: ", adapter); 180762306a36Sopenharmony_ci common_tagq_depth = true; 180862306a36Sopenharmony_ci for (tgt_id = 1; tgt_id < adapter->maxdev; tgt_id++) 180962306a36Sopenharmony_ci if (adapter->qdepth[tgt_id] != adapter->qdepth[0]) { 181062306a36Sopenharmony_ci common_tagq_depth = false; 181162306a36Sopenharmony_ci break; 181262306a36Sopenharmony_ci } 181362306a36Sopenharmony_ci if (common_tagq_depth) { 181462306a36Sopenharmony_ci if (adapter->qdepth[0] > 0) 181562306a36Sopenharmony_ci blogic_info("%d", adapter, adapter->qdepth[0]); 181662306a36Sopenharmony_ci else 181762306a36Sopenharmony_ci blogic_info("Automatic", adapter); 181862306a36Sopenharmony_ci } else 181962306a36Sopenharmony_ci blogic_info("Individual", adapter); 182062306a36Sopenharmony_ci blogic_info(", Untagged Queue Depth: %d\n", adapter, 182162306a36Sopenharmony_ci adapter->untag_qdepth); 182262306a36Sopenharmony_ci if (adapter->terminfo_valid) { 182362306a36Sopenharmony_ci if (adapter->wide) 182462306a36Sopenharmony_ci blogic_info(" SCSI Bus Termination: %s", adapter, 182562306a36Sopenharmony_ci (adapter->low_term ? (adapter->high_term ? "Both Enabled" : "Low Enabled") : (adapter->high_term ? "High Enabled" : "Both Disabled"))); 182662306a36Sopenharmony_ci else 182762306a36Sopenharmony_ci blogic_info(" SCSI Bus Termination: %s", adapter, 182862306a36Sopenharmony_ci (adapter->low_term ? "Enabled" : "Disabled")); 182962306a36Sopenharmony_ci if (adapter->scam) 183062306a36Sopenharmony_ci blogic_info(", SCAM: %s", adapter, 183162306a36Sopenharmony_ci (adapter->scam_enabled ? (adapter->scam_lev2 ? "Enabled, Level 2" : "Enabled, Level 1") : "Disabled")); 183262306a36Sopenharmony_ci blogic_info("\n", adapter); 183362306a36Sopenharmony_ci } 183462306a36Sopenharmony_ci /* 183562306a36Sopenharmony_ci Indicate reporting the Host Adapter configuration completed 183662306a36Sopenharmony_ci successfully. 183762306a36Sopenharmony_ci */ 183862306a36Sopenharmony_ci return true; 183962306a36Sopenharmony_ci} 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ci 184262306a36Sopenharmony_ci/* 184362306a36Sopenharmony_ci blogic_getres acquires the system resources necessary to use 184462306a36Sopenharmony_ci Host Adapter. 184562306a36Sopenharmony_ci*/ 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_cistatic bool __init blogic_getres(struct blogic_adapter *adapter) 184862306a36Sopenharmony_ci{ 184962306a36Sopenharmony_ci if (adapter->irq_ch == 0) { 185062306a36Sopenharmony_ci blogic_err("NO LEGAL INTERRUPT CHANNEL ASSIGNED - DETACHING\n", 185162306a36Sopenharmony_ci adapter); 185262306a36Sopenharmony_ci return false; 185362306a36Sopenharmony_ci } 185462306a36Sopenharmony_ci /* 185562306a36Sopenharmony_ci Acquire shared access to the IRQ Channel. 185662306a36Sopenharmony_ci */ 185762306a36Sopenharmony_ci if (request_irq(adapter->irq_ch, blogic_inthandler, IRQF_SHARED, 185862306a36Sopenharmony_ci adapter->full_model, adapter) < 0) { 185962306a36Sopenharmony_ci blogic_err("UNABLE TO ACQUIRE IRQ CHANNEL %d - DETACHING\n", 186062306a36Sopenharmony_ci adapter, adapter->irq_ch); 186162306a36Sopenharmony_ci return false; 186262306a36Sopenharmony_ci } 186362306a36Sopenharmony_ci adapter->irq_acquired = true; 186462306a36Sopenharmony_ci /* 186562306a36Sopenharmony_ci Indicate the System Resource Acquisition completed successfully, 186662306a36Sopenharmony_ci */ 186762306a36Sopenharmony_ci return true; 186862306a36Sopenharmony_ci} 186962306a36Sopenharmony_ci 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_ci/* 187262306a36Sopenharmony_ci blogic_relres releases any system resources previously acquired 187362306a36Sopenharmony_ci by blogic_getres. 187462306a36Sopenharmony_ci*/ 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_cistatic void blogic_relres(struct blogic_adapter *adapter) 187762306a36Sopenharmony_ci{ 187862306a36Sopenharmony_ci /* 187962306a36Sopenharmony_ci Release shared access to the IRQ Channel. 188062306a36Sopenharmony_ci */ 188162306a36Sopenharmony_ci if (adapter->irq_acquired) 188262306a36Sopenharmony_ci free_irq(adapter->irq_ch, adapter); 188362306a36Sopenharmony_ci /* 188462306a36Sopenharmony_ci Release any allocated memory structs not released elsewhere 188562306a36Sopenharmony_ci */ 188662306a36Sopenharmony_ci if (adapter->mbox_space) 188762306a36Sopenharmony_ci dma_free_coherent(&adapter->pci_device->dev, adapter->mbox_sz, 188862306a36Sopenharmony_ci adapter->mbox_space, adapter->mbox_space_handle); 188962306a36Sopenharmony_ci pci_dev_put(adapter->pci_device); 189062306a36Sopenharmony_ci adapter->mbox_space = NULL; 189162306a36Sopenharmony_ci adapter->mbox_space_handle = 0; 189262306a36Sopenharmony_ci adapter->mbox_sz = 0; 189362306a36Sopenharmony_ci} 189462306a36Sopenharmony_ci 189562306a36Sopenharmony_ci 189662306a36Sopenharmony_ci/* 189762306a36Sopenharmony_ci blogic_initadapter initializes Host Adapter. This is the only 189862306a36Sopenharmony_ci function called during SCSI Host Adapter detection which modifies the state 189962306a36Sopenharmony_ci of the Host Adapter from its initial power on or hard reset state. 190062306a36Sopenharmony_ci*/ 190162306a36Sopenharmony_ci 190262306a36Sopenharmony_cistatic bool blogic_initadapter(struct blogic_adapter *adapter) 190362306a36Sopenharmony_ci{ 190462306a36Sopenharmony_ci struct blogic_extmbox_req extmbox_req; 190562306a36Sopenharmony_ci enum blogic_rr_req rr_req; 190662306a36Sopenharmony_ci enum blogic_setccb_fmt setccb_fmt; 190762306a36Sopenharmony_ci int tgt_id; 190862306a36Sopenharmony_ci 190962306a36Sopenharmony_ci /* 191062306a36Sopenharmony_ci Initialize the pointers to the first and last CCBs that are 191162306a36Sopenharmony_ci queued for completion processing. 191262306a36Sopenharmony_ci */ 191362306a36Sopenharmony_ci adapter->firstccb = NULL; 191462306a36Sopenharmony_ci adapter->lastccb = NULL; 191562306a36Sopenharmony_ci 191662306a36Sopenharmony_ci /* 191762306a36Sopenharmony_ci Initialize the Bus Device Reset Pending CCB, Tagged Queuing Active, 191862306a36Sopenharmony_ci Command Successful Flag, Active Commands, and Commands Since Reset 191962306a36Sopenharmony_ci for each Target Device. 192062306a36Sopenharmony_ci */ 192162306a36Sopenharmony_ci for (tgt_id = 0; tgt_id < adapter->maxdev; tgt_id++) { 192262306a36Sopenharmony_ci adapter->bdr_pend[tgt_id] = NULL; 192362306a36Sopenharmony_ci adapter->tgt_flags[tgt_id].tagq_active = false; 192462306a36Sopenharmony_ci adapter->tgt_flags[tgt_id].cmd_good = false; 192562306a36Sopenharmony_ci adapter->active_cmds[tgt_id] = 0; 192662306a36Sopenharmony_ci adapter->cmds_since_rst[tgt_id] = 0; 192762306a36Sopenharmony_ci } 192862306a36Sopenharmony_ci 192962306a36Sopenharmony_ci /* 193062306a36Sopenharmony_ci FlashPoint Host Adapters do not use Outgoing and Incoming Mailboxes. 193162306a36Sopenharmony_ci */ 193262306a36Sopenharmony_ci if (blogic_flashpoint_type(adapter)) 193362306a36Sopenharmony_ci goto done; 193462306a36Sopenharmony_ci 193562306a36Sopenharmony_ci /* 193662306a36Sopenharmony_ci Initialize the Outgoing and Incoming Mailbox pointers. 193762306a36Sopenharmony_ci */ 193862306a36Sopenharmony_ci adapter->mbox_sz = adapter->mbox_count * (sizeof(struct blogic_outbox) + sizeof(struct blogic_inbox)); 193962306a36Sopenharmony_ci adapter->mbox_space = dma_alloc_coherent(&adapter->pci_device->dev, 194062306a36Sopenharmony_ci adapter->mbox_sz, &adapter->mbox_space_handle, 194162306a36Sopenharmony_ci GFP_KERNEL); 194262306a36Sopenharmony_ci if (adapter->mbox_space == NULL) 194362306a36Sopenharmony_ci return blogic_failure(adapter, "MAILBOX ALLOCATION"); 194462306a36Sopenharmony_ci adapter->first_outbox = (struct blogic_outbox *) adapter->mbox_space; 194562306a36Sopenharmony_ci adapter->last_outbox = adapter->first_outbox + adapter->mbox_count - 1; 194662306a36Sopenharmony_ci adapter->next_outbox = adapter->first_outbox; 194762306a36Sopenharmony_ci adapter->first_inbox = (struct blogic_inbox *) (adapter->last_outbox + 1); 194862306a36Sopenharmony_ci adapter->last_inbox = adapter->first_inbox + adapter->mbox_count - 1; 194962306a36Sopenharmony_ci adapter->next_inbox = adapter->first_inbox; 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_ci /* 195262306a36Sopenharmony_ci Initialize the Outgoing and Incoming Mailbox structures. 195362306a36Sopenharmony_ci */ 195462306a36Sopenharmony_ci memset(adapter->first_outbox, 0, 195562306a36Sopenharmony_ci adapter->mbox_count * sizeof(struct blogic_outbox)); 195662306a36Sopenharmony_ci memset(adapter->first_inbox, 0, 195762306a36Sopenharmony_ci adapter->mbox_count * sizeof(struct blogic_inbox)); 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci /* 196062306a36Sopenharmony_ci Initialize the Host Adapter's Pointer to the Outgoing/Incoming 196162306a36Sopenharmony_ci Mailboxes. 196262306a36Sopenharmony_ci */ 196362306a36Sopenharmony_ci extmbox_req.mbox_count = adapter->mbox_count; 196462306a36Sopenharmony_ci extmbox_req.base_mbox_addr = (u32) adapter->mbox_space_handle; 196562306a36Sopenharmony_ci if (blogic_cmd(adapter, BLOGIC_INIT_EXT_MBOX, &extmbox_req, 196662306a36Sopenharmony_ci sizeof(extmbox_req), NULL, 0) < 0) 196762306a36Sopenharmony_ci return blogic_failure(adapter, "MAILBOX INITIALIZATION"); 196862306a36Sopenharmony_ci /* 196962306a36Sopenharmony_ci Enable Strict Round Robin Mode if supported by the Host Adapter. In 197062306a36Sopenharmony_ci Strict Round Robin Mode, the Host Adapter only looks at the next 197162306a36Sopenharmony_ci Outgoing Mailbox for each new command, rather than scanning 197262306a36Sopenharmony_ci through all the Outgoing Mailboxes to find any that have new 197362306a36Sopenharmony_ci commands in them. Strict Round Robin Mode is significantly more 197462306a36Sopenharmony_ci efficient. 197562306a36Sopenharmony_ci */ 197662306a36Sopenharmony_ci if (adapter->strict_rr) { 197762306a36Sopenharmony_ci rr_req = BLOGIC_STRICT_RR_MODE; 197862306a36Sopenharmony_ci if (blogic_cmd(adapter, BLOGIC_STRICT_RR, &rr_req, 197962306a36Sopenharmony_ci sizeof(rr_req), NULL, 0) < 0) 198062306a36Sopenharmony_ci return blogic_failure(adapter, 198162306a36Sopenharmony_ci "ENABLE STRICT ROUND ROBIN MODE"); 198262306a36Sopenharmony_ci } 198362306a36Sopenharmony_ci 198462306a36Sopenharmony_ci /* 198562306a36Sopenharmony_ci For Host Adapters that support Extended LUN Format CCBs, issue the 198662306a36Sopenharmony_ci Set CCB Format command to allow 32 Logical Units per Target Device. 198762306a36Sopenharmony_ci */ 198862306a36Sopenharmony_ci if (adapter->ext_lun) { 198962306a36Sopenharmony_ci setccb_fmt = BLOGIC_EXT_LUN_CCB; 199062306a36Sopenharmony_ci if (blogic_cmd(adapter, BLOGIC_SETCCB_FMT, &setccb_fmt, 199162306a36Sopenharmony_ci sizeof(setccb_fmt), NULL, 0) < 0) 199262306a36Sopenharmony_ci return blogic_failure(adapter, "SET CCB FORMAT"); 199362306a36Sopenharmony_ci } 199462306a36Sopenharmony_ci 199562306a36Sopenharmony_ci /* 199662306a36Sopenharmony_ci Announce Successful Initialization. 199762306a36Sopenharmony_ci */ 199862306a36Sopenharmony_cidone: 199962306a36Sopenharmony_ci if (!adapter->adapter_initd) { 200062306a36Sopenharmony_ci blogic_info("*** %s Initialized Successfully ***\n", adapter, 200162306a36Sopenharmony_ci adapter->full_model); 200262306a36Sopenharmony_ci blogic_info("\n", adapter); 200362306a36Sopenharmony_ci } else 200462306a36Sopenharmony_ci blogic_warn("*** %s Initialized Successfully ***\n", adapter, 200562306a36Sopenharmony_ci adapter->full_model); 200662306a36Sopenharmony_ci adapter->adapter_initd = true; 200762306a36Sopenharmony_ci 200862306a36Sopenharmony_ci /* 200962306a36Sopenharmony_ci Indicate the Host Adapter Initialization completed successfully. 201062306a36Sopenharmony_ci */ 201162306a36Sopenharmony_ci return true; 201262306a36Sopenharmony_ci} 201362306a36Sopenharmony_ci 201462306a36Sopenharmony_ci 201562306a36Sopenharmony_ci/* 201662306a36Sopenharmony_ci blogic_inquiry inquires about the Target Devices accessible 201762306a36Sopenharmony_ci through Host Adapter. 201862306a36Sopenharmony_ci*/ 201962306a36Sopenharmony_ci 202062306a36Sopenharmony_cistatic bool __init blogic_inquiry(struct blogic_adapter *adapter) 202162306a36Sopenharmony_ci{ 202262306a36Sopenharmony_ci u16 installed_devs; 202362306a36Sopenharmony_ci u8 installed_devs0to7[8]; 202462306a36Sopenharmony_ci struct blogic_setup_info setupinfo; 202562306a36Sopenharmony_ci u8 sync_period[BLOGIC_MAXDEV]; 202662306a36Sopenharmony_ci unsigned char req_replylen; 202762306a36Sopenharmony_ci int tgt_id; 202862306a36Sopenharmony_ci 202962306a36Sopenharmony_ci /* 203062306a36Sopenharmony_ci Wait a few seconds between the Host Adapter Hard Reset which 203162306a36Sopenharmony_ci initiates a SCSI Bus Reset and issuing any SCSI Commands. Some 203262306a36Sopenharmony_ci SCSI devices get confused if they receive SCSI Commands too soon 203362306a36Sopenharmony_ci after a SCSI Bus Reset. 203462306a36Sopenharmony_ci */ 203562306a36Sopenharmony_ci blogic_delay(adapter->bus_settle_time); 203662306a36Sopenharmony_ci /* 203762306a36Sopenharmony_ci FlashPoint Host Adapters do not provide for Target Device Inquiry. 203862306a36Sopenharmony_ci */ 203962306a36Sopenharmony_ci if (blogic_flashpoint_type(adapter)) 204062306a36Sopenharmony_ci return true; 204162306a36Sopenharmony_ci /* 204262306a36Sopenharmony_ci Inhibit the Target Device Inquiry if requested. 204362306a36Sopenharmony_ci */ 204462306a36Sopenharmony_ci if (adapter->drvr_opts != NULL && adapter->drvr_opts->stop_tgt_inquiry) 204562306a36Sopenharmony_ci return true; 204662306a36Sopenharmony_ci /* 204762306a36Sopenharmony_ci Issue the Inquire Target Devices command for host adapters with 204862306a36Sopenharmony_ci firmware version 4.25 or later, or the Inquire Installed Devices 204962306a36Sopenharmony_ci ID 0 to 7 command for older host adapters. This is necessary to 205062306a36Sopenharmony_ci force Synchronous Transfer Negotiation so that the Inquire Setup 205162306a36Sopenharmony_ci Information and Inquire Synchronous Period commands will return 205262306a36Sopenharmony_ci valid data. The Inquire Target Devices command is preferable to 205362306a36Sopenharmony_ci Inquire Installed Devices ID 0 to 7 since it only probes Logical 205462306a36Sopenharmony_ci Unit 0 of each Target Device. 205562306a36Sopenharmony_ci */ 205662306a36Sopenharmony_ci if (strcmp(adapter->fw_ver, "4.25") >= 0) { 205762306a36Sopenharmony_ci 205862306a36Sopenharmony_ci /* 205962306a36Sopenharmony_ci Issue a Inquire Target Devices command. Inquire Target 206062306a36Sopenharmony_ci Devices only tests Logical Unit 0 of each Target Device 206162306a36Sopenharmony_ci unlike the Inquire Installed Devices commands which test 206262306a36Sopenharmony_ci Logical Units 0 - 7. Two bytes are returned, where byte 206362306a36Sopenharmony_ci 0 bit 0 set indicates that Target Device 0 exists, and so on. 206462306a36Sopenharmony_ci */ 206562306a36Sopenharmony_ci 206662306a36Sopenharmony_ci if (blogic_cmd(adapter, BLOGIC_INQ_DEV, NULL, 0, 206762306a36Sopenharmony_ci &installed_devs, sizeof(installed_devs)) 206862306a36Sopenharmony_ci != sizeof(installed_devs)) 206962306a36Sopenharmony_ci return blogic_failure(adapter, "INQUIRE TARGET DEVICES"); 207062306a36Sopenharmony_ci for (tgt_id = 0; tgt_id < adapter->maxdev; tgt_id++) 207162306a36Sopenharmony_ci adapter->tgt_flags[tgt_id].tgt_exists = 207262306a36Sopenharmony_ci (installed_devs & (1 << tgt_id) ? true : false); 207362306a36Sopenharmony_ci } else { 207462306a36Sopenharmony_ci 207562306a36Sopenharmony_ci /* 207662306a36Sopenharmony_ci Issue an Inquire Installed Devices command. For each 207762306a36Sopenharmony_ci Target Device, a byte is returned where bit 0 set 207862306a36Sopenharmony_ci indicates that Logical Unit 0 * exists, bit 1 set 207962306a36Sopenharmony_ci indicates that Logical Unit 1 exists, and so on. 208062306a36Sopenharmony_ci */ 208162306a36Sopenharmony_ci 208262306a36Sopenharmony_ci if (blogic_cmd(adapter, BLOGIC_INQ_DEV0TO7, NULL, 0, 208362306a36Sopenharmony_ci &installed_devs0to7, sizeof(installed_devs0to7)) 208462306a36Sopenharmony_ci != sizeof(installed_devs0to7)) 208562306a36Sopenharmony_ci return blogic_failure(adapter, 208662306a36Sopenharmony_ci "INQUIRE INSTALLED DEVICES ID 0 TO 7"); 208762306a36Sopenharmony_ci for (tgt_id = 0; tgt_id < 8; tgt_id++) 208862306a36Sopenharmony_ci adapter->tgt_flags[tgt_id].tgt_exists = 208962306a36Sopenharmony_ci installed_devs0to7[tgt_id] != 0; 209062306a36Sopenharmony_ci } 209162306a36Sopenharmony_ci /* 209262306a36Sopenharmony_ci Issue the Inquire Setup Information command. 209362306a36Sopenharmony_ci */ 209462306a36Sopenharmony_ci req_replylen = sizeof(setupinfo); 209562306a36Sopenharmony_ci if (blogic_cmd(adapter, BLOGIC_INQ_SETUPINFO, &req_replylen, 209662306a36Sopenharmony_ci sizeof(req_replylen), &setupinfo, sizeof(setupinfo)) 209762306a36Sopenharmony_ci != sizeof(setupinfo)) 209862306a36Sopenharmony_ci return blogic_failure(adapter, "INQUIRE SETUP INFORMATION"); 209962306a36Sopenharmony_ci for (tgt_id = 0; tgt_id < adapter->maxdev; tgt_id++) 210062306a36Sopenharmony_ci adapter->sync_offset[tgt_id] = (tgt_id < 8 ? setupinfo.sync0to7[tgt_id].offset : setupinfo.sync8to15[tgt_id - 8].offset); 210162306a36Sopenharmony_ci if (strcmp(adapter->fw_ver, "5.06L") >= 0) 210262306a36Sopenharmony_ci for (tgt_id = 0; tgt_id < adapter->maxdev; tgt_id++) 210362306a36Sopenharmony_ci adapter->tgt_flags[tgt_id].wide_active = (tgt_id < 8 ? (setupinfo.wide_tx_active0to7 & (1 << tgt_id) ? true : false) : (setupinfo.wide_tx_active8to15 & (1 << (tgt_id - 8)) ? true : false)); 210462306a36Sopenharmony_ci /* 210562306a36Sopenharmony_ci Issue the Inquire Synchronous Period command. 210662306a36Sopenharmony_ci */ 210762306a36Sopenharmony_ci if (adapter->fw_ver[0] >= '3') { 210862306a36Sopenharmony_ci 210962306a36Sopenharmony_ci /* Issue a Inquire Synchronous Period command. For each 211062306a36Sopenharmony_ci Target Device, a byte is returned which represents the 211162306a36Sopenharmony_ci Synchronous Transfer Period in units of 10 nanoseconds. 211262306a36Sopenharmony_ci */ 211362306a36Sopenharmony_ci 211462306a36Sopenharmony_ci req_replylen = sizeof(sync_period); 211562306a36Sopenharmony_ci if (blogic_cmd(adapter, BLOGIC_INQ_SYNC_PERIOD, &req_replylen, 211662306a36Sopenharmony_ci sizeof(req_replylen), &sync_period, 211762306a36Sopenharmony_ci sizeof(sync_period)) != sizeof(sync_period)) 211862306a36Sopenharmony_ci return blogic_failure(adapter, 211962306a36Sopenharmony_ci "INQUIRE SYNCHRONOUS PERIOD"); 212062306a36Sopenharmony_ci for (tgt_id = 0; tgt_id < adapter->maxdev; tgt_id++) 212162306a36Sopenharmony_ci adapter->sync_period[tgt_id] = sync_period[tgt_id]; 212262306a36Sopenharmony_ci } else 212362306a36Sopenharmony_ci for (tgt_id = 0; tgt_id < adapter->maxdev; tgt_id++) 212462306a36Sopenharmony_ci if (setupinfo.sync0to7[tgt_id].offset > 0) 212562306a36Sopenharmony_ci adapter->sync_period[tgt_id] = 20 + 5 * setupinfo.sync0to7[tgt_id].tx_period; 212662306a36Sopenharmony_ci /* 212762306a36Sopenharmony_ci Indicate the Target Device Inquiry completed successfully. 212862306a36Sopenharmony_ci */ 212962306a36Sopenharmony_ci return true; 213062306a36Sopenharmony_ci} 213162306a36Sopenharmony_ci 213262306a36Sopenharmony_ci/* 213362306a36Sopenharmony_ci blogic_inithoststruct initializes the fields in the SCSI Host 213462306a36Sopenharmony_ci structure. The base, io_port, n_io_ports, irq, and dma_channel fields in the 213562306a36Sopenharmony_ci SCSI Host structure are intentionally left uninitialized, as this driver 213662306a36Sopenharmony_ci handles acquisition and release of these resources explicitly, as well as 213762306a36Sopenharmony_ci ensuring exclusive access to the Host Adapter hardware and data structures 213862306a36Sopenharmony_ci through explicit acquisition and release of the Host Adapter's Lock. 213962306a36Sopenharmony_ci*/ 214062306a36Sopenharmony_ci 214162306a36Sopenharmony_cistatic void __init blogic_inithoststruct(struct blogic_adapter *adapter, 214262306a36Sopenharmony_ci struct Scsi_Host *host) 214362306a36Sopenharmony_ci{ 214462306a36Sopenharmony_ci host->max_id = adapter->maxdev; 214562306a36Sopenharmony_ci host->max_lun = adapter->maxlun; 214662306a36Sopenharmony_ci host->max_channel = 0; 214762306a36Sopenharmony_ci host->unique_id = adapter->io_addr; 214862306a36Sopenharmony_ci host->this_id = adapter->scsi_id; 214962306a36Sopenharmony_ci host->can_queue = adapter->drvr_qdepth; 215062306a36Sopenharmony_ci host->sg_tablesize = adapter->drvr_sglimit; 215162306a36Sopenharmony_ci host->cmd_per_lun = adapter->untag_qdepth; 215262306a36Sopenharmony_ci} 215362306a36Sopenharmony_ci 215462306a36Sopenharmony_ci/* 215562306a36Sopenharmony_ci blogic_slaveconfig will actually set the queue depth on individual 215662306a36Sopenharmony_ci scsi devices as they are permanently added to the device chain. We 215762306a36Sopenharmony_ci shamelessly rip off the SelectQueueDepths code to make this work mostly 215862306a36Sopenharmony_ci like it used to. Since we don't get called once at the end of the scan 215962306a36Sopenharmony_ci but instead get called for each device, we have to do things a bit 216062306a36Sopenharmony_ci differently. 216162306a36Sopenharmony_ci*/ 216262306a36Sopenharmony_cistatic int blogic_slaveconfig(struct scsi_device *dev) 216362306a36Sopenharmony_ci{ 216462306a36Sopenharmony_ci struct blogic_adapter *adapter = 216562306a36Sopenharmony_ci (struct blogic_adapter *) dev->host->hostdata; 216662306a36Sopenharmony_ci int tgt_id = dev->id; 216762306a36Sopenharmony_ci int qdepth = adapter->qdepth[tgt_id]; 216862306a36Sopenharmony_ci 216962306a36Sopenharmony_ci if (adapter->tgt_flags[tgt_id].tagq_ok && 217062306a36Sopenharmony_ci (adapter->tagq_ok & (1 << tgt_id))) { 217162306a36Sopenharmony_ci if (qdepth == 0) 217262306a36Sopenharmony_ci qdepth = BLOGIC_MAX_AUTO_TAG_DEPTH; 217362306a36Sopenharmony_ci adapter->qdepth[tgt_id] = qdepth; 217462306a36Sopenharmony_ci scsi_change_queue_depth(dev, qdepth); 217562306a36Sopenharmony_ci } else { 217662306a36Sopenharmony_ci adapter->tagq_ok &= ~(1 << tgt_id); 217762306a36Sopenharmony_ci qdepth = adapter->untag_qdepth; 217862306a36Sopenharmony_ci adapter->qdepth[tgt_id] = qdepth; 217962306a36Sopenharmony_ci scsi_change_queue_depth(dev, qdepth); 218062306a36Sopenharmony_ci } 218162306a36Sopenharmony_ci qdepth = 0; 218262306a36Sopenharmony_ci for (tgt_id = 0; tgt_id < adapter->maxdev; tgt_id++) 218362306a36Sopenharmony_ci if (adapter->tgt_flags[tgt_id].tgt_exists) 218462306a36Sopenharmony_ci qdepth += adapter->qdepth[tgt_id]; 218562306a36Sopenharmony_ci if (qdepth > adapter->alloc_ccbs) 218662306a36Sopenharmony_ci blogic_create_addlccbs(adapter, qdepth - adapter->alloc_ccbs, 218762306a36Sopenharmony_ci false); 218862306a36Sopenharmony_ci return 0; 218962306a36Sopenharmony_ci} 219062306a36Sopenharmony_ci 219162306a36Sopenharmony_ci/* 219262306a36Sopenharmony_ci blogic_init probes for BusLogic Host Adapters at the standard 219362306a36Sopenharmony_ci I/O Addresses where they may be located, initializing, registering, and 219462306a36Sopenharmony_ci reporting the configuration of each BusLogic Host Adapter it finds. It 219562306a36Sopenharmony_ci returns the number of BusLogic Host Adapters successfully initialized and 219662306a36Sopenharmony_ci registered. 219762306a36Sopenharmony_ci*/ 219862306a36Sopenharmony_ci 219962306a36Sopenharmony_cistatic int __init blogic_init(void) 220062306a36Sopenharmony_ci{ 220162306a36Sopenharmony_ci int drvr_optindex = 0, probeindex; 220262306a36Sopenharmony_ci struct blogic_adapter *adapter; 220362306a36Sopenharmony_ci int ret = 0; 220462306a36Sopenharmony_ci 220562306a36Sopenharmony_ci#ifdef MODULE 220662306a36Sopenharmony_ci if (BusLogic) 220762306a36Sopenharmony_ci blogic_setup(BusLogic); 220862306a36Sopenharmony_ci#endif 220962306a36Sopenharmony_ci 221062306a36Sopenharmony_ci if (blogic_probe_options.noprobe) 221162306a36Sopenharmony_ci return -ENODEV; 221262306a36Sopenharmony_ci blogic_probeinfo_list = 221362306a36Sopenharmony_ci kcalloc(BLOGIC_MAX_ADAPTERS, sizeof(struct blogic_probeinfo), 221462306a36Sopenharmony_ci GFP_KERNEL); 221562306a36Sopenharmony_ci if (blogic_probeinfo_list == NULL) { 221662306a36Sopenharmony_ci blogic_err("BusLogic: Unable to allocate Probe Info List\n", 221762306a36Sopenharmony_ci NULL); 221862306a36Sopenharmony_ci return -ENOMEM; 221962306a36Sopenharmony_ci } 222062306a36Sopenharmony_ci 222162306a36Sopenharmony_ci adapter = kzalloc(sizeof(struct blogic_adapter), GFP_KERNEL); 222262306a36Sopenharmony_ci if (adapter == NULL) { 222362306a36Sopenharmony_ci kfree(blogic_probeinfo_list); 222462306a36Sopenharmony_ci blogic_err("BusLogic: Unable to allocate Prototype Host Adapter\n", NULL); 222562306a36Sopenharmony_ci return -ENOMEM; 222662306a36Sopenharmony_ci } 222762306a36Sopenharmony_ci 222862306a36Sopenharmony_ci#ifdef MODULE 222962306a36Sopenharmony_ci if (BusLogic != NULL) 223062306a36Sopenharmony_ci blogic_setup(BusLogic); 223162306a36Sopenharmony_ci#endif 223262306a36Sopenharmony_ci blogic_init_probeinfo_list(adapter); 223362306a36Sopenharmony_ci for (probeindex = 0; probeindex < blogic_probeinfo_count; probeindex++) { 223462306a36Sopenharmony_ci struct blogic_probeinfo *probeinfo = 223562306a36Sopenharmony_ci &blogic_probeinfo_list[probeindex]; 223662306a36Sopenharmony_ci struct blogic_adapter *myadapter = adapter; 223762306a36Sopenharmony_ci struct Scsi_Host *host; 223862306a36Sopenharmony_ci 223962306a36Sopenharmony_ci if (probeinfo->io_addr == 0) 224062306a36Sopenharmony_ci continue; 224162306a36Sopenharmony_ci memset(myadapter, 0, sizeof(struct blogic_adapter)); 224262306a36Sopenharmony_ci myadapter->adapter_type = probeinfo->adapter_type; 224362306a36Sopenharmony_ci myadapter->adapter_bus_type = probeinfo->adapter_bus_type; 224462306a36Sopenharmony_ci myadapter->io_addr = probeinfo->io_addr; 224562306a36Sopenharmony_ci myadapter->pci_addr = probeinfo->pci_addr; 224662306a36Sopenharmony_ci myadapter->bus = probeinfo->bus; 224762306a36Sopenharmony_ci myadapter->dev = probeinfo->dev; 224862306a36Sopenharmony_ci myadapter->pci_device = probeinfo->pci_device; 224962306a36Sopenharmony_ci myadapter->irq_ch = probeinfo->irq_ch; 225062306a36Sopenharmony_ci myadapter->addr_count = 225162306a36Sopenharmony_ci blogic_adapter_addr_count[myadapter->adapter_type]; 225262306a36Sopenharmony_ci 225362306a36Sopenharmony_ci /* 225462306a36Sopenharmony_ci Make sure region is free prior to probing. 225562306a36Sopenharmony_ci */ 225662306a36Sopenharmony_ci if (!request_region(myadapter->io_addr, myadapter->addr_count, 225762306a36Sopenharmony_ci "BusLogic")) 225862306a36Sopenharmony_ci continue; 225962306a36Sopenharmony_ci /* 226062306a36Sopenharmony_ci Probe the Host Adapter. If unsuccessful, abort further 226162306a36Sopenharmony_ci initialization. 226262306a36Sopenharmony_ci */ 226362306a36Sopenharmony_ci if (!blogic_probe(myadapter)) { 226462306a36Sopenharmony_ci release_region(myadapter->io_addr, 226562306a36Sopenharmony_ci myadapter->addr_count); 226662306a36Sopenharmony_ci continue; 226762306a36Sopenharmony_ci } 226862306a36Sopenharmony_ci /* 226962306a36Sopenharmony_ci Hard Reset the Host Adapter. If unsuccessful, abort further 227062306a36Sopenharmony_ci initialization. 227162306a36Sopenharmony_ci */ 227262306a36Sopenharmony_ci if (!blogic_hwreset(myadapter, true)) { 227362306a36Sopenharmony_ci release_region(myadapter->io_addr, 227462306a36Sopenharmony_ci myadapter->addr_count); 227562306a36Sopenharmony_ci continue; 227662306a36Sopenharmony_ci } 227762306a36Sopenharmony_ci /* 227862306a36Sopenharmony_ci Check the Host Adapter. If unsuccessful, abort further 227962306a36Sopenharmony_ci initialization. 228062306a36Sopenharmony_ci */ 228162306a36Sopenharmony_ci if (!blogic_checkadapter(myadapter)) { 228262306a36Sopenharmony_ci release_region(myadapter->io_addr, 228362306a36Sopenharmony_ci myadapter->addr_count); 228462306a36Sopenharmony_ci continue; 228562306a36Sopenharmony_ci } 228662306a36Sopenharmony_ci /* 228762306a36Sopenharmony_ci Initialize the Driver Options field if provided. 228862306a36Sopenharmony_ci */ 228962306a36Sopenharmony_ci if (drvr_optindex < blogic_drvr_options_count) 229062306a36Sopenharmony_ci myadapter->drvr_opts = 229162306a36Sopenharmony_ci &blogic_drvr_options[drvr_optindex++]; 229262306a36Sopenharmony_ci /* 229362306a36Sopenharmony_ci Announce the Driver Version and Date, Author's Name, 229462306a36Sopenharmony_ci Copyright Notice, and Electronic Mail Address. 229562306a36Sopenharmony_ci */ 229662306a36Sopenharmony_ci blogic_announce_drvr(myadapter); 229762306a36Sopenharmony_ci /* 229862306a36Sopenharmony_ci Register the SCSI Host structure. 229962306a36Sopenharmony_ci */ 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_ci host = scsi_host_alloc(&blogic_template, 230262306a36Sopenharmony_ci sizeof(struct blogic_adapter)); 230362306a36Sopenharmony_ci if (host == NULL) { 230462306a36Sopenharmony_ci release_region(myadapter->io_addr, 230562306a36Sopenharmony_ci myadapter->addr_count); 230662306a36Sopenharmony_ci continue; 230762306a36Sopenharmony_ci } 230862306a36Sopenharmony_ci myadapter = (struct blogic_adapter *) host->hostdata; 230962306a36Sopenharmony_ci memcpy(myadapter, adapter, sizeof(struct blogic_adapter)); 231062306a36Sopenharmony_ci myadapter->scsi_host = host; 231162306a36Sopenharmony_ci myadapter->host_no = host->host_no; 231262306a36Sopenharmony_ci /* 231362306a36Sopenharmony_ci Add Host Adapter to the end of the list of registered 231462306a36Sopenharmony_ci BusLogic Host Adapters. 231562306a36Sopenharmony_ci */ 231662306a36Sopenharmony_ci list_add_tail(&myadapter->host_list, &blogic_host_list); 231762306a36Sopenharmony_ci 231862306a36Sopenharmony_ci /* 231962306a36Sopenharmony_ci Read the Host Adapter Configuration, Configure the Host 232062306a36Sopenharmony_ci Adapter, Acquire the System Resources necessary to use 232162306a36Sopenharmony_ci the Host Adapter, then Create the Initial CCBs, Initialize 232262306a36Sopenharmony_ci the Host Adapter, and finally perform Target Device 232362306a36Sopenharmony_ci Inquiry. From this point onward, any failure will be 232462306a36Sopenharmony_ci assumed to be due to a problem with the Host Adapter, 232562306a36Sopenharmony_ci rather than due to having mistakenly identified this port 232662306a36Sopenharmony_ci as belonging to a BusLogic Host Adapter. The I/O Address 232762306a36Sopenharmony_ci range will not be released, thereby preventing it from 232862306a36Sopenharmony_ci being incorrectly identified as any other type of Host 232962306a36Sopenharmony_ci Adapter. 233062306a36Sopenharmony_ci */ 233162306a36Sopenharmony_ci if (blogic_rdconfig(myadapter) && 233262306a36Sopenharmony_ci blogic_reportconfig(myadapter) && 233362306a36Sopenharmony_ci blogic_getres(myadapter) && 233462306a36Sopenharmony_ci blogic_create_initccbs(myadapter) && 233562306a36Sopenharmony_ci blogic_initadapter(myadapter) && 233662306a36Sopenharmony_ci blogic_inquiry(myadapter)) { 233762306a36Sopenharmony_ci /* 233862306a36Sopenharmony_ci Initialization has been completed successfully. 233962306a36Sopenharmony_ci Release and re-register usage of the I/O Address 234062306a36Sopenharmony_ci range so that the Model Name of the Host Adapter 234162306a36Sopenharmony_ci will appear, and initialize the SCSI Host structure. 234262306a36Sopenharmony_ci */ 234362306a36Sopenharmony_ci release_region(myadapter->io_addr, 234462306a36Sopenharmony_ci myadapter->addr_count); 234562306a36Sopenharmony_ci if (!request_region(myadapter->io_addr, 234662306a36Sopenharmony_ci myadapter->addr_count, 234762306a36Sopenharmony_ci myadapter->full_model)) { 234862306a36Sopenharmony_ci printk(KERN_WARNING 234962306a36Sopenharmony_ci "BusLogic: Release and re-register of " 235062306a36Sopenharmony_ci "port 0x%04lx failed \n", 235162306a36Sopenharmony_ci (unsigned long)myadapter->io_addr); 235262306a36Sopenharmony_ci blogic_destroy_ccbs(myadapter); 235362306a36Sopenharmony_ci blogic_relres(myadapter); 235462306a36Sopenharmony_ci list_del(&myadapter->host_list); 235562306a36Sopenharmony_ci scsi_host_put(host); 235662306a36Sopenharmony_ci ret = -ENOMEM; 235762306a36Sopenharmony_ci } else { 235862306a36Sopenharmony_ci blogic_inithoststruct(myadapter, 235962306a36Sopenharmony_ci host); 236062306a36Sopenharmony_ci if (scsi_add_host(host, myadapter->pci_device 236162306a36Sopenharmony_ci ? &myadapter->pci_device->dev 236262306a36Sopenharmony_ci : NULL)) { 236362306a36Sopenharmony_ci printk(KERN_WARNING 236462306a36Sopenharmony_ci "BusLogic: scsi_add_host()" 236562306a36Sopenharmony_ci "failed!\n"); 236662306a36Sopenharmony_ci blogic_destroy_ccbs(myadapter); 236762306a36Sopenharmony_ci blogic_relres(myadapter); 236862306a36Sopenharmony_ci list_del(&myadapter->host_list); 236962306a36Sopenharmony_ci scsi_host_put(host); 237062306a36Sopenharmony_ci ret = -ENODEV; 237162306a36Sopenharmony_ci } else 237262306a36Sopenharmony_ci scsi_scan_host(host); 237362306a36Sopenharmony_ci } 237462306a36Sopenharmony_ci } else { 237562306a36Sopenharmony_ci /* 237662306a36Sopenharmony_ci An error occurred during Host Adapter Configuration 237762306a36Sopenharmony_ci Querying, Host Adapter Configuration, Resource 237862306a36Sopenharmony_ci Acquisition, CCB Creation, Host Adapter 237962306a36Sopenharmony_ci Initialization, or Target Device Inquiry, so 238062306a36Sopenharmony_ci remove Host Adapter from the list of registered 238162306a36Sopenharmony_ci BusLogic Host Adapters, destroy the CCBs, Release 238262306a36Sopenharmony_ci the System Resources, and Unregister the SCSI 238362306a36Sopenharmony_ci Host. 238462306a36Sopenharmony_ci */ 238562306a36Sopenharmony_ci blogic_destroy_ccbs(myadapter); 238662306a36Sopenharmony_ci blogic_relres(myadapter); 238762306a36Sopenharmony_ci list_del(&myadapter->host_list); 238862306a36Sopenharmony_ci scsi_host_put(host); 238962306a36Sopenharmony_ci ret = -ENODEV; 239062306a36Sopenharmony_ci } 239162306a36Sopenharmony_ci } 239262306a36Sopenharmony_ci kfree(adapter); 239362306a36Sopenharmony_ci kfree(blogic_probeinfo_list); 239462306a36Sopenharmony_ci blogic_probeinfo_list = NULL; 239562306a36Sopenharmony_ci return ret; 239662306a36Sopenharmony_ci} 239762306a36Sopenharmony_ci 239862306a36Sopenharmony_ci 239962306a36Sopenharmony_ci/* 240062306a36Sopenharmony_ci blogic_deladapter releases all resources previously acquired to 240162306a36Sopenharmony_ci support a specific Host Adapter, including the I/O Address range, and 240262306a36Sopenharmony_ci unregisters the BusLogic Host Adapter. 240362306a36Sopenharmony_ci*/ 240462306a36Sopenharmony_ci 240562306a36Sopenharmony_cistatic int __exit blogic_deladapter(struct blogic_adapter *adapter) 240662306a36Sopenharmony_ci{ 240762306a36Sopenharmony_ci struct Scsi_Host *host = adapter->scsi_host; 240862306a36Sopenharmony_ci 240962306a36Sopenharmony_ci scsi_remove_host(host); 241062306a36Sopenharmony_ci 241162306a36Sopenharmony_ci /* 241262306a36Sopenharmony_ci FlashPoint Host Adapters must first be released by the FlashPoint 241362306a36Sopenharmony_ci SCCB Manager. 241462306a36Sopenharmony_ci */ 241562306a36Sopenharmony_ci if (blogic_flashpoint_type(adapter)) 241662306a36Sopenharmony_ci FlashPoint_ReleaseHostAdapter(adapter->cardhandle); 241762306a36Sopenharmony_ci /* 241862306a36Sopenharmony_ci Destroy the CCBs and release any system resources acquired to 241962306a36Sopenharmony_ci support Host Adapter. 242062306a36Sopenharmony_ci */ 242162306a36Sopenharmony_ci blogic_destroy_ccbs(adapter); 242262306a36Sopenharmony_ci blogic_relres(adapter); 242362306a36Sopenharmony_ci /* 242462306a36Sopenharmony_ci Release usage of the I/O Address range. 242562306a36Sopenharmony_ci */ 242662306a36Sopenharmony_ci release_region(adapter->io_addr, adapter->addr_count); 242762306a36Sopenharmony_ci /* 242862306a36Sopenharmony_ci Remove Host Adapter from the list of registered BusLogic 242962306a36Sopenharmony_ci Host Adapters. 243062306a36Sopenharmony_ci */ 243162306a36Sopenharmony_ci list_del(&adapter->host_list); 243262306a36Sopenharmony_ci 243362306a36Sopenharmony_ci scsi_host_put(host); 243462306a36Sopenharmony_ci return 0; 243562306a36Sopenharmony_ci} 243662306a36Sopenharmony_ci 243762306a36Sopenharmony_ci 243862306a36Sopenharmony_ci/* 243962306a36Sopenharmony_ci blogic_qcompleted_ccb queues CCB for completion processing. 244062306a36Sopenharmony_ci*/ 244162306a36Sopenharmony_ci 244262306a36Sopenharmony_cistatic void blogic_qcompleted_ccb(struct blogic_ccb *ccb) 244362306a36Sopenharmony_ci{ 244462306a36Sopenharmony_ci struct blogic_adapter *adapter = ccb->adapter; 244562306a36Sopenharmony_ci 244662306a36Sopenharmony_ci ccb->status = BLOGIC_CCB_COMPLETE; 244762306a36Sopenharmony_ci ccb->next = NULL; 244862306a36Sopenharmony_ci if (adapter->firstccb == NULL) { 244962306a36Sopenharmony_ci adapter->firstccb = ccb; 245062306a36Sopenharmony_ci adapter->lastccb = ccb; 245162306a36Sopenharmony_ci } else { 245262306a36Sopenharmony_ci adapter->lastccb->next = ccb; 245362306a36Sopenharmony_ci adapter->lastccb = ccb; 245462306a36Sopenharmony_ci } 245562306a36Sopenharmony_ci adapter->active_cmds[ccb->tgt_id]--; 245662306a36Sopenharmony_ci} 245762306a36Sopenharmony_ci 245862306a36Sopenharmony_ci 245962306a36Sopenharmony_ci/* 246062306a36Sopenharmony_ci blogic_resultcode computes a SCSI Subsystem Result Code from 246162306a36Sopenharmony_ci the Host Adapter Status and Target Device Status. 246262306a36Sopenharmony_ci*/ 246362306a36Sopenharmony_ci 246462306a36Sopenharmony_cistatic int blogic_resultcode(struct blogic_adapter *adapter, 246562306a36Sopenharmony_ci enum blogic_adapter_status adapter_status, 246662306a36Sopenharmony_ci enum blogic_tgt_status tgt_status) 246762306a36Sopenharmony_ci{ 246862306a36Sopenharmony_ci int hoststatus; 246962306a36Sopenharmony_ci 247062306a36Sopenharmony_ci switch (adapter_status) { 247162306a36Sopenharmony_ci case BLOGIC_CMD_CMPLT_NORMAL: 247262306a36Sopenharmony_ci case BLOGIC_LINK_CMD_CMPLT: 247362306a36Sopenharmony_ci case BLOGIC_LINK_CMD_CMPLT_FLAG: 247462306a36Sopenharmony_ci hoststatus = DID_OK; 247562306a36Sopenharmony_ci break; 247662306a36Sopenharmony_ci case BLOGIC_SELECT_TIMEOUT: 247762306a36Sopenharmony_ci hoststatus = DID_TIME_OUT; 247862306a36Sopenharmony_ci break; 247962306a36Sopenharmony_ci case BLOGIC_INVALID_OUTBOX_CODE: 248062306a36Sopenharmony_ci case BLOGIC_INVALID_CMD_CODE: 248162306a36Sopenharmony_ci case BLOGIC_BAD_CMD_PARAM: 248262306a36Sopenharmony_ci blogic_warn("BusLogic Driver Protocol Error 0x%02X\n", 248362306a36Sopenharmony_ci adapter, adapter_status); 248462306a36Sopenharmony_ci fallthrough; 248562306a36Sopenharmony_ci case BLOGIC_DATA_UNDERRUN: 248662306a36Sopenharmony_ci case BLOGIC_DATA_OVERRUN: 248762306a36Sopenharmony_ci case BLOGIC_NOEXPECT_BUSFREE: 248862306a36Sopenharmony_ci case BLOGIC_LINKCCB_BADLUN: 248962306a36Sopenharmony_ci case BLOGIC_AUTOREQSENSE_FAIL: 249062306a36Sopenharmony_ci case BLOGIC_TAGQUEUE_REJECT: 249162306a36Sopenharmony_ci case BLOGIC_BAD_MSG_RCVD: 249262306a36Sopenharmony_ci case BLOGIC_HW_FAIL: 249362306a36Sopenharmony_ci case BLOGIC_BAD_RECONNECT: 249462306a36Sopenharmony_ci case BLOGIC_ABRT_QUEUE: 249562306a36Sopenharmony_ci case BLOGIC_ADAPTER_SW_ERROR: 249662306a36Sopenharmony_ci case BLOGIC_HW_TIMEOUT: 249762306a36Sopenharmony_ci case BLOGIC_PARITY_ERR: 249862306a36Sopenharmony_ci hoststatus = DID_ERROR; 249962306a36Sopenharmony_ci break; 250062306a36Sopenharmony_ci case BLOGIC_INVALID_BUSPHASE: 250162306a36Sopenharmony_ci case BLOGIC_NORESPONSE_TO_ATN: 250262306a36Sopenharmony_ci case BLOGIC_HW_RESET: 250362306a36Sopenharmony_ci case BLOGIC_RST_FROM_OTHERDEV: 250462306a36Sopenharmony_ci case BLOGIC_HW_BDR: 250562306a36Sopenharmony_ci hoststatus = DID_RESET; 250662306a36Sopenharmony_ci break; 250762306a36Sopenharmony_ci default: 250862306a36Sopenharmony_ci blogic_warn("Unknown Host Adapter Status 0x%02X\n", adapter, 250962306a36Sopenharmony_ci adapter_status); 251062306a36Sopenharmony_ci hoststatus = DID_ERROR; 251162306a36Sopenharmony_ci break; 251262306a36Sopenharmony_ci } 251362306a36Sopenharmony_ci return (hoststatus << 16) | tgt_status; 251462306a36Sopenharmony_ci} 251562306a36Sopenharmony_ci 251662306a36Sopenharmony_ci/* 251762306a36Sopenharmony_ci * turn the dma address from an inbox into a ccb pointer 251862306a36Sopenharmony_ci * This is rather inefficient. 251962306a36Sopenharmony_ci */ 252062306a36Sopenharmony_cistatic struct blogic_ccb * 252162306a36Sopenharmony_ciblogic_inbox_to_ccb(struct blogic_adapter *adapter, struct blogic_inbox *inbox) 252262306a36Sopenharmony_ci{ 252362306a36Sopenharmony_ci struct blogic_ccb *ccb; 252462306a36Sopenharmony_ci 252562306a36Sopenharmony_ci for (ccb = adapter->all_ccbs; ccb; ccb = ccb->next_all) 252662306a36Sopenharmony_ci if (inbox->ccb == ccb->dma_handle) 252762306a36Sopenharmony_ci break; 252862306a36Sopenharmony_ci 252962306a36Sopenharmony_ci return ccb; 253062306a36Sopenharmony_ci} 253162306a36Sopenharmony_ci 253262306a36Sopenharmony_ci/* 253362306a36Sopenharmony_ci blogic_scan_inbox scans the Incoming Mailboxes saving any 253462306a36Sopenharmony_ci Incoming Mailbox entries for completion processing. 253562306a36Sopenharmony_ci*/ 253662306a36Sopenharmony_cistatic void blogic_scan_inbox(struct blogic_adapter *adapter) 253762306a36Sopenharmony_ci{ 253862306a36Sopenharmony_ci /* 253962306a36Sopenharmony_ci Scan through the Incoming Mailboxes in Strict Round Robin 254062306a36Sopenharmony_ci fashion, saving any completed CCBs for further processing. It 254162306a36Sopenharmony_ci is essential that for each CCB and SCSI Command issued, command 254262306a36Sopenharmony_ci completion processing is performed exactly once. Therefore, 254362306a36Sopenharmony_ci only Incoming Mailboxes with completion code Command Completed 254462306a36Sopenharmony_ci Without Error, Command Completed With Error, or Command Aborted 254562306a36Sopenharmony_ci At Host Request are saved for completion processing. When an 254662306a36Sopenharmony_ci Incoming Mailbox has a completion code of Aborted Command Not 254762306a36Sopenharmony_ci Found, the CCB had already completed or been aborted before the 254862306a36Sopenharmony_ci current Abort request was processed, and so completion processing 254962306a36Sopenharmony_ci has already occurred and no further action should be taken. 255062306a36Sopenharmony_ci */ 255162306a36Sopenharmony_ci struct blogic_inbox *next_inbox = adapter->next_inbox; 255262306a36Sopenharmony_ci enum blogic_cmplt_code comp_code; 255362306a36Sopenharmony_ci 255462306a36Sopenharmony_ci while ((comp_code = next_inbox->comp_code) != BLOGIC_INBOX_FREE) { 255562306a36Sopenharmony_ci struct blogic_ccb *ccb = blogic_inbox_to_ccb(adapter, next_inbox); 255662306a36Sopenharmony_ci if (!ccb) { 255762306a36Sopenharmony_ci /* 255862306a36Sopenharmony_ci * This should never happen, unless the CCB list is 255962306a36Sopenharmony_ci * corrupted in memory. 256062306a36Sopenharmony_ci */ 256162306a36Sopenharmony_ci blogic_warn("Could not find CCB for dma address %x\n", adapter, next_inbox->ccb); 256262306a36Sopenharmony_ci } else if (comp_code != BLOGIC_CMD_NOTFOUND) { 256362306a36Sopenharmony_ci if (ccb->status == BLOGIC_CCB_ACTIVE || 256462306a36Sopenharmony_ci ccb->status == BLOGIC_CCB_RESET) { 256562306a36Sopenharmony_ci /* 256662306a36Sopenharmony_ci Save the Completion Code for this CCB and 256762306a36Sopenharmony_ci queue the CCB for completion processing. 256862306a36Sopenharmony_ci */ 256962306a36Sopenharmony_ci ccb->comp_code = comp_code; 257062306a36Sopenharmony_ci blogic_qcompleted_ccb(ccb); 257162306a36Sopenharmony_ci } else { 257262306a36Sopenharmony_ci /* 257362306a36Sopenharmony_ci If a CCB ever appears in an Incoming Mailbox 257462306a36Sopenharmony_ci and is not marked as status Active or Reset, 257562306a36Sopenharmony_ci then there is most likely a bug in 257662306a36Sopenharmony_ci the Host Adapter firmware. 257762306a36Sopenharmony_ci */ 257862306a36Sopenharmony_ci blogic_warn("Illegal CCB #%ld status %d in Incoming Mailbox\n", adapter, ccb->serial, ccb->status); 257962306a36Sopenharmony_ci } 258062306a36Sopenharmony_ci } 258162306a36Sopenharmony_ci next_inbox->comp_code = BLOGIC_INBOX_FREE; 258262306a36Sopenharmony_ci if (++next_inbox > adapter->last_inbox) 258362306a36Sopenharmony_ci next_inbox = adapter->first_inbox; 258462306a36Sopenharmony_ci } 258562306a36Sopenharmony_ci adapter->next_inbox = next_inbox; 258662306a36Sopenharmony_ci} 258762306a36Sopenharmony_ci 258862306a36Sopenharmony_ci 258962306a36Sopenharmony_ci/* 259062306a36Sopenharmony_ci blogic_process_ccbs iterates over the completed CCBs for Host 259162306a36Sopenharmony_ci Adapter setting the SCSI Command Result Codes, deallocating the CCBs, and 259262306a36Sopenharmony_ci calling the SCSI Subsystem Completion Routines. The Host Adapter's Lock 259362306a36Sopenharmony_ci should already have been acquired by the caller. 259462306a36Sopenharmony_ci*/ 259562306a36Sopenharmony_ci 259662306a36Sopenharmony_cistatic void blogic_process_ccbs(struct blogic_adapter *adapter) 259762306a36Sopenharmony_ci{ 259862306a36Sopenharmony_ci if (adapter->processing_ccbs) 259962306a36Sopenharmony_ci return; 260062306a36Sopenharmony_ci adapter->processing_ccbs = true; 260162306a36Sopenharmony_ci while (adapter->firstccb != NULL) { 260262306a36Sopenharmony_ci struct blogic_ccb *ccb = adapter->firstccb; 260362306a36Sopenharmony_ci struct scsi_cmnd *command = ccb->command; 260462306a36Sopenharmony_ci adapter->firstccb = ccb->next; 260562306a36Sopenharmony_ci if (adapter->firstccb == NULL) 260662306a36Sopenharmony_ci adapter->lastccb = NULL; 260762306a36Sopenharmony_ci /* 260862306a36Sopenharmony_ci Process the Completed CCB. 260962306a36Sopenharmony_ci */ 261062306a36Sopenharmony_ci if (ccb->opcode == BLOGIC_BDR) { 261162306a36Sopenharmony_ci int tgt_id = ccb->tgt_id; 261262306a36Sopenharmony_ci 261362306a36Sopenharmony_ci blogic_warn("Bus Device Reset CCB #%ld to Target %d Completed\n", adapter, ccb->serial, tgt_id); 261462306a36Sopenharmony_ci blogic_inc_count(&adapter->tgt_stats[tgt_id].bdr_done); 261562306a36Sopenharmony_ci adapter->tgt_flags[tgt_id].tagq_active = false; 261662306a36Sopenharmony_ci adapter->cmds_since_rst[tgt_id] = 0; 261762306a36Sopenharmony_ci adapter->last_resetdone[tgt_id] = jiffies; 261862306a36Sopenharmony_ci /* 261962306a36Sopenharmony_ci Place CCB back on the Host Adapter's free list. 262062306a36Sopenharmony_ci */ 262162306a36Sopenharmony_ci blogic_dealloc_ccb(ccb, 1); 262262306a36Sopenharmony_ci#if 0 /* this needs to be redone different for new EH */ 262362306a36Sopenharmony_ci /* 262462306a36Sopenharmony_ci Bus Device Reset CCBs have the command field 262562306a36Sopenharmony_ci non-NULL only when a Bus Device Reset was requested 262662306a36Sopenharmony_ci for a command that did not have a currently active 262762306a36Sopenharmony_ci CCB in the Host Adapter (i.e., a Synchronous Bus 262862306a36Sopenharmony_ci Device Reset), and hence would not have its 262962306a36Sopenharmony_ci Completion Routine called otherwise. 263062306a36Sopenharmony_ci */ 263162306a36Sopenharmony_ci while (command != NULL) { 263262306a36Sopenharmony_ci struct scsi_cmnd *nxt_cmd = 263362306a36Sopenharmony_ci command->reset_chain; 263462306a36Sopenharmony_ci command->reset_chain = NULL; 263562306a36Sopenharmony_ci command->result = DID_RESET << 16; 263662306a36Sopenharmony_ci scsi_done(command); 263762306a36Sopenharmony_ci command = nxt_cmd; 263862306a36Sopenharmony_ci } 263962306a36Sopenharmony_ci#endif 264062306a36Sopenharmony_ci /* 264162306a36Sopenharmony_ci Iterate over the CCBs for this Host Adapter 264262306a36Sopenharmony_ci performing completion processing for any CCBs 264362306a36Sopenharmony_ci marked as Reset for this Target. 264462306a36Sopenharmony_ci */ 264562306a36Sopenharmony_ci for (ccb = adapter->all_ccbs; ccb != NULL; 264662306a36Sopenharmony_ci ccb = ccb->next_all) 264762306a36Sopenharmony_ci if (ccb->status == BLOGIC_CCB_RESET && 264862306a36Sopenharmony_ci ccb->tgt_id == tgt_id) { 264962306a36Sopenharmony_ci command = ccb->command; 265062306a36Sopenharmony_ci blogic_dealloc_ccb(ccb, 1); 265162306a36Sopenharmony_ci adapter->active_cmds[tgt_id]--; 265262306a36Sopenharmony_ci command->result = DID_RESET << 16; 265362306a36Sopenharmony_ci scsi_done(command); 265462306a36Sopenharmony_ci } 265562306a36Sopenharmony_ci adapter->bdr_pend[tgt_id] = NULL; 265662306a36Sopenharmony_ci } else { 265762306a36Sopenharmony_ci /* 265862306a36Sopenharmony_ci Translate the Completion Code, Host Adapter Status, 265962306a36Sopenharmony_ci and Target Device Status into a SCSI Subsystem 266062306a36Sopenharmony_ci Result Code. 266162306a36Sopenharmony_ci */ 266262306a36Sopenharmony_ci switch (ccb->comp_code) { 266362306a36Sopenharmony_ci case BLOGIC_INBOX_FREE: 266462306a36Sopenharmony_ci case BLOGIC_CMD_NOTFOUND: 266562306a36Sopenharmony_ci case BLOGIC_INVALID_CCB: 266662306a36Sopenharmony_ci blogic_warn("CCB #%ld to Target %d Impossible State\n", adapter, ccb->serial, ccb->tgt_id); 266762306a36Sopenharmony_ci break; 266862306a36Sopenharmony_ci case BLOGIC_CMD_COMPLETE_GOOD: 266962306a36Sopenharmony_ci adapter->tgt_stats[ccb->tgt_id] 267062306a36Sopenharmony_ci .cmds_complete++; 267162306a36Sopenharmony_ci adapter->tgt_flags[ccb->tgt_id] 267262306a36Sopenharmony_ci .cmd_good = true; 267362306a36Sopenharmony_ci command->result = DID_OK << 16; 267462306a36Sopenharmony_ci break; 267562306a36Sopenharmony_ci case BLOGIC_CMD_ABORT_BY_HOST: 267662306a36Sopenharmony_ci blogic_warn("CCB #%ld to Target %d Aborted\n", 267762306a36Sopenharmony_ci adapter, ccb->serial, ccb->tgt_id); 267862306a36Sopenharmony_ci blogic_inc_count(&adapter->tgt_stats[ccb->tgt_id].aborts_done); 267962306a36Sopenharmony_ci command->result = DID_ABORT << 16; 268062306a36Sopenharmony_ci break; 268162306a36Sopenharmony_ci case BLOGIC_CMD_COMPLETE_ERROR: 268262306a36Sopenharmony_ci command->result = blogic_resultcode(adapter, 268362306a36Sopenharmony_ci ccb->adapter_status, ccb->tgt_status); 268462306a36Sopenharmony_ci if (ccb->adapter_status != BLOGIC_SELECT_TIMEOUT) { 268562306a36Sopenharmony_ci adapter->tgt_stats[ccb->tgt_id] 268662306a36Sopenharmony_ci .cmds_complete++; 268762306a36Sopenharmony_ci if (blogic_global_options.trace_err) { 268862306a36Sopenharmony_ci int i; 268962306a36Sopenharmony_ci blogic_notice("CCB #%ld Target %d: Result %X Host " 269062306a36Sopenharmony_ci "Adapter Status %02X Target Status %02X\n", adapter, ccb->serial, ccb->tgt_id, command->result, ccb->adapter_status, ccb->tgt_status); 269162306a36Sopenharmony_ci blogic_notice("CDB ", adapter); 269262306a36Sopenharmony_ci for (i = 0; i < ccb->cdblen; i++) 269362306a36Sopenharmony_ci blogic_notice(" %02X", adapter, ccb->cdb[i]); 269462306a36Sopenharmony_ci blogic_notice("\n", adapter); 269562306a36Sopenharmony_ci blogic_notice("Sense ", adapter); 269662306a36Sopenharmony_ci for (i = 0; i < ccb->sense_datalen; i++) 269762306a36Sopenharmony_ci blogic_notice(" %02X", adapter, command->sense_buffer[i]); 269862306a36Sopenharmony_ci blogic_notice("\n", adapter); 269962306a36Sopenharmony_ci } 270062306a36Sopenharmony_ci } 270162306a36Sopenharmony_ci break; 270262306a36Sopenharmony_ci } 270362306a36Sopenharmony_ci /* 270462306a36Sopenharmony_ci When an INQUIRY command completes normally, save the 270562306a36Sopenharmony_ci CmdQue (Tagged Queuing Supported) and WBus16 (16 Bit 270662306a36Sopenharmony_ci Wide Data Transfers Supported) bits. 270762306a36Sopenharmony_ci */ 270862306a36Sopenharmony_ci if (ccb->cdb[0] == INQUIRY && ccb->cdb[1] == 0 && 270962306a36Sopenharmony_ci ccb->adapter_status == BLOGIC_CMD_CMPLT_NORMAL) { 271062306a36Sopenharmony_ci struct blogic_tgt_flags *tgt_flags = 271162306a36Sopenharmony_ci &adapter->tgt_flags[ccb->tgt_id]; 271262306a36Sopenharmony_ci struct scsi_inquiry *inquiry = 271362306a36Sopenharmony_ci (struct scsi_inquiry *) scsi_sglist(command); 271462306a36Sopenharmony_ci tgt_flags->tgt_exists = true; 271562306a36Sopenharmony_ci tgt_flags->tagq_ok = inquiry->CmdQue; 271662306a36Sopenharmony_ci tgt_flags->wide_ok = inquiry->WBus16; 271762306a36Sopenharmony_ci } 271862306a36Sopenharmony_ci /* 271962306a36Sopenharmony_ci Place CCB back on the Host Adapter's free list. 272062306a36Sopenharmony_ci */ 272162306a36Sopenharmony_ci blogic_dealloc_ccb(ccb, 1); 272262306a36Sopenharmony_ci /* 272362306a36Sopenharmony_ci Call the SCSI Command Completion Routine. 272462306a36Sopenharmony_ci */ 272562306a36Sopenharmony_ci scsi_done(command); 272662306a36Sopenharmony_ci } 272762306a36Sopenharmony_ci } 272862306a36Sopenharmony_ci adapter->processing_ccbs = false; 272962306a36Sopenharmony_ci} 273062306a36Sopenharmony_ci 273162306a36Sopenharmony_ci 273262306a36Sopenharmony_ci/* 273362306a36Sopenharmony_ci blogic_inthandler handles hardware interrupts from BusLogic Host 273462306a36Sopenharmony_ci Adapters. 273562306a36Sopenharmony_ci*/ 273662306a36Sopenharmony_ci 273762306a36Sopenharmony_cistatic irqreturn_t blogic_inthandler(int irq_ch, void *devid) 273862306a36Sopenharmony_ci{ 273962306a36Sopenharmony_ci struct blogic_adapter *adapter = (struct blogic_adapter *) devid; 274062306a36Sopenharmony_ci unsigned long processor_flag; 274162306a36Sopenharmony_ci /* 274262306a36Sopenharmony_ci Acquire exclusive access to Host Adapter. 274362306a36Sopenharmony_ci */ 274462306a36Sopenharmony_ci spin_lock_irqsave(adapter->scsi_host->host_lock, processor_flag); 274562306a36Sopenharmony_ci /* 274662306a36Sopenharmony_ci Handle Interrupts appropriately for each Host Adapter type. 274762306a36Sopenharmony_ci */ 274862306a36Sopenharmony_ci if (blogic_multimaster_type(adapter)) { 274962306a36Sopenharmony_ci union blogic_int_reg intreg; 275062306a36Sopenharmony_ci /* 275162306a36Sopenharmony_ci Read the Host Adapter Interrupt Register. 275262306a36Sopenharmony_ci */ 275362306a36Sopenharmony_ci intreg.all = blogic_rdint(adapter); 275462306a36Sopenharmony_ci if (intreg.ir.int_valid) { 275562306a36Sopenharmony_ci /* 275662306a36Sopenharmony_ci Acknowledge the interrupt and reset the Host Adapter 275762306a36Sopenharmony_ci Interrupt Register. 275862306a36Sopenharmony_ci */ 275962306a36Sopenharmony_ci blogic_intreset(adapter); 276062306a36Sopenharmony_ci /* 276162306a36Sopenharmony_ci Process valid External SCSI Bus Reset and Incoming 276262306a36Sopenharmony_ci Mailbox Loaded Interrupts. Command Complete 276362306a36Sopenharmony_ci Interrupts are noted, and Outgoing Mailbox Available 276462306a36Sopenharmony_ci Interrupts are ignored, as they are never enabled. 276562306a36Sopenharmony_ci */ 276662306a36Sopenharmony_ci if (intreg.ir.ext_busreset) 276762306a36Sopenharmony_ci adapter->adapter_extreset = true; 276862306a36Sopenharmony_ci else if (intreg.ir.mailin_loaded) 276962306a36Sopenharmony_ci blogic_scan_inbox(adapter); 277062306a36Sopenharmony_ci else if (intreg.ir.cmd_complete) 277162306a36Sopenharmony_ci adapter->adapter_cmd_complete = true; 277262306a36Sopenharmony_ci } 277362306a36Sopenharmony_ci } else { 277462306a36Sopenharmony_ci /* 277562306a36Sopenharmony_ci Check if there is a pending interrupt for this Host Adapter. 277662306a36Sopenharmony_ci */ 277762306a36Sopenharmony_ci if (FlashPoint_InterruptPending(adapter->cardhandle)) 277862306a36Sopenharmony_ci switch (FlashPoint_HandleInterrupt(adapter->cardhandle)) { 277962306a36Sopenharmony_ci case FPOINT_NORMAL_INT: 278062306a36Sopenharmony_ci break; 278162306a36Sopenharmony_ci case FPOINT_EXT_RESET: 278262306a36Sopenharmony_ci adapter->adapter_extreset = true; 278362306a36Sopenharmony_ci break; 278462306a36Sopenharmony_ci case FPOINT_INTERN_ERR: 278562306a36Sopenharmony_ci blogic_warn("Internal FlashPoint Error detected - Resetting Host Adapter\n", adapter); 278662306a36Sopenharmony_ci adapter->adapter_intern_err = true; 278762306a36Sopenharmony_ci break; 278862306a36Sopenharmony_ci } 278962306a36Sopenharmony_ci } 279062306a36Sopenharmony_ci /* 279162306a36Sopenharmony_ci Process any completed CCBs. 279262306a36Sopenharmony_ci */ 279362306a36Sopenharmony_ci if (adapter->firstccb != NULL) 279462306a36Sopenharmony_ci blogic_process_ccbs(adapter); 279562306a36Sopenharmony_ci /* 279662306a36Sopenharmony_ci Reset the Host Adapter if requested. 279762306a36Sopenharmony_ci */ 279862306a36Sopenharmony_ci if (adapter->adapter_extreset) { 279962306a36Sopenharmony_ci blogic_warn("Resetting %s due to External SCSI Bus Reset\n", adapter, adapter->full_model); 280062306a36Sopenharmony_ci blogic_inc_count(&adapter->ext_resets); 280162306a36Sopenharmony_ci blogic_resetadapter(adapter, false); 280262306a36Sopenharmony_ci adapter->adapter_extreset = false; 280362306a36Sopenharmony_ci } else if (adapter->adapter_intern_err) { 280462306a36Sopenharmony_ci blogic_warn("Resetting %s due to Host Adapter Internal Error\n", adapter, adapter->full_model); 280562306a36Sopenharmony_ci blogic_inc_count(&adapter->adapter_intern_errors); 280662306a36Sopenharmony_ci blogic_resetadapter(adapter, true); 280762306a36Sopenharmony_ci adapter->adapter_intern_err = false; 280862306a36Sopenharmony_ci } 280962306a36Sopenharmony_ci /* 281062306a36Sopenharmony_ci Release exclusive access to Host Adapter. 281162306a36Sopenharmony_ci */ 281262306a36Sopenharmony_ci spin_unlock_irqrestore(adapter->scsi_host->host_lock, processor_flag); 281362306a36Sopenharmony_ci return IRQ_HANDLED; 281462306a36Sopenharmony_ci} 281562306a36Sopenharmony_ci 281662306a36Sopenharmony_ci 281762306a36Sopenharmony_ci/* 281862306a36Sopenharmony_ci blogic_write_outbox places CCB and Action Code into an Outgoing 281962306a36Sopenharmony_ci Mailbox for execution by Host Adapter. The Host Adapter's Lock should 282062306a36Sopenharmony_ci already have been acquired by the caller. 282162306a36Sopenharmony_ci*/ 282262306a36Sopenharmony_ci 282362306a36Sopenharmony_cistatic bool blogic_write_outbox(struct blogic_adapter *adapter, 282462306a36Sopenharmony_ci enum blogic_action action, struct blogic_ccb *ccb) 282562306a36Sopenharmony_ci{ 282662306a36Sopenharmony_ci struct blogic_outbox *next_outbox; 282762306a36Sopenharmony_ci 282862306a36Sopenharmony_ci next_outbox = adapter->next_outbox; 282962306a36Sopenharmony_ci if (next_outbox->action == BLOGIC_OUTBOX_FREE) { 283062306a36Sopenharmony_ci ccb->status = BLOGIC_CCB_ACTIVE; 283162306a36Sopenharmony_ci /* 283262306a36Sopenharmony_ci The CCB field must be written before the Action Code field 283362306a36Sopenharmony_ci since the Host Adapter is operating asynchronously and the 283462306a36Sopenharmony_ci locking code does not protect against simultaneous access 283562306a36Sopenharmony_ci by the Host Adapter. 283662306a36Sopenharmony_ci */ 283762306a36Sopenharmony_ci next_outbox->ccb = ccb->dma_handle; 283862306a36Sopenharmony_ci next_outbox->action = action; 283962306a36Sopenharmony_ci blogic_execmbox(adapter); 284062306a36Sopenharmony_ci if (++next_outbox > adapter->last_outbox) 284162306a36Sopenharmony_ci next_outbox = adapter->first_outbox; 284262306a36Sopenharmony_ci adapter->next_outbox = next_outbox; 284362306a36Sopenharmony_ci if (action == BLOGIC_MBOX_START) { 284462306a36Sopenharmony_ci adapter->active_cmds[ccb->tgt_id]++; 284562306a36Sopenharmony_ci if (ccb->opcode != BLOGIC_BDR) 284662306a36Sopenharmony_ci adapter->tgt_stats[ccb->tgt_id].cmds_tried++; 284762306a36Sopenharmony_ci } 284862306a36Sopenharmony_ci return true; 284962306a36Sopenharmony_ci } 285062306a36Sopenharmony_ci return false; 285162306a36Sopenharmony_ci} 285262306a36Sopenharmony_ci 285362306a36Sopenharmony_ci/* Error Handling (EH) support */ 285462306a36Sopenharmony_ci 285562306a36Sopenharmony_cistatic int blogic_hostreset(struct scsi_cmnd *SCpnt) 285662306a36Sopenharmony_ci{ 285762306a36Sopenharmony_ci struct blogic_adapter *adapter = 285862306a36Sopenharmony_ci (struct blogic_adapter *) SCpnt->device->host->hostdata; 285962306a36Sopenharmony_ci 286062306a36Sopenharmony_ci unsigned int id = SCpnt->device->id; 286162306a36Sopenharmony_ci struct blogic_tgt_stats *stats = &adapter->tgt_stats[id]; 286262306a36Sopenharmony_ci int rc; 286362306a36Sopenharmony_ci 286462306a36Sopenharmony_ci spin_lock_irq(SCpnt->device->host->host_lock); 286562306a36Sopenharmony_ci 286662306a36Sopenharmony_ci blogic_inc_count(&stats->adapter_reset_req); 286762306a36Sopenharmony_ci 286862306a36Sopenharmony_ci rc = blogic_resetadapter(adapter, false); 286962306a36Sopenharmony_ci spin_unlock_irq(SCpnt->device->host->host_lock); 287062306a36Sopenharmony_ci return rc; 287162306a36Sopenharmony_ci} 287262306a36Sopenharmony_ci 287362306a36Sopenharmony_ci/* 287462306a36Sopenharmony_ci blogic_qcmd creates a CCB for Command and places it into an 287562306a36Sopenharmony_ci Outgoing Mailbox for execution by the associated Host Adapter. 287662306a36Sopenharmony_ci*/ 287762306a36Sopenharmony_ci 287862306a36Sopenharmony_cistatic int blogic_qcmd_lck(struct scsi_cmnd *command) 287962306a36Sopenharmony_ci{ 288062306a36Sopenharmony_ci void (*comp_cb)(struct scsi_cmnd *) = scsi_done; 288162306a36Sopenharmony_ci struct blogic_adapter *adapter = 288262306a36Sopenharmony_ci (struct blogic_adapter *) command->device->host->hostdata; 288362306a36Sopenharmony_ci struct blogic_tgt_flags *tgt_flags = 288462306a36Sopenharmony_ci &adapter->tgt_flags[command->device->id]; 288562306a36Sopenharmony_ci struct blogic_tgt_stats *tgt_stats = adapter->tgt_stats; 288662306a36Sopenharmony_ci unsigned char *cdb = command->cmnd; 288762306a36Sopenharmony_ci int cdblen = command->cmd_len; 288862306a36Sopenharmony_ci int tgt_id = command->device->id; 288962306a36Sopenharmony_ci int lun = command->device->lun; 289062306a36Sopenharmony_ci int buflen = scsi_bufflen(command); 289162306a36Sopenharmony_ci int count; 289262306a36Sopenharmony_ci struct blogic_ccb *ccb; 289362306a36Sopenharmony_ci dma_addr_t sense_buf; 289462306a36Sopenharmony_ci 289562306a36Sopenharmony_ci /* 289662306a36Sopenharmony_ci SCSI REQUEST_SENSE commands will be executed automatically by the 289762306a36Sopenharmony_ci Host Adapter for any errors, so they should not be executed 289862306a36Sopenharmony_ci explicitly unless the Sense Data is zero indicating that no error 289962306a36Sopenharmony_ci occurred. 290062306a36Sopenharmony_ci */ 290162306a36Sopenharmony_ci if (cdb[0] == REQUEST_SENSE && command->sense_buffer[0] != 0) { 290262306a36Sopenharmony_ci command->result = DID_OK << 16; 290362306a36Sopenharmony_ci comp_cb(command); 290462306a36Sopenharmony_ci return 0; 290562306a36Sopenharmony_ci } 290662306a36Sopenharmony_ci /* 290762306a36Sopenharmony_ci Allocate a CCB from the Host Adapter's free list. In the unlikely 290862306a36Sopenharmony_ci event that there are none available and memory allocation fails, 290962306a36Sopenharmony_ci wait 1 second and try again. If that fails, the Host Adapter is 291062306a36Sopenharmony_ci probably hung so signal an error as a Host Adapter Hard Reset 291162306a36Sopenharmony_ci should be initiated soon. 291262306a36Sopenharmony_ci */ 291362306a36Sopenharmony_ci ccb = blogic_alloc_ccb(adapter); 291462306a36Sopenharmony_ci if (ccb == NULL) { 291562306a36Sopenharmony_ci spin_unlock_irq(adapter->scsi_host->host_lock); 291662306a36Sopenharmony_ci blogic_delay(1); 291762306a36Sopenharmony_ci spin_lock_irq(adapter->scsi_host->host_lock); 291862306a36Sopenharmony_ci ccb = blogic_alloc_ccb(adapter); 291962306a36Sopenharmony_ci if (ccb == NULL) { 292062306a36Sopenharmony_ci command->result = DID_ERROR << 16; 292162306a36Sopenharmony_ci comp_cb(command); 292262306a36Sopenharmony_ci return 0; 292362306a36Sopenharmony_ci } 292462306a36Sopenharmony_ci } 292562306a36Sopenharmony_ci 292662306a36Sopenharmony_ci /* 292762306a36Sopenharmony_ci Initialize the fields in the BusLogic Command Control Block (CCB). 292862306a36Sopenharmony_ci */ 292962306a36Sopenharmony_ci count = scsi_dma_map(command); 293062306a36Sopenharmony_ci BUG_ON(count < 0); 293162306a36Sopenharmony_ci if (count) { 293262306a36Sopenharmony_ci struct scatterlist *sg; 293362306a36Sopenharmony_ci int i; 293462306a36Sopenharmony_ci 293562306a36Sopenharmony_ci ccb->opcode = BLOGIC_INITIATOR_CCB_SG; 293662306a36Sopenharmony_ci ccb->datalen = count * sizeof(struct blogic_sg_seg); 293762306a36Sopenharmony_ci if (blogic_multimaster_type(adapter)) 293862306a36Sopenharmony_ci ccb->data = (unsigned int) ccb->dma_handle + 293962306a36Sopenharmony_ci ((unsigned long) &ccb->sglist - 294062306a36Sopenharmony_ci (unsigned long) ccb); 294162306a36Sopenharmony_ci else 294262306a36Sopenharmony_ci ccb->data = virt_to_32bit_virt(ccb->sglist); 294362306a36Sopenharmony_ci 294462306a36Sopenharmony_ci scsi_for_each_sg(command, sg, count, i) { 294562306a36Sopenharmony_ci ccb->sglist[i].segbytes = sg_dma_len(sg); 294662306a36Sopenharmony_ci ccb->sglist[i].segdata = sg_dma_address(sg); 294762306a36Sopenharmony_ci } 294862306a36Sopenharmony_ci } else if (!count) { 294962306a36Sopenharmony_ci ccb->opcode = BLOGIC_INITIATOR_CCB; 295062306a36Sopenharmony_ci ccb->datalen = buflen; 295162306a36Sopenharmony_ci ccb->data = 0; 295262306a36Sopenharmony_ci } 295362306a36Sopenharmony_ci 295462306a36Sopenharmony_ci switch (cdb[0]) { 295562306a36Sopenharmony_ci case READ_6: 295662306a36Sopenharmony_ci case READ_10: 295762306a36Sopenharmony_ci ccb->datadir = BLOGIC_DATAIN_CHECKED; 295862306a36Sopenharmony_ci tgt_stats[tgt_id].read_cmds++; 295962306a36Sopenharmony_ci blogic_addcount(&tgt_stats[tgt_id].bytesread, buflen); 296062306a36Sopenharmony_ci blogic_incszbucket(tgt_stats[tgt_id].read_sz_buckets, buflen); 296162306a36Sopenharmony_ci break; 296262306a36Sopenharmony_ci case WRITE_6: 296362306a36Sopenharmony_ci case WRITE_10: 296462306a36Sopenharmony_ci ccb->datadir = BLOGIC_DATAOUT_CHECKED; 296562306a36Sopenharmony_ci tgt_stats[tgt_id].write_cmds++; 296662306a36Sopenharmony_ci blogic_addcount(&tgt_stats[tgt_id].byteswritten, buflen); 296762306a36Sopenharmony_ci blogic_incszbucket(tgt_stats[tgt_id].write_sz_buckets, buflen); 296862306a36Sopenharmony_ci break; 296962306a36Sopenharmony_ci default: 297062306a36Sopenharmony_ci ccb->datadir = BLOGIC_UNCHECKED_TX; 297162306a36Sopenharmony_ci break; 297262306a36Sopenharmony_ci } 297362306a36Sopenharmony_ci ccb->cdblen = cdblen; 297462306a36Sopenharmony_ci ccb->adapter_status = 0; 297562306a36Sopenharmony_ci ccb->tgt_status = 0; 297662306a36Sopenharmony_ci ccb->tgt_id = tgt_id; 297762306a36Sopenharmony_ci ccb->lun = lun; 297862306a36Sopenharmony_ci ccb->tag_enable = false; 297962306a36Sopenharmony_ci ccb->legacytag_enable = false; 298062306a36Sopenharmony_ci /* 298162306a36Sopenharmony_ci BusLogic recommends that after a Reset the first couple of 298262306a36Sopenharmony_ci commands that are sent to a Target Device be sent in a non 298362306a36Sopenharmony_ci Tagged Queue fashion so that the Host Adapter and Target Device 298462306a36Sopenharmony_ci can establish Synchronous and Wide Transfer before Queue Tag 298562306a36Sopenharmony_ci messages can interfere with the Synchronous and Wide Negotiation 298662306a36Sopenharmony_ci messages. By waiting to enable Tagged Queuing until after the 298762306a36Sopenharmony_ci first BLOGIC_MAX_TAG_DEPTH commands have been queued, it is 298862306a36Sopenharmony_ci assured that after a Reset any pending commands are requeued 298962306a36Sopenharmony_ci before Tagged Queuing is enabled and that the Tagged Queuing 299062306a36Sopenharmony_ci message will not occur while the partition table is being printed. 299162306a36Sopenharmony_ci In addition, some devices do not properly handle the transition 299262306a36Sopenharmony_ci from non-tagged to tagged commands, so it is necessary to wait 299362306a36Sopenharmony_ci until there are no pending commands for a target device 299462306a36Sopenharmony_ci before queuing tagged commands. 299562306a36Sopenharmony_ci */ 299662306a36Sopenharmony_ci if (adapter->cmds_since_rst[tgt_id]++ >= BLOGIC_MAX_TAG_DEPTH && 299762306a36Sopenharmony_ci !tgt_flags->tagq_active && 299862306a36Sopenharmony_ci adapter->active_cmds[tgt_id] == 0 299962306a36Sopenharmony_ci && tgt_flags->tagq_ok && 300062306a36Sopenharmony_ci (adapter->tagq_ok & (1 << tgt_id))) { 300162306a36Sopenharmony_ci tgt_flags->tagq_active = true; 300262306a36Sopenharmony_ci blogic_notice("Tagged Queuing now active for Target %d\n", 300362306a36Sopenharmony_ci adapter, tgt_id); 300462306a36Sopenharmony_ci } 300562306a36Sopenharmony_ci if (tgt_flags->tagq_active) { 300662306a36Sopenharmony_ci enum blogic_queuetag queuetag = BLOGIC_SIMPLETAG; 300762306a36Sopenharmony_ci /* 300862306a36Sopenharmony_ci When using Tagged Queuing with Simple Queue Tags, it 300962306a36Sopenharmony_ci appears that disk drive controllers do not guarantee that 301062306a36Sopenharmony_ci a queued command will not remain in a disconnected state 301162306a36Sopenharmony_ci indefinitely if commands that read or write nearer the 301262306a36Sopenharmony_ci head position continue to arrive without interruption. 301362306a36Sopenharmony_ci Therefore, for each Target Device this driver keeps track 301462306a36Sopenharmony_ci of the last time either the queue was empty or an Ordered 301562306a36Sopenharmony_ci Queue Tag was issued. If more than 4 seconds (one fifth 301662306a36Sopenharmony_ci of the 20 second disk timeout) have elapsed since this 301762306a36Sopenharmony_ci last sequence point, this command will be issued with an 301862306a36Sopenharmony_ci Ordered Queue Tag rather than a Simple Queue Tag, which 301962306a36Sopenharmony_ci forces the Target Device to complete all previously 302062306a36Sopenharmony_ci queued commands before this command may be executed. 302162306a36Sopenharmony_ci */ 302262306a36Sopenharmony_ci if (adapter->active_cmds[tgt_id] == 0) 302362306a36Sopenharmony_ci adapter->last_seqpoint[tgt_id] = jiffies; 302462306a36Sopenharmony_ci else if (time_after(jiffies, 302562306a36Sopenharmony_ci adapter->last_seqpoint[tgt_id] + 4 * HZ)) { 302662306a36Sopenharmony_ci adapter->last_seqpoint[tgt_id] = jiffies; 302762306a36Sopenharmony_ci queuetag = BLOGIC_ORDEREDTAG; 302862306a36Sopenharmony_ci } 302962306a36Sopenharmony_ci if (adapter->ext_lun) { 303062306a36Sopenharmony_ci ccb->tag_enable = true; 303162306a36Sopenharmony_ci ccb->queuetag = queuetag; 303262306a36Sopenharmony_ci } else { 303362306a36Sopenharmony_ci ccb->legacytag_enable = true; 303462306a36Sopenharmony_ci ccb->legacy_tag = queuetag; 303562306a36Sopenharmony_ci } 303662306a36Sopenharmony_ci } 303762306a36Sopenharmony_ci memcpy(ccb->cdb, cdb, cdblen); 303862306a36Sopenharmony_ci ccb->sense_datalen = SCSI_SENSE_BUFFERSIZE; 303962306a36Sopenharmony_ci ccb->command = command; 304062306a36Sopenharmony_ci sense_buf = dma_map_single(&adapter->pci_device->dev, 304162306a36Sopenharmony_ci command->sense_buffer, ccb->sense_datalen, 304262306a36Sopenharmony_ci DMA_FROM_DEVICE); 304362306a36Sopenharmony_ci if (dma_mapping_error(&adapter->pci_device->dev, sense_buf)) { 304462306a36Sopenharmony_ci blogic_err("DMA mapping for sense data buffer failed\n", 304562306a36Sopenharmony_ci adapter); 304662306a36Sopenharmony_ci blogic_dealloc_ccb(ccb, 0); 304762306a36Sopenharmony_ci return SCSI_MLQUEUE_HOST_BUSY; 304862306a36Sopenharmony_ci } 304962306a36Sopenharmony_ci ccb->sensedata = sense_buf; 305062306a36Sopenharmony_ci if (blogic_multimaster_type(adapter)) { 305162306a36Sopenharmony_ci /* 305262306a36Sopenharmony_ci Place the CCB in an Outgoing Mailbox. The higher levels 305362306a36Sopenharmony_ci of the SCSI Subsystem should not attempt to queue more 305462306a36Sopenharmony_ci commands than can be placed in Outgoing Mailboxes, so 305562306a36Sopenharmony_ci there should always be one free. In the unlikely event 305662306a36Sopenharmony_ci that there are none available, wait 1 second and try 305762306a36Sopenharmony_ci again. If that fails, the Host Adapter is probably hung 305862306a36Sopenharmony_ci so signal an error as a Host Adapter Hard Reset should 305962306a36Sopenharmony_ci be initiated soon. 306062306a36Sopenharmony_ci */ 306162306a36Sopenharmony_ci if (!blogic_write_outbox(adapter, BLOGIC_MBOX_START, ccb)) { 306262306a36Sopenharmony_ci spin_unlock_irq(adapter->scsi_host->host_lock); 306362306a36Sopenharmony_ci blogic_warn("Unable to write Outgoing Mailbox - Pausing for 1 second\n", adapter); 306462306a36Sopenharmony_ci blogic_delay(1); 306562306a36Sopenharmony_ci spin_lock_irq(adapter->scsi_host->host_lock); 306662306a36Sopenharmony_ci if (!blogic_write_outbox(adapter, BLOGIC_MBOX_START, 306762306a36Sopenharmony_ci ccb)) { 306862306a36Sopenharmony_ci blogic_warn("Still unable to write Outgoing Mailbox - Host Adapter Dead?\n", adapter); 306962306a36Sopenharmony_ci blogic_dealloc_ccb(ccb, 1); 307062306a36Sopenharmony_ci command->result = DID_ERROR << 16; 307162306a36Sopenharmony_ci scsi_done(command); 307262306a36Sopenharmony_ci } 307362306a36Sopenharmony_ci } 307462306a36Sopenharmony_ci } else { 307562306a36Sopenharmony_ci /* 307662306a36Sopenharmony_ci Call the FlashPoint SCCB Manager to start execution of 307762306a36Sopenharmony_ci the CCB. 307862306a36Sopenharmony_ci */ 307962306a36Sopenharmony_ci ccb->status = BLOGIC_CCB_ACTIVE; 308062306a36Sopenharmony_ci adapter->active_cmds[tgt_id]++; 308162306a36Sopenharmony_ci tgt_stats[tgt_id].cmds_tried++; 308262306a36Sopenharmony_ci FlashPoint_StartCCB(adapter->cardhandle, ccb); 308362306a36Sopenharmony_ci /* 308462306a36Sopenharmony_ci The Command may have already completed and 308562306a36Sopenharmony_ci blogic_qcompleted_ccb been called, or it may still be 308662306a36Sopenharmony_ci pending. 308762306a36Sopenharmony_ci */ 308862306a36Sopenharmony_ci if (ccb->status == BLOGIC_CCB_COMPLETE) 308962306a36Sopenharmony_ci blogic_process_ccbs(adapter); 309062306a36Sopenharmony_ci } 309162306a36Sopenharmony_ci return 0; 309262306a36Sopenharmony_ci} 309362306a36Sopenharmony_ci 309462306a36Sopenharmony_cistatic DEF_SCSI_QCMD(blogic_qcmd) 309562306a36Sopenharmony_ci 309662306a36Sopenharmony_ci#if 0 309762306a36Sopenharmony_ci/* 309862306a36Sopenharmony_ci blogic_abort aborts Command if possible. 309962306a36Sopenharmony_ci*/ 310062306a36Sopenharmony_ci 310162306a36Sopenharmony_cistatic int blogic_abort(struct scsi_cmnd *command) 310262306a36Sopenharmony_ci{ 310362306a36Sopenharmony_ci struct blogic_adapter *adapter = 310462306a36Sopenharmony_ci (struct blogic_adapter *) command->device->host->hostdata; 310562306a36Sopenharmony_ci 310662306a36Sopenharmony_ci int tgt_id = command->device->id; 310762306a36Sopenharmony_ci struct blogic_ccb *ccb; 310862306a36Sopenharmony_ci blogic_inc_count(&adapter->tgt_stats[tgt_id].aborts_request); 310962306a36Sopenharmony_ci 311062306a36Sopenharmony_ci /* 311162306a36Sopenharmony_ci Attempt to find an Active CCB for this Command. If no Active 311262306a36Sopenharmony_ci CCB for this Command is found, then no Abort is necessary. 311362306a36Sopenharmony_ci */ 311462306a36Sopenharmony_ci for (ccb = adapter->all_ccbs; ccb != NULL; ccb = ccb->next_all) 311562306a36Sopenharmony_ci if (ccb->command == command) 311662306a36Sopenharmony_ci break; 311762306a36Sopenharmony_ci if (ccb == NULL) { 311862306a36Sopenharmony_ci blogic_warn("Unable to Abort Command to Target %d - No CCB Found\n", adapter, tgt_id); 311962306a36Sopenharmony_ci return SUCCESS; 312062306a36Sopenharmony_ci } else if (ccb->status == BLOGIC_CCB_COMPLETE) { 312162306a36Sopenharmony_ci blogic_warn("Unable to Abort Command to Target %d - CCB Completed\n", adapter, tgt_id); 312262306a36Sopenharmony_ci return SUCCESS; 312362306a36Sopenharmony_ci } else if (ccb->status == BLOGIC_CCB_RESET) { 312462306a36Sopenharmony_ci blogic_warn("Unable to Abort Command to Target %d - CCB Reset\n", adapter, tgt_id); 312562306a36Sopenharmony_ci return SUCCESS; 312662306a36Sopenharmony_ci } 312762306a36Sopenharmony_ci if (blogic_multimaster_type(adapter)) { 312862306a36Sopenharmony_ci /* 312962306a36Sopenharmony_ci Attempt to Abort this CCB. MultiMaster Firmware versions 313062306a36Sopenharmony_ci prior to 5.xx do not generate Abort Tag messages, but only 313162306a36Sopenharmony_ci generate the non-tagged Abort message. Since non-tagged 313262306a36Sopenharmony_ci commands are not sent by the Host Adapter until the queue 313362306a36Sopenharmony_ci of outstanding tagged commands has completed, and the 313462306a36Sopenharmony_ci Abort message is treated as a non-tagged command, it is 313562306a36Sopenharmony_ci effectively impossible to abort commands when Tagged 313662306a36Sopenharmony_ci Queuing is active. Firmware version 5.xx does generate 313762306a36Sopenharmony_ci Abort Tag messages, so it is possible to abort commands 313862306a36Sopenharmony_ci when Tagged Queuing is active. 313962306a36Sopenharmony_ci */ 314062306a36Sopenharmony_ci if (adapter->tgt_flags[tgt_id].tagq_active && 314162306a36Sopenharmony_ci adapter->fw_ver[0] < '5') { 314262306a36Sopenharmony_ci blogic_warn("Unable to Abort CCB #%ld to Target %d - Abort Tag Not Supported\n", adapter, ccb->serial, tgt_id); 314362306a36Sopenharmony_ci return FAILURE; 314462306a36Sopenharmony_ci } else if (blogic_write_outbox(adapter, BLOGIC_MBOX_ABORT, 314562306a36Sopenharmony_ci ccb)) { 314662306a36Sopenharmony_ci blogic_warn("Aborting CCB #%ld to Target %d\n", 314762306a36Sopenharmony_ci adapter, ccb->serial, tgt_id); 314862306a36Sopenharmony_ci blogic_inc_count(&adapter->tgt_stats[tgt_id].aborts_tried); 314962306a36Sopenharmony_ci return SUCCESS; 315062306a36Sopenharmony_ci } else { 315162306a36Sopenharmony_ci blogic_warn("Unable to Abort CCB #%ld to Target %d - No Outgoing Mailboxes\n", adapter, ccb->serial, tgt_id); 315262306a36Sopenharmony_ci return FAILURE; 315362306a36Sopenharmony_ci } 315462306a36Sopenharmony_ci } else { 315562306a36Sopenharmony_ci /* 315662306a36Sopenharmony_ci Call the FlashPoint SCCB Manager to abort execution of 315762306a36Sopenharmony_ci the CCB. 315862306a36Sopenharmony_ci */ 315962306a36Sopenharmony_ci blogic_warn("Aborting CCB #%ld to Target %d\n", adapter, 316062306a36Sopenharmony_ci ccb->serial, tgt_id); 316162306a36Sopenharmony_ci blogic_inc_count(&adapter->tgt_stats[tgt_id].aborts_tried); 316262306a36Sopenharmony_ci FlashPoint_AbortCCB(adapter->cardhandle, ccb); 316362306a36Sopenharmony_ci /* 316462306a36Sopenharmony_ci The Abort may have already been completed and 316562306a36Sopenharmony_ci blogic_qcompleted_ccb been called, or it 316662306a36Sopenharmony_ci may still be pending. 316762306a36Sopenharmony_ci */ 316862306a36Sopenharmony_ci if (ccb->status == BLOGIC_CCB_COMPLETE) 316962306a36Sopenharmony_ci blogic_process_ccbs(adapter); 317062306a36Sopenharmony_ci return SUCCESS; 317162306a36Sopenharmony_ci } 317262306a36Sopenharmony_ci return SUCCESS; 317362306a36Sopenharmony_ci} 317462306a36Sopenharmony_ci 317562306a36Sopenharmony_ci#endif 317662306a36Sopenharmony_ci/* 317762306a36Sopenharmony_ci blogic_resetadapter resets Host Adapter if possible, marking all 317862306a36Sopenharmony_ci currently executing SCSI Commands as having been Reset. 317962306a36Sopenharmony_ci*/ 318062306a36Sopenharmony_ci 318162306a36Sopenharmony_cistatic int blogic_resetadapter(struct blogic_adapter *adapter, bool hard_reset) 318262306a36Sopenharmony_ci{ 318362306a36Sopenharmony_ci struct blogic_ccb *ccb; 318462306a36Sopenharmony_ci int tgt_id; 318562306a36Sopenharmony_ci 318662306a36Sopenharmony_ci /* 318762306a36Sopenharmony_ci * Attempt to Reset and Reinitialize the Host Adapter. 318862306a36Sopenharmony_ci */ 318962306a36Sopenharmony_ci 319062306a36Sopenharmony_ci if (!(blogic_hwreset(adapter, hard_reset) && 319162306a36Sopenharmony_ci blogic_initadapter(adapter))) { 319262306a36Sopenharmony_ci blogic_err("Resetting %s Failed\n", adapter, 319362306a36Sopenharmony_ci adapter->full_model); 319462306a36Sopenharmony_ci return FAILURE; 319562306a36Sopenharmony_ci } 319662306a36Sopenharmony_ci 319762306a36Sopenharmony_ci /* 319862306a36Sopenharmony_ci * Deallocate all currently executing CCBs. 319962306a36Sopenharmony_ci */ 320062306a36Sopenharmony_ci 320162306a36Sopenharmony_ci for (ccb = adapter->all_ccbs; ccb != NULL; ccb = ccb->next_all) 320262306a36Sopenharmony_ci if (ccb->status == BLOGIC_CCB_ACTIVE) 320362306a36Sopenharmony_ci blogic_dealloc_ccb(ccb, 1); 320462306a36Sopenharmony_ci /* 320562306a36Sopenharmony_ci * Wait a few seconds between the Host Adapter Hard Reset which 320662306a36Sopenharmony_ci * initiates a SCSI Bus Reset and issuing any SCSI Commands. Some 320762306a36Sopenharmony_ci * SCSI devices get confused if they receive SCSI Commands too soon 320862306a36Sopenharmony_ci * after a SCSI Bus Reset. 320962306a36Sopenharmony_ci */ 321062306a36Sopenharmony_ci 321162306a36Sopenharmony_ci if (hard_reset) { 321262306a36Sopenharmony_ci spin_unlock_irq(adapter->scsi_host->host_lock); 321362306a36Sopenharmony_ci blogic_delay(adapter->bus_settle_time); 321462306a36Sopenharmony_ci spin_lock_irq(adapter->scsi_host->host_lock); 321562306a36Sopenharmony_ci } 321662306a36Sopenharmony_ci 321762306a36Sopenharmony_ci for (tgt_id = 0; tgt_id < adapter->maxdev; tgt_id++) { 321862306a36Sopenharmony_ci adapter->last_resettried[tgt_id] = jiffies; 321962306a36Sopenharmony_ci adapter->last_resetdone[tgt_id] = jiffies; 322062306a36Sopenharmony_ci } 322162306a36Sopenharmony_ci return SUCCESS; 322262306a36Sopenharmony_ci} 322362306a36Sopenharmony_ci 322462306a36Sopenharmony_ci/* 322562306a36Sopenharmony_ci blogic_diskparam returns the Heads/Sectors/Cylinders BIOS Disk 322662306a36Sopenharmony_ci Parameters for Disk. The default disk geometry is 64 heads, 32 sectors, and 322762306a36Sopenharmony_ci the appropriate number of cylinders so as not to exceed drive capacity. In 322862306a36Sopenharmony_ci order for disks equal to or larger than 1 GB to be addressable by the BIOS 322962306a36Sopenharmony_ci without exceeding the BIOS limitation of 1024 cylinders, Extended Translation 323062306a36Sopenharmony_ci may be enabled in AutoSCSI on FlashPoint Host Adapters and on "W" and "C" 323162306a36Sopenharmony_ci series MultiMaster Host Adapters, or by a dip switch setting on "S" and "A" 323262306a36Sopenharmony_ci series MultiMaster Host Adapters. With Extended Translation enabled, drives 323362306a36Sopenharmony_ci between 1 GB inclusive and 2 GB exclusive are given a disk geometry of 128 323462306a36Sopenharmony_ci heads and 32 sectors, and drives above 2 GB inclusive are given a disk 323562306a36Sopenharmony_ci geometry of 255 heads and 63 sectors. However, if the BIOS detects that the 323662306a36Sopenharmony_ci Extended Translation setting does not match the geometry in the partition 323762306a36Sopenharmony_ci table, then the translation inferred from the partition table will be used by 323862306a36Sopenharmony_ci the BIOS, and a warning may be displayed. 323962306a36Sopenharmony_ci*/ 324062306a36Sopenharmony_ci 324162306a36Sopenharmony_cistatic int blogic_diskparam(struct scsi_device *sdev, struct block_device *dev, 324262306a36Sopenharmony_ci sector_t capacity, int *params) 324362306a36Sopenharmony_ci{ 324462306a36Sopenharmony_ci struct blogic_adapter *adapter = 324562306a36Sopenharmony_ci (struct blogic_adapter *) sdev->host->hostdata; 324662306a36Sopenharmony_ci struct bios_diskparam *diskparam = (struct bios_diskparam *) params; 324762306a36Sopenharmony_ci unsigned char *buf; 324862306a36Sopenharmony_ci 324962306a36Sopenharmony_ci if (adapter->ext_trans_enable && capacity >= 2 * 1024 * 1024 /* 1 GB in 512 byte sectors */) { 325062306a36Sopenharmony_ci if (capacity >= 4 * 1024 * 1024 /* 2 GB in 512 byte sectors */) { 325162306a36Sopenharmony_ci diskparam->heads = 255; 325262306a36Sopenharmony_ci diskparam->sectors = 63; 325362306a36Sopenharmony_ci } else { 325462306a36Sopenharmony_ci diskparam->heads = 128; 325562306a36Sopenharmony_ci diskparam->sectors = 32; 325662306a36Sopenharmony_ci } 325762306a36Sopenharmony_ci } else { 325862306a36Sopenharmony_ci diskparam->heads = 64; 325962306a36Sopenharmony_ci diskparam->sectors = 32; 326062306a36Sopenharmony_ci } 326162306a36Sopenharmony_ci diskparam->cylinders = (unsigned long) capacity / (diskparam->heads * diskparam->sectors); 326262306a36Sopenharmony_ci buf = scsi_bios_ptable(dev); 326362306a36Sopenharmony_ci if (buf == NULL) 326462306a36Sopenharmony_ci return 0; 326562306a36Sopenharmony_ci /* 326662306a36Sopenharmony_ci If the boot sector partition table flag is valid, search for 326762306a36Sopenharmony_ci a partition table entry whose end_head matches one of the 326862306a36Sopenharmony_ci standard BusLogic geometry translations (64/32, 128/32, or 255/63). 326962306a36Sopenharmony_ci */ 327062306a36Sopenharmony_ci if (*(unsigned short *) (buf + 64) == MSDOS_LABEL_MAGIC) { 327162306a36Sopenharmony_ci struct msdos_partition *part1_entry = 327262306a36Sopenharmony_ci (struct msdos_partition *)buf; 327362306a36Sopenharmony_ci struct msdos_partition *part_entry = part1_entry; 327462306a36Sopenharmony_ci int saved_cyl = diskparam->cylinders, part_no; 327562306a36Sopenharmony_ci unsigned char part_end_head = 0, part_end_sector = 0; 327662306a36Sopenharmony_ci 327762306a36Sopenharmony_ci for (part_no = 0; part_no < 4; part_no++) { 327862306a36Sopenharmony_ci part_end_head = part_entry->end_head; 327962306a36Sopenharmony_ci part_end_sector = part_entry->end_sector & 0x3F; 328062306a36Sopenharmony_ci if (part_end_head == 64 - 1) { 328162306a36Sopenharmony_ci diskparam->heads = 64; 328262306a36Sopenharmony_ci diskparam->sectors = 32; 328362306a36Sopenharmony_ci break; 328462306a36Sopenharmony_ci } else if (part_end_head == 128 - 1) { 328562306a36Sopenharmony_ci diskparam->heads = 128; 328662306a36Sopenharmony_ci diskparam->sectors = 32; 328762306a36Sopenharmony_ci break; 328862306a36Sopenharmony_ci } else if (part_end_head == 255 - 1) { 328962306a36Sopenharmony_ci diskparam->heads = 255; 329062306a36Sopenharmony_ci diskparam->sectors = 63; 329162306a36Sopenharmony_ci break; 329262306a36Sopenharmony_ci } 329362306a36Sopenharmony_ci part_entry++; 329462306a36Sopenharmony_ci } 329562306a36Sopenharmony_ci if (part_no == 4) { 329662306a36Sopenharmony_ci part_end_head = part1_entry->end_head; 329762306a36Sopenharmony_ci part_end_sector = part1_entry->end_sector & 0x3F; 329862306a36Sopenharmony_ci } 329962306a36Sopenharmony_ci diskparam->cylinders = (unsigned long) capacity / (diskparam->heads * diskparam->sectors); 330062306a36Sopenharmony_ci if (part_no < 4 && part_end_sector == diskparam->sectors) { 330162306a36Sopenharmony_ci if (diskparam->cylinders != saved_cyl) 330262306a36Sopenharmony_ci blogic_warn("Adopting Geometry %d/%d from Partition Table\n", adapter, diskparam->heads, diskparam->sectors); 330362306a36Sopenharmony_ci } else if (part_end_head > 0 || part_end_sector > 0) { 330462306a36Sopenharmony_ci blogic_warn("Warning: Partition Table appears to have Geometry %d/%d which is\n", adapter, part_end_head + 1, part_end_sector); 330562306a36Sopenharmony_ci blogic_warn("not compatible with current BusLogic Host Adapter Geometry %d/%d\n", adapter, diskparam->heads, diskparam->sectors); 330662306a36Sopenharmony_ci } 330762306a36Sopenharmony_ci } 330862306a36Sopenharmony_ci kfree(buf); 330962306a36Sopenharmony_ci return 0; 331062306a36Sopenharmony_ci} 331162306a36Sopenharmony_ci 331262306a36Sopenharmony_ci 331362306a36Sopenharmony_ci/* 331462306a36Sopenharmony_ci BugLogic_ProcDirectoryInfo implements /proc/scsi/BusLogic/<N>. 331562306a36Sopenharmony_ci*/ 331662306a36Sopenharmony_ci 331762306a36Sopenharmony_cistatic int blogic_write_info(struct Scsi_Host *shost, char *procbuf, 331862306a36Sopenharmony_ci int bytes_avail) 331962306a36Sopenharmony_ci{ 332062306a36Sopenharmony_ci struct blogic_adapter *adapter = 332162306a36Sopenharmony_ci (struct blogic_adapter *) shost->hostdata; 332262306a36Sopenharmony_ci struct blogic_tgt_stats *tgt_stats; 332362306a36Sopenharmony_ci 332462306a36Sopenharmony_ci tgt_stats = adapter->tgt_stats; 332562306a36Sopenharmony_ci adapter->ext_resets = 0; 332662306a36Sopenharmony_ci adapter->adapter_intern_errors = 0; 332762306a36Sopenharmony_ci memset(tgt_stats, 0, BLOGIC_MAXDEV * sizeof(struct blogic_tgt_stats)); 332862306a36Sopenharmony_ci return 0; 332962306a36Sopenharmony_ci} 333062306a36Sopenharmony_ci 333162306a36Sopenharmony_cistatic int blogic_show_info(struct seq_file *m, struct Scsi_Host *shost) 333262306a36Sopenharmony_ci{ 333362306a36Sopenharmony_ci struct blogic_adapter *adapter = (struct blogic_adapter *) shost->hostdata; 333462306a36Sopenharmony_ci struct blogic_tgt_stats *tgt_stats; 333562306a36Sopenharmony_ci int tgt; 333662306a36Sopenharmony_ci 333762306a36Sopenharmony_ci tgt_stats = adapter->tgt_stats; 333862306a36Sopenharmony_ci seq_write(m, adapter->msgbuf, adapter->msgbuflen); 333962306a36Sopenharmony_ci seq_printf(m, "\n\ 334062306a36Sopenharmony_ciCurrent Driver Queue Depth: %d\n\ 334162306a36Sopenharmony_ciCurrently Allocated CCBs: %d\n", adapter->drvr_qdepth, adapter->alloc_ccbs); 334262306a36Sopenharmony_ci seq_puts(m, "\n\n\ 334362306a36Sopenharmony_ci DATA TRANSFER STATISTICS\n\ 334462306a36Sopenharmony_ci\n\ 334562306a36Sopenharmony_ciTarget Tagged Queuing Queue Depth Active Attempted Completed\n\ 334662306a36Sopenharmony_ci====== ============== =========== ====== ========= =========\n"); 334762306a36Sopenharmony_ci for (tgt = 0; tgt < adapter->maxdev; tgt++) { 334862306a36Sopenharmony_ci struct blogic_tgt_flags *tgt_flags = &adapter->tgt_flags[tgt]; 334962306a36Sopenharmony_ci if (!tgt_flags->tgt_exists) 335062306a36Sopenharmony_ci continue; 335162306a36Sopenharmony_ci seq_printf(m, " %2d %s", tgt, (tgt_flags->tagq_ok ? (tgt_flags->tagq_active ? " Active" : (adapter->tagq_ok & (1 << tgt) 335262306a36Sopenharmony_ci ? " Permitted" : " Disabled")) 335362306a36Sopenharmony_ci : "Not Supported")); 335462306a36Sopenharmony_ci seq_printf(m, 335562306a36Sopenharmony_ci " %3d %3u %9u %9u\n", adapter->qdepth[tgt], adapter->active_cmds[tgt], tgt_stats[tgt].cmds_tried, tgt_stats[tgt].cmds_complete); 335662306a36Sopenharmony_ci } 335762306a36Sopenharmony_ci seq_puts(m, "\n\ 335862306a36Sopenharmony_ciTarget Read Commands Write Commands Total Bytes Read Total Bytes Written\n\ 335962306a36Sopenharmony_ci====== ============= ============== =================== ===================\n"); 336062306a36Sopenharmony_ci for (tgt = 0; tgt < adapter->maxdev; tgt++) { 336162306a36Sopenharmony_ci struct blogic_tgt_flags *tgt_flags = &adapter->tgt_flags[tgt]; 336262306a36Sopenharmony_ci if (!tgt_flags->tgt_exists) 336362306a36Sopenharmony_ci continue; 336462306a36Sopenharmony_ci seq_printf(m, " %2d %9u %9u", tgt, tgt_stats[tgt].read_cmds, tgt_stats[tgt].write_cmds); 336562306a36Sopenharmony_ci if (tgt_stats[tgt].bytesread.billions > 0) 336662306a36Sopenharmony_ci seq_printf(m, " %9u%09u", tgt_stats[tgt].bytesread.billions, tgt_stats[tgt].bytesread.units); 336762306a36Sopenharmony_ci else 336862306a36Sopenharmony_ci seq_printf(m, " %9u", tgt_stats[tgt].bytesread.units); 336962306a36Sopenharmony_ci if (tgt_stats[tgt].byteswritten.billions > 0) 337062306a36Sopenharmony_ci seq_printf(m, " %9u%09u\n", tgt_stats[tgt].byteswritten.billions, tgt_stats[tgt].byteswritten.units); 337162306a36Sopenharmony_ci else 337262306a36Sopenharmony_ci seq_printf(m, " %9u\n", tgt_stats[tgt].byteswritten.units); 337362306a36Sopenharmony_ci } 337462306a36Sopenharmony_ci seq_puts(m, "\n\ 337562306a36Sopenharmony_ciTarget Command 0-1KB 1-2KB 2-4KB 4-8KB 8-16KB\n\ 337662306a36Sopenharmony_ci====== ======= ========= ========= ========= ========= =========\n"); 337762306a36Sopenharmony_ci for (tgt = 0; tgt < adapter->maxdev; tgt++) { 337862306a36Sopenharmony_ci struct blogic_tgt_flags *tgt_flags = &adapter->tgt_flags[tgt]; 337962306a36Sopenharmony_ci if (!tgt_flags->tgt_exists) 338062306a36Sopenharmony_ci continue; 338162306a36Sopenharmony_ci seq_printf(m, 338262306a36Sopenharmony_ci " %2d Read %9u %9u %9u %9u %9u\n", tgt, 338362306a36Sopenharmony_ci tgt_stats[tgt].read_sz_buckets[0], 338462306a36Sopenharmony_ci tgt_stats[tgt].read_sz_buckets[1], tgt_stats[tgt].read_sz_buckets[2], tgt_stats[tgt].read_sz_buckets[3], tgt_stats[tgt].read_sz_buckets[4]); 338562306a36Sopenharmony_ci seq_printf(m, 338662306a36Sopenharmony_ci " %2d Write %9u %9u %9u %9u %9u\n", tgt, 338762306a36Sopenharmony_ci tgt_stats[tgt].write_sz_buckets[0], 338862306a36Sopenharmony_ci tgt_stats[tgt].write_sz_buckets[1], tgt_stats[tgt].write_sz_buckets[2], tgt_stats[tgt].write_sz_buckets[3], tgt_stats[tgt].write_sz_buckets[4]); 338962306a36Sopenharmony_ci } 339062306a36Sopenharmony_ci seq_puts(m, "\n\ 339162306a36Sopenharmony_ciTarget Command 16-32KB 32-64KB 64-128KB 128-256KB 256KB+\n\ 339262306a36Sopenharmony_ci====== ======= ========= ========= ========= ========= =========\n"); 339362306a36Sopenharmony_ci for (tgt = 0; tgt < adapter->maxdev; tgt++) { 339462306a36Sopenharmony_ci struct blogic_tgt_flags *tgt_flags = &adapter->tgt_flags[tgt]; 339562306a36Sopenharmony_ci if (!tgt_flags->tgt_exists) 339662306a36Sopenharmony_ci continue; 339762306a36Sopenharmony_ci seq_printf(m, 339862306a36Sopenharmony_ci " %2d Read %9u %9u %9u %9u %9u\n", tgt, 339962306a36Sopenharmony_ci tgt_stats[tgt].read_sz_buckets[5], 340062306a36Sopenharmony_ci tgt_stats[tgt].read_sz_buckets[6], tgt_stats[tgt].read_sz_buckets[7], tgt_stats[tgt].read_sz_buckets[8], tgt_stats[tgt].read_sz_buckets[9]); 340162306a36Sopenharmony_ci seq_printf(m, 340262306a36Sopenharmony_ci " %2d Write %9u %9u %9u %9u %9u\n", tgt, 340362306a36Sopenharmony_ci tgt_stats[tgt].write_sz_buckets[5], 340462306a36Sopenharmony_ci tgt_stats[tgt].write_sz_buckets[6], tgt_stats[tgt].write_sz_buckets[7], tgt_stats[tgt].write_sz_buckets[8], tgt_stats[tgt].write_sz_buckets[9]); 340562306a36Sopenharmony_ci } 340662306a36Sopenharmony_ci seq_puts(m, "\n\n\ 340762306a36Sopenharmony_ci ERROR RECOVERY STATISTICS\n\ 340862306a36Sopenharmony_ci\n\ 340962306a36Sopenharmony_ci Command Aborts Bus Device Resets Host Adapter Resets\n\ 341062306a36Sopenharmony_ciTarget Requested Completed Requested Completed Requested Completed\n\ 341162306a36Sopenharmony_ci ID \\\\\\\\ Attempted //// \\\\\\\\ Attempted //// \\\\\\\\ Attempted ////\n\ 341262306a36Sopenharmony_ci====== ===== ===== ===== ===== ===== ===== ===== ===== =====\n"); 341362306a36Sopenharmony_ci for (tgt = 0; tgt < adapter->maxdev; tgt++) { 341462306a36Sopenharmony_ci struct blogic_tgt_flags *tgt_flags = &adapter->tgt_flags[tgt]; 341562306a36Sopenharmony_ci if (!tgt_flags->tgt_exists) 341662306a36Sopenharmony_ci continue; 341762306a36Sopenharmony_ci seq_printf(m, " %2d %5d %5d %5d %5d %5d %5d %5d %5d %5d\n", 341862306a36Sopenharmony_ci tgt, tgt_stats[tgt].aborts_request, 341962306a36Sopenharmony_ci tgt_stats[tgt].aborts_tried, 342062306a36Sopenharmony_ci tgt_stats[tgt].aborts_done, 342162306a36Sopenharmony_ci tgt_stats[tgt].bdr_request, 342262306a36Sopenharmony_ci tgt_stats[tgt].bdr_tried, 342362306a36Sopenharmony_ci tgt_stats[tgt].bdr_done, 342462306a36Sopenharmony_ci tgt_stats[tgt].adapter_reset_req, 342562306a36Sopenharmony_ci tgt_stats[tgt].adapter_reset_attempt, 342662306a36Sopenharmony_ci tgt_stats[tgt].adapter_reset_done); 342762306a36Sopenharmony_ci } 342862306a36Sopenharmony_ci seq_printf(m, "\nExternal Host Adapter Resets: %d\n", adapter->ext_resets); 342962306a36Sopenharmony_ci seq_printf(m, "Host Adapter Internal Errors: %d\n", adapter->adapter_intern_errors); 343062306a36Sopenharmony_ci return 0; 343162306a36Sopenharmony_ci} 343262306a36Sopenharmony_ci 343362306a36Sopenharmony_ci 343462306a36Sopenharmony_ci/* 343562306a36Sopenharmony_ci blogic_msg prints Driver Messages. 343662306a36Sopenharmony_ci*/ 343762306a36Sopenharmony_ci__printf(2, 4) 343862306a36Sopenharmony_cistatic void blogic_msg(enum blogic_msglevel msglevel, char *fmt, 343962306a36Sopenharmony_ci struct blogic_adapter *adapter, ...) 344062306a36Sopenharmony_ci{ 344162306a36Sopenharmony_ci static char buf[BLOGIC_LINEBUF_SIZE]; 344262306a36Sopenharmony_ci static bool begin = true; 344362306a36Sopenharmony_ci va_list args; 344462306a36Sopenharmony_ci int len = 0; 344562306a36Sopenharmony_ci 344662306a36Sopenharmony_ci va_start(args, adapter); 344762306a36Sopenharmony_ci len = vscnprintf(buf, sizeof(buf), fmt, args); 344862306a36Sopenharmony_ci va_end(args); 344962306a36Sopenharmony_ci if (msglevel == BLOGIC_ANNOUNCE_LEVEL) { 345062306a36Sopenharmony_ci static int msglines = 0; 345162306a36Sopenharmony_ci strcpy(&adapter->msgbuf[adapter->msgbuflen], buf); 345262306a36Sopenharmony_ci adapter->msgbuflen += len; 345362306a36Sopenharmony_ci if (++msglines <= 2) 345462306a36Sopenharmony_ci printk("%sscsi: %s", blogic_msglevelmap[msglevel], buf); 345562306a36Sopenharmony_ci } else if (msglevel == BLOGIC_INFO_LEVEL) { 345662306a36Sopenharmony_ci strcpy(&adapter->msgbuf[adapter->msgbuflen], buf); 345762306a36Sopenharmony_ci adapter->msgbuflen += len; 345862306a36Sopenharmony_ci if (begin) { 345962306a36Sopenharmony_ci if (buf[0] != '\n' || len > 1) 346062306a36Sopenharmony_ci printk("%sscsi%d: %s", blogic_msglevelmap[msglevel], adapter->host_no, buf); 346162306a36Sopenharmony_ci } else 346262306a36Sopenharmony_ci pr_cont("%s", buf); 346362306a36Sopenharmony_ci } else { 346462306a36Sopenharmony_ci if (begin) { 346562306a36Sopenharmony_ci if (adapter != NULL && adapter->adapter_initd) 346662306a36Sopenharmony_ci printk("%sscsi%d: %s", blogic_msglevelmap[msglevel], adapter->host_no, buf); 346762306a36Sopenharmony_ci else 346862306a36Sopenharmony_ci printk("%s%s", blogic_msglevelmap[msglevel], buf); 346962306a36Sopenharmony_ci } else 347062306a36Sopenharmony_ci pr_cont("%s", buf); 347162306a36Sopenharmony_ci } 347262306a36Sopenharmony_ci begin = (buf[len - 1] == '\n'); 347362306a36Sopenharmony_ci} 347462306a36Sopenharmony_ci 347562306a36Sopenharmony_ci 347662306a36Sopenharmony_ci/* 347762306a36Sopenharmony_ci blogic_parse parses an individual option keyword. It returns true 347862306a36Sopenharmony_ci and updates the pointer if the keyword is recognized and false otherwise. 347962306a36Sopenharmony_ci*/ 348062306a36Sopenharmony_ci 348162306a36Sopenharmony_cistatic bool __init blogic_parse(char **str, char *keyword) 348262306a36Sopenharmony_ci{ 348362306a36Sopenharmony_ci char *pointer = *str; 348462306a36Sopenharmony_ci while (*keyword != '\0') { 348562306a36Sopenharmony_ci char strch = *pointer++; 348662306a36Sopenharmony_ci char keywordch = *keyword++; 348762306a36Sopenharmony_ci if (strch >= 'A' && strch <= 'Z') 348862306a36Sopenharmony_ci strch += 'a' - 'Z'; 348962306a36Sopenharmony_ci if (keywordch >= 'A' && keywordch <= 'Z') 349062306a36Sopenharmony_ci keywordch += 'a' - 'Z'; 349162306a36Sopenharmony_ci if (strch != keywordch) 349262306a36Sopenharmony_ci return false; 349362306a36Sopenharmony_ci } 349462306a36Sopenharmony_ci *str = pointer; 349562306a36Sopenharmony_ci return true; 349662306a36Sopenharmony_ci} 349762306a36Sopenharmony_ci 349862306a36Sopenharmony_ci 349962306a36Sopenharmony_ci/* 350062306a36Sopenharmony_ci blogic_parseopts handles processing of BusLogic Driver Options 350162306a36Sopenharmony_ci specifications. 350262306a36Sopenharmony_ci 350362306a36Sopenharmony_ci BusLogic Driver Options may be specified either via the Linux Kernel Command 350462306a36Sopenharmony_ci Line or via the Loadable Kernel Module Installation Facility. Driver Options 350562306a36Sopenharmony_ci for multiple host adapters may be specified either by separating the option 350662306a36Sopenharmony_ci strings by a semicolon, or by specifying multiple "BusLogic=" strings on the 350762306a36Sopenharmony_ci command line. Individual option specifications for a single host adapter are 350862306a36Sopenharmony_ci separated by commas. The Probing and Debugging Options apply to all host 350962306a36Sopenharmony_ci adapters whereas the remaining options apply individually only to the 351062306a36Sopenharmony_ci selected host adapter. 351162306a36Sopenharmony_ci 351262306a36Sopenharmony_ci The BusLogic Driver Probing Options are described in 351362306a36Sopenharmony_ci <file:Documentation/scsi/BusLogic.rst>. 351462306a36Sopenharmony_ci*/ 351562306a36Sopenharmony_ci 351662306a36Sopenharmony_cistatic int __init blogic_parseopts(char *options) 351762306a36Sopenharmony_ci{ 351862306a36Sopenharmony_ci while (true) { 351962306a36Sopenharmony_ci struct blogic_drvr_options *drvr_opts = 352062306a36Sopenharmony_ci &blogic_drvr_options[blogic_drvr_options_count++]; 352162306a36Sopenharmony_ci int tgt_id; 352262306a36Sopenharmony_ci 352362306a36Sopenharmony_ci memset(drvr_opts, 0, sizeof(struct blogic_drvr_options)); 352462306a36Sopenharmony_ci while (*options != '\0' && *options != ';') { 352562306a36Sopenharmony_ci if (blogic_parse(&options, "NoProbePCI")) 352662306a36Sopenharmony_ci blogic_probe_options.noprobe_pci = true; 352762306a36Sopenharmony_ci else if (blogic_parse(&options, "NoProbe")) 352862306a36Sopenharmony_ci blogic_probe_options.noprobe = true; 352962306a36Sopenharmony_ci else if (blogic_parse(&options, "NoSortPCI")) 353062306a36Sopenharmony_ci blogic_probe_options.nosort_pci = true; 353162306a36Sopenharmony_ci else if (blogic_parse(&options, "MultiMasterFirst")) 353262306a36Sopenharmony_ci blogic_probe_options.multimaster_first = true; 353362306a36Sopenharmony_ci else if (blogic_parse(&options, "FlashPointFirst")) 353462306a36Sopenharmony_ci blogic_probe_options.flashpoint_first = true; 353562306a36Sopenharmony_ci /* Tagged Queuing Options. */ 353662306a36Sopenharmony_ci else if (blogic_parse(&options, "QueueDepth:[") || 353762306a36Sopenharmony_ci blogic_parse(&options, "QD:[")) { 353862306a36Sopenharmony_ci for (tgt_id = 0; tgt_id < BLOGIC_MAXDEV; tgt_id++) { 353962306a36Sopenharmony_ci unsigned short qdepth = simple_strtoul(options, &options, 0); 354062306a36Sopenharmony_ci if (qdepth > BLOGIC_MAX_TAG_DEPTH) { 354162306a36Sopenharmony_ci blogic_err("BusLogic: Invalid Driver Options (invalid Queue Depth %d)\n", NULL, qdepth); 354262306a36Sopenharmony_ci return 0; 354362306a36Sopenharmony_ci } 354462306a36Sopenharmony_ci drvr_opts->qdepth[tgt_id] = qdepth; 354562306a36Sopenharmony_ci if (*options == ',') 354662306a36Sopenharmony_ci options++; 354762306a36Sopenharmony_ci else if (*options == ']') 354862306a36Sopenharmony_ci break; 354962306a36Sopenharmony_ci else { 355062306a36Sopenharmony_ci blogic_err("BusLogic: Invalid Driver Options (',' or ']' expected at '%s')\n", NULL, options); 355162306a36Sopenharmony_ci return 0; 355262306a36Sopenharmony_ci } 355362306a36Sopenharmony_ci } 355462306a36Sopenharmony_ci if (*options != ']') { 355562306a36Sopenharmony_ci blogic_err("BusLogic: Invalid Driver Options (']' expected at '%s')\n", NULL, options); 355662306a36Sopenharmony_ci return 0; 355762306a36Sopenharmony_ci } else 355862306a36Sopenharmony_ci options++; 355962306a36Sopenharmony_ci } else if (blogic_parse(&options, "QueueDepth:") || blogic_parse(&options, "QD:")) { 356062306a36Sopenharmony_ci unsigned short qdepth = simple_strtoul(options, &options, 0); 356162306a36Sopenharmony_ci if (qdepth == 0 || 356262306a36Sopenharmony_ci qdepth > BLOGIC_MAX_TAG_DEPTH) { 356362306a36Sopenharmony_ci blogic_err("BusLogic: Invalid Driver Options (invalid Queue Depth %d)\n", NULL, qdepth); 356462306a36Sopenharmony_ci return 0; 356562306a36Sopenharmony_ci } 356662306a36Sopenharmony_ci drvr_opts->common_qdepth = qdepth; 356762306a36Sopenharmony_ci for (tgt_id = 0; tgt_id < BLOGIC_MAXDEV; tgt_id++) 356862306a36Sopenharmony_ci drvr_opts->qdepth[tgt_id] = qdepth; 356962306a36Sopenharmony_ci } else if (blogic_parse(&options, "TaggedQueuing:") || 357062306a36Sopenharmony_ci blogic_parse(&options, "TQ:")) { 357162306a36Sopenharmony_ci if (blogic_parse(&options, "Default")) { 357262306a36Sopenharmony_ci drvr_opts->tagq_ok = 0x0000; 357362306a36Sopenharmony_ci drvr_opts->tagq_ok_mask = 0x0000; 357462306a36Sopenharmony_ci } else if (blogic_parse(&options, "Enable")) { 357562306a36Sopenharmony_ci drvr_opts->tagq_ok = 0xFFFF; 357662306a36Sopenharmony_ci drvr_opts->tagq_ok_mask = 0xFFFF; 357762306a36Sopenharmony_ci } else if (blogic_parse(&options, "Disable")) { 357862306a36Sopenharmony_ci drvr_opts->tagq_ok = 0x0000; 357962306a36Sopenharmony_ci drvr_opts->tagq_ok_mask = 0xFFFF; 358062306a36Sopenharmony_ci } else { 358162306a36Sopenharmony_ci unsigned short tgt_bit; 358262306a36Sopenharmony_ci for (tgt_id = 0, tgt_bit = 1; 358362306a36Sopenharmony_ci tgt_id < BLOGIC_MAXDEV; 358462306a36Sopenharmony_ci tgt_id++, tgt_bit <<= 1) 358562306a36Sopenharmony_ci switch (*options++) { 358662306a36Sopenharmony_ci case 'Y': 358762306a36Sopenharmony_ci drvr_opts->tagq_ok |= tgt_bit; 358862306a36Sopenharmony_ci drvr_opts->tagq_ok_mask |= tgt_bit; 358962306a36Sopenharmony_ci break; 359062306a36Sopenharmony_ci case 'N': 359162306a36Sopenharmony_ci drvr_opts->tagq_ok &= ~tgt_bit; 359262306a36Sopenharmony_ci drvr_opts->tagq_ok_mask |= tgt_bit; 359362306a36Sopenharmony_ci break; 359462306a36Sopenharmony_ci case 'X': 359562306a36Sopenharmony_ci break; 359662306a36Sopenharmony_ci default: 359762306a36Sopenharmony_ci options--; 359862306a36Sopenharmony_ci tgt_id = BLOGIC_MAXDEV; 359962306a36Sopenharmony_ci break; 360062306a36Sopenharmony_ci } 360162306a36Sopenharmony_ci } 360262306a36Sopenharmony_ci } 360362306a36Sopenharmony_ci /* Miscellaneous Options. */ 360462306a36Sopenharmony_ci else if (blogic_parse(&options, "BusSettleTime:") || 360562306a36Sopenharmony_ci blogic_parse(&options, "BST:")) { 360662306a36Sopenharmony_ci unsigned short bus_settle_time = 360762306a36Sopenharmony_ci simple_strtoul(options, &options, 0); 360862306a36Sopenharmony_ci if (bus_settle_time > 5 * 60) { 360962306a36Sopenharmony_ci blogic_err("BusLogic: Invalid Driver Options (invalid Bus Settle Time %d)\n", NULL, bus_settle_time); 361062306a36Sopenharmony_ci return 0; 361162306a36Sopenharmony_ci } 361262306a36Sopenharmony_ci drvr_opts->bus_settle_time = bus_settle_time; 361362306a36Sopenharmony_ci } else if (blogic_parse(&options, 361462306a36Sopenharmony_ci "InhibitTargetInquiry")) 361562306a36Sopenharmony_ci drvr_opts->stop_tgt_inquiry = true; 361662306a36Sopenharmony_ci /* Debugging Options. */ 361762306a36Sopenharmony_ci else if (blogic_parse(&options, "TraceProbe")) 361862306a36Sopenharmony_ci blogic_global_options.trace_probe = true; 361962306a36Sopenharmony_ci else if (blogic_parse(&options, "TraceHardwareReset")) 362062306a36Sopenharmony_ci blogic_global_options.trace_hw_reset = true; 362162306a36Sopenharmony_ci else if (blogic_parse(&options, "TraceConfiguration")) 362262306a36Sopenharmony_ci blogic_global_options.trace_config = true; 362362306a36Sopenharmony_ci else if (blogic_parse(&options, "TraceErrors")) 362462306a36Sopenharmony_ci blogic_global_options.trace_err = true; 362562306a36Sopenharmony_ci else if (blogic_parse(&options, "Debug")) { 362662306a36Sopenharmony_ci blogic_global_options.trace_probe = true; 362762306a36Sopenharmony_ci blogic_global_options.trace_hw_reset = true; 362862306a36Sopenharmony_ci blogic_global_options.trace_config = true; 362962306a36Sopenharmony_ci blogic_global_options.trace_err = true; 363062306a36Sopenharmony_ci } 363162306a36Sopenharmony_ci if (*options == ',') 363262306a36Sopenharmony_ci options++; 363362306a36Sopenharmony_ci else if (*options != ';' && *options != '\0') { 363462306a36Sopenharmony_ci blogic_err("BusLogic: Unexpected Driver Option '%s' ignored\n", NULL, options); 363562306a36Sopenharmony_ci *options = '\0'; 363662306a36Sopenharmony_ci } 363762306a36Sopenharmony_ci } 363862306a36Sopenharmony_ci if (!(blogic_drvr_options_count == 0 || 363962306a36Sopenharmony_ci blogic_probeinfo_count == 0 || 364062306a36Sopenharmony_ci blogic_drvr_options_count == blogic_probeinfo_count)) { 364162306a36Sopenharmony_ci blogic_err("BusLogic: Invalid Driver Options (all or no I/O Addresses must be specified)\n", NULL); 364262306a36Sopenharmony_ci return 0; 364362306a36Sopenharmony_ci } 364462306a36Sopenharmony_ci /* 364562306a36Sopenharmony_ci Tagged Queuing is disabled when the Queue Depth is 1 since queuing 364662306a36Sopenharmony_ci multiple commands is not possible. 364762306a36Sopenharmony_ci */ 364862306a36Sopenharmony_ci for (tgt_id = 0; tgt_id < BLOGIC_MAXDEV; tgt_id++) 364962306a36Sopenharmony_ci if (drvr_opts->qdepth[tgt_id] == 1) { 365062306a36Sopenharmony_ci unsigned short tgt_bit = 1 << tgt_id; 365162306a36Sopenharmony_ci drvr_opts->tagq_ok &= ~tgt_bit; 365262306a36Sopenharmony_ci drvr_opts->tagq_ok_mask |= tgt_bit; 365362306a36Sopenharmony_ci } 365462306a36Sopenharmony_ci if (*options == ';') 365562306a36Sopenharmony_ci options++; 365662306a36Sopenharmony_ci if (*options == '\0') 365762306a36Sopenharmony_ci return 0; 365862306a36Sopenharmony_ci } 365962306a36Sopenharmony_ci return 1; 366062306a36Sopenharmony_ci} 366162306a36Sopenharmony_ci 366262306a36Sopenharmony_ci/* 366362306a36Sopenharmony_ci Get it all started 366462306a36Sopenharmony_ci*/ 366562306a36Sopenharmony_ci 366662306a36Sopenharmony_cistatic const struct scsi_host_template blogic_template = { 366762306a36Sopenharmony_ci .module = THIS_MODULE, 366862306a36Sopenharmony_ci .proc_name = "BusLogic", 366962306a36Sopenharmony_ci .write_info = blogic_write_info, 367062306a36Sopenharmony_ci .show_info = blogic_show_info, 367162306a36Sopenharmony_ci .name = "BusLogic", 367262306a36Sopenharmony_ci .info = blogic_drvr_info, 367362306a36Sopenharmony_ci .queuecommand = blogic_qcmd, 367462306a36Sopenharmony_ci .slave_configure = blogic_slaveconfig, 367562306a36Sopenharmony_ci .bios_param = blogic_diskparam, 367662306a36Sopenharmony_ci .eh_host_reset_handler = blogic_hostreset, 367762306a36Sopenharmony_ci#if 0 367862306a36Sopenharmony_ci .eh_abort_handler = blogic_abort, 367962306a36Sopenharmony_ci#endif 368062306a36Sopenharmony_ci .max_sectors = 128, 368162306a36Sopenharmony_ci}; 368262306a36Sopenharmony_ci 368362306a36Sopenharmony_ci/* 368462306a36Sopenharmony_ci blogic_setup handles processing of Kernel Command Line Arguments. 368562306a36Sopenharmony_ci*/ 368662306a36Sopenharmony_ci 368762306a36Sopenharmony_cistatic int __init blogic_setup(char *str) 368862306a36Sopenharmony_ci{ 368962306a36Sopenharmony_ci int ints[3]; 369062306a36Sopenharmony_ci 369162306a36Sopenharmony_ci (void) get_options(str, ARRAY_SIZE(ints), ints); 369262306a36Sopenharmony_ci 369362306a36Sopenharmony_ci if (ints[0] != 0) { 369462306a36Sopenharmony_ci blogic_err("BusLogic: Obsolete Command Line Entry Format Ignored\n", NULL); 369562306a36Sopenharmony_ci return 0; 369662306a36Sopenharmony_ci } 369762306a36Sopenharmony_ci if (str == NULL || *str == '\0') 369862306a36Sopenharmony_ci return 0; 369962306a36Sopenharmony_ci return blogic_parseopts(str); 370062306a36Sopenharmony_ci} 370162306a36Sopenharmony_ci 370262306a36Sopenharmony_ci/* 370362306a36Sopenharmony_ci * Exit function. Deletes all hosts associated with this driver. 370462306a36Sopenharmony_ci */ 370562306a36Sopenharmony_ci 370662306a36Sopenharmony_cistatic void __exit blogic_exit(void) 370762306a36Sopenharmony_ci{ 370862306a36Sopenharmony_ci struct blogic_adapter *ha, *next; 370962306a36Sopenharmony_ci 371062306a36Sopenharmony_ci list_for_each_entry_safe(ha, next, &blogic_host_list, host_list) 371162306a36Sopenharmony_ci blogic_deladapter(ha); 371262306a36Sopenharmony_ci} 371362306a36Sopenharmony_ci 371462306a36Sopenharmony_ci__setup("BusLogic=", blogic_setup); 371562306a36Sopenharmony_ci 371662306a36Sopenharmony_ci#ifdef MODULE 371762306a36Sopenharmony_ci/*static struct pci_device_id blogic_pci_tbl[] = { 371862306a36Sopenharmony_ci { PCI_VENDOR_ID_BUSLOGIC, PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER, 371962306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 372062306a36Sopenharmony_ci { PCI_VENDOR_ID_BUSLOGIC, PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER_NC, 372162306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 372262306a36Sopenharmony_ci { PCI_VENDOR_ID_BUSLOGIC, PCI_DEVICE_ID_BUSLOGIC_FLASHPOINT, 372362306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 372462306a36Sopenharmony_ci { } 372562306a36Sopenharmony_ci};*/ 372662306a36Sopenharmony_cistatic const struct pci_device_id blogic_pci_tbl[] = { 372762306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_BUSLOGIC, PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER)}, 372862306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_BUSLOGIC, PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER_NC)}, 372962306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_BUSLOGIC, PCI_DEVICE_ID_BUSLOGIC_FLASHPOINT)}, 373062306a36Sopenharmony_ci {0, }, 373162306a36Sopenharmony_ci}; 373262306a36Sopenharmony_ci#endif 373362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, blogic_pci_tbl); 373462306a36Sopenharmony_ci 373562306a36Sopenharmony_cimodule_init(blogic_init); 373662306a36Sopenharmony_cimodule_exit(blogic_exit); 3737