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