| // SPDX-License-Identifier: GPL-2.0 | 
 | /* | 
 |  * Linux Driver for Mylex DAC960/AcceleRAID/eXtremeRAID PCI RAID Controllers | 
 |  * | 
 |  * Copyright 2017 Hannes Reinecke, SUSE Linux GmbH <hare@suse.com> | 
 |  * | 
 |  * Based on the original DAC960 driver, | 
 |  * Copyright 1998-2001 by Leonard N. Zubkoff <lnz@dandelion.com> | 
 |  * Portions Copyright 2002 by Mylex (An IBM Business Unit) | 
 |  * | 
 |  */ | 
 |  | 
 | #include <linux/module.h> | 
 | #include <linux/types.h> | 
 | #include <linux/delay.h> | 
 | #include <linux/interrupt.h> | 
 | #include <linux/pci.h> | 
 | #include <linux/raid_class.h> | 
 | #include <asm/unaligned.h> | 
 | #include <scsi/scsi.h> | 
 | #include <scsi/scsi_host.h> | 
 | #include <scsi/scsi_device.h> | 
 | #include <scsi/scsi_cmnd.h> | 
 | #include <scsi/scsi_tcq.h> | 
 | #include "myrb.h" | 
 |  | 
 | static struct raid_template *myrb_raid_template; | 
 |  | 
 | static void myrb_monitor(struct work_struct *work); | 
 | static inline void myrb_translate_devstate(void *DeviceState); | 
 |  | 
 | static inline int myrb_logical_channel(struct Scsi_Host *shost) | 
 | { | 
 | 	return shost->max_channel - 1; | 
 | } | 
 |  | 
 | static struct myrb_devstate_name_entry { | 
 | 	enum myrb_devstate state; | 
 | 	const char *name; | 
 | } myrb_devstate_name_list[] = { | 
 | 	{ MYRB_DEVICE_DEAD, "Dead" }, | 
 | 	{ MYRB_DEVICE_WO, "WriteOnly" }, | 
 | 	{ MYRB_DEVICE_ONLINE, "Online" }, | 
 | 	{ MYRB_DEVICE_CRITICAL, "Critical" }, | 
 | 	{ MYRB_DEVICE_STANDBY, "Standby" }, | 
 | 	{ MYRB_DEVICE_OFFLINE, "Offline" }, | 
 | }; | 
 |  | 
 | static const char *myrb_devstate_name(enum myrb_devstate state) | 
 | { | 
 | 	struct myrb_devstate_name_entry *entry = myrb_devstate_name_list; | 
 | 	int i; | 
 |  | 
 | 	for (i = 0; i < ARRAY_SIZE(myrb_devstate_name_list); i++) { | 
 | 		if (entry[i].state == state) | 
 | 			return entry[i].name; | 
 | 	} | 
 | 	return "Unknown"; | 
 | } | 
 |  | 
 | static struct myrb_raidlevel_name_entry { | 
 | 	enum myrb_raidlevel level; | 
 | 	const char *name; | 
 | } myrb_raidlevel_name_list[] = { | 
 | 	{ MYRB_RAID_LEVEL0, "RAID0" }, | 
 | 	{ MYRB_RAID_LEVEL1, "RAID1" }, | 
 | 	{ MYRB_RAID_LEVEL3, "RAID3" }, | 
 | 	{ MYRB_RAID_LEVEL5, "RAID5" }, | 
 | 	{ MYRB_RAID_LEVEL6, "RAID6" }, | 
 | 	{ MYRB_RAID_JBOD, "JBOD" }, | 
 | }; | 
 |  | 
 | static const char *myrb_raidlevel_name(enum myrb_raidlevel level) | 
 | { | 
 | 	struct myrb_raidlevel_name_entry *entry = myrb_raidlevel_name_list; | 
 | 	int i; | 
 |  | 
 | 	for (i = 0; i < ARRAY_SIZE(myrb_raidlevel_name_list); i++) { | 
 | 		if (entry[i].level == level) | 
 | 			return entry[i].name; | 
 | 	} | 
 | 	return NULL; | 
 | } | 
 |  | 
 | /* | 
 |  * myrb_create_mempools - allocates auxiliary data structures | 
 |  * | 
 |  * Return: true on success, false otherwise. | 
 |  */ | 
 | static bool myrb_create_mempools(struct pci_dev *pdev, struct myrb_hba *cb) | 
 | { | 
 | 	size_t elem_size, elem_align; | 
 |  | 
 | 	elem_align = sizeof(struct myrb_sge); | 
 | 	elem_size = cb->host->sg_tablesize * elem_align; | 
 | 	cb->sg_pool = dma_pool_create("myrb_sg", &pdev->dev, | 
 | 				      elem_size, elem_align, 0); | 
 | 	if (cb->sg_pool == NULL) { | 
 | 		shost_printk(KERN_ERR, cb->host, | 
 | 			     "Failed to allocate SG pool\n"); | 
 | 		return false; | 
 | 	} | 
 |  | 
 | 	cb->dcdb_pool = dma_pool_create("myrb_dcdb", &pdev->dev, | 
 | 				       sizeof(struct myrb_dcdb), | 
 | 				       sizeof(unsigned int), 0); | 
 | 	if (!cb->dcdb_pool) { | 
 | 		dma_pool_destroy(cb->sg_pool); | 
 | 		cb->sg_pool = NULL; | 
 | 		shost_printk(KERN_ERR, cb->host, | 
 | 			     "Failed to allocate DCDB pool\n"); | 
 | 		return false; | 
 | 	} | 
 |  | 
 | 	snprintf(cb->work_q_name, sizeof(cb->work_q_name), | 
 | 		 "myrb_wq_%d", cb->host->host_no); | 
 | 	cb->work_q = create_singlethread_workqueue(cb->work_q_name); | 
 | 	if (!cb->work_q) { | 
 | 		dma_pool_destroy(cb->dcdb_pool); | 
 | 		cb->dcdb_pool = NULL; | 
 | 		dma_pool_destroy(cb->sg_pool); | 
 | 		cb->sg_pool = NULL; | 
 | 		shost_printk(KERN_ERR, cb->host, | 
 | 			     "Failed to create workqueue\n"); | 
 | 		return false; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * Initialize the Monitoring Timer. | 
 | 	 */ | 
 | 	INIT_DELAYED_WORK(&cb->monitor_work, myrb_monitor); | 
 | 	queue_delayed_work(cb->work_q, &cb->monitor_work, 1); | 
 |  | 
 | 	return true; | 
 | } | 
 |  | 
 | /* | 
 |  * myrb_destroy_mempools - tears down the memory pools for the controller | 
 |  */ | 
 | static void myrb_destroy_mempools(struct myrb_hba *cb) | 
 | { | 
 | 	cancel_delayed_work_sync(&cb->monitor_work); | 
 | 	destroy_workqueue(cb->work_q); | 
 |  | 
 | 	dma_pool_destroy(cb->sg_pool); | 
 | 	dma_pool_destroy(cb->dcdb_pool); | 
 | } | 
 |  | 
 | /* | 
 |  * myrb_reset_cmd - reset command block | 
 |  */ | 
 | static inline void myrb_reset_cmd(struct myrb_cmdblk *cmd_blk) | 
 | { | 
 | 	union myrb_cmd_mbox *mbox = &cmd_blk->mbox; | 
 |  | 
 | 	memset(mbox, 0, sizeof(union myrb_cmd_mbox)); | 
 | 	cmd_blk->status = 0; | 
 | } | 
 |  | 
 | /* | 
 |  * myrb_qcmd - queues command block for execution | 
 |  */ | 
 | static void myrb_qcmd(struct myrb_hba *cb, struct myrb_cmdblk *cmd_blk) | 
 | { | 
 | 	void __iomem *base = cb->io_base; | 
 | 	union myrb_cmd_mbox *mbox = &cmd_blk->mbox; | 
 | 	union myrb_cmd_mbox *next_mbox = cb->next_cmd_mbox; | 
 |  | 
 | 	cb->write_cmd_mbox(next_mbox, mbox); | 
 | 	if (cb->prev_cmd_mbox1->words[0] == 0 || | 
 | 	    cb->prev_cmd_mbox2->words[0] == 0) | 
 | 		cb->get_cmd_mbox(base); | 
 | 	cb->prev_cmd_mbox2 = cb->prev_cmd_mbox1; | 
 | 	cb->prev_cmd_mbox1 = next_mbox; | 
 | 	if (++next_mbox > cb->last_cmd_mbox) | 
 | 		next_mbox = cb->first_cmd_mbox; | 
 | 	cb->next_cmd_mbox = next_mbox; | 
 | } | 
 |  | 
 | /* | 
 |  * myrb_exec_cmd - executes command block and waits for completion. | 
 |  * | 
 |  * Return: command status | 
 |  */ | 
 | static unsigned short myrb_exec_cmd(struct myrb_hba *cb, | 
 | 		struct myrb_cmdblk *cmd_blk) | 
 | { | 
 | 	DECLARE_COMPLETION_ONSTACK(cmpl); | 
 | 	unsigned long flags; | 
 |  | 
 | 	cmd_blk->completion = &cmpl; | 
 |  | 
 | 	spin_lock_irqsave(&cb->queue_lock, flags); | 
 | 	cb->qcmd(cb, cmd_blk); | 
 | 	spin_unlock_irqrestore(&cb->queue_lock, flags); | 
 |  | 
 | 	wait_for_completion(&cmpl); | 
 | 	return cmd_blk->status; | 
 | } | 
 |  | 
 | /* | 
 |  * myrb_exec_type3 - executes a type 3 command and waits for completion. | 
 |  * | 
 |  * Return: command status | 
 |  */ | 
 | static unsigned short myrb_exec_type3(struct myrb_hba *cb, | 
 | 		enum myrb_cmd_opcode op, dma_addr_t addr) | 
 | { | 
 | 	struct myrb_cmdblk *cmd_blk = &cb->dcmd_blk; | 
 | 	union myrb_cmd_mbox *mbox = &cmd_blk->mbox; | 
 | 	unsigned short status; | 
 |  | 
 | 	mutex_lock(&cb->dcmd_mutex); | 
 | 	myrb_reset_cmd(cmd_blk); | 
 | 	mbox->type3.id = MYRB_DCMD_TAG; | 
 | 	mbox->type3.opcode = op; | 
 | 	mbox->type3.addr = addr; | 
 | 	status = myrb_exec_cmd(cb, cmd_blk); | 
 | 	mutex_unlock(&cb->dcmd_mutex); | 
 | 	return status; | 
 | } | 
 |  | 
 | /* | 
 |  * myrb_exec_type3D - executes a type 3D command and waits for completion. | 
 |  * | 
 |  * Return: command status | 
 |  */ | 
 | static unsigned short myrb_exec_type3D(struct myrb_hba *cb, | 
 | 		enum myrb_cmd_opcode op, struct scsi_device *sdev, | 
 | 		struct myrb_pdev_state *pdev_info) | 
 | { | 
 | 	struct myrb_cmdblk *cmd_blk = &cb->dcmd_blk; | 
 | 	union myrb_cmd_mbox *mbox = &cmd_blk->mbox; | 
 | 	unsigned short status; | 
 | 	dma_addr_t pdev_info_addr; | 
 |  | 
 | 	pdev_info_addr = dma_map_single(&cb->pdev->dev, pdev_info, | 
 | 					sizeof(struct myrb_pdev_state), | 
 | 					DMA_FROM_DEVICE); | 
 | 	if (dma_mapping_error(&cb->pdev->dev, pdev_info_addr)) | 
 | 		return MYRB_STATUS_SUBSYS_FAILED; | 
 |  | 
 | 	mutex_lock(&cb->dcmd_mutex); | 
 | 	myrb_reset_cmd(cmd_blk); | 
 | 	mbox->type3D.id = MYRB_DCMD_TAG; | 
 | 	mbox->type3D.opcode = op; | 
 | 	mbox->type3D.channel = sdev->channel; | 
 | 	mbox->type3D.target = sdev->id; | 
 | 	mbox->type3D.addr = pdev_info_addr; | 
 | 	status = myrb_exec_cmd(cb, cmd_blk); | 
 | 	mutex_unlock(&cb->dcmd_mutex); | 
 | 	dma_unmap_single(&cb->pdev->dev, pdev_info_addr, | 
 | 			 sizeof(struct myrb_pdev_state), DMA_FROM_DEVICE); | 
 | 	if (status == MYRB_STATUS_SUCCESS && | 
 | 	    mbox->type3D.opcode == MYRB_CMD_GET_DEVICE_STATE_OLD) | 
 | 		myrb_translate_devstate(pdev_info); | 
 |  | 
 | 	return status; | 
 | } | 
 |  | 
 | static char *myrb_event_msg[] = { | 
 | 	"killed because write recovery failed", | 
 | 	"killed because of SCSI bus reset failure", | 
 | 	"killed because of double check condition", | 
 | 	"killed because it was removed", | 
 | 	"killed because of gross error on SCSI chip", | 
 | 	"killed because of bad tag returned from drive", | 
 | 	"killed because of timeout on SCSI command", | 
 | 	"killed because of reset SCSI command issued from system", | 
 | 	"killed because busy or parity error count exceeded limit", | 
 | 	"killed because of 'kill drive' command from system", | 
 | 	"killed because of selection timeout", | 
 | 	"killed due to SCSI phase sequence error", | 
 | 	"killed due to unknown status", | 
 | }; | 
 |  | 
 | /** | 
 |  * myrb_get_event - get event log from HBA | 
 |  * @cb: pointer to the hba structure | 
 |  * @event: number of the event | 
 |  * | 
 |  * Execute a type 3E command and logs the event message | 
 |  */ | 
 | static void myrb_get_event(struct myrb_hba *cb, unsigned int event) | 
 | { | 
 | 	struct myrb_cmdblk *cmd_blk = &cb->mcmd_blk; | 
 | 	union myrb_cmd_mbox *mbox = &cmd_blk->mbox; | 
 | 	struct myrb_log_entry *ev_buf; | 
 | 	dma_addr_t ev_addr; | 
 | 	unsigned short status; | 
 |  | 
 | 	ev_buf = dma_alloc_coherent(&cb->pdev->dev, | 
 | 				    sizeof(struct myrb_log_entry), | 
 | 				    &ev_addr, GFP_KERNEL); | 
 | 	if (!ev_buf) | 
 | 		return; | 
 |  | 
 | 	myrb_reset_cmd(cmd_blk); | 
 | 	mbox->type3E.id = MYRB_MCMD_TAG; | 
 | 	mbox->type3E.opcode = MYRB_CMD_EVENT_LOG_OPERATION; | 
 | 	mbox->type3E.optype = DAC960_V1_GetEventLogEntry; | 
 | 	mbox->type3E.opqual = 1; | 
 | 	mbox->type3E.ev_seq = event; | 
 | 	mbox->type3E.addr = ev_addr; | 
 | 	status = myrb_exec_cmd(cb, cmd_blk); | 
 | 	if (status != MYRB_STATUS_SUCCESS) | 
 | 		shost_printk(KERN_INFO, cb->host, | 
 | 			     "Failed to get event log %d, status %04x\n", | 
 | 			     event, status); | 
 |  | 
 | 	else if (ev_buf->seq_num == event) { | 
 | 		struct scsi_sense_hdr sshdr; | 
 |  | 
 | 		memset(&sshdr, 0, sizeof(sshdr)); | 
 | 		scsi_normalize_sense(ev_buf->sense, 32, &sshdr); | 
 |  | 
 | 		if (sshdr.sense_key == VENDOR_SPECIFIC && | 
 | 		    sshdr.asc == 0x80 && | 
 | 		    sshdr.ascq < ARRAY_SIZE(myrb_event_msg)) | 
 | 			shost_printk(KERN_CRIT, cb->host, | 
 | 				     "Physical drive %d:%d: %s\n", | 
 | 				     ev_buf->channel, ev_buf->target, | 
 | 				     myrb_event_msg[sshdr.ascq]); | 
 | 		else | 
 | 			shost_printk(KERN_CRIT, cb->host, | 
 | 				     "Physical drive %d:%d: Sense: %X/%02X/%02X\n", | 
 | 				     ev_buf->channel, ev_buf->target, | 
 | 				     sshdr.sense_key, sshdr.asc, sshdr.ascq); | 
 | 	} | 
 |  | 
 | 	dma_free_coherent(&cb->pdev->dev, sizeof(struct myrb_log_entry), | 
 | 			  ev_buf, ev_addr); | 
 | } | 
 |  | 
 | /* | 
 |  * myrb_get_errtable - retrieves the error table from the controller | 
 |  * | 
 |  * Executes a type 3 command and logs the error table from the controller. | 
 |  */ | 
 | static void myrb_get_errtable(struct myrb_hba *cb) | 
 | { | 
 | 	struct myrb_cmdblk *cmd_blk = &cb->mcmd_blk; | 
 | 	union myrb_cmd_mbox *mbox = &cmd_blk->mbox; | 
 | 	unsigned short status; | 
 | 	struct myrb_error_entry old_table[MYRB_MAX_CHANNELS * MYRB_MAX_TARGETS]; | 
 |  | 
 | 	memcpy(&old_table, cb->err_table, sizeof(old_table)); | 
 |  | 
 | 	myrb_reset_cmd(cmd_blk); | 
 | 	mbox->type3.id = MYRB_MCMD_TAG; | 
 | 	mbox->type3.opcode = MYRB_CMD_GET_ERROR_TABLE; | 
 | 	mbox->type3.addr = cb->err_table_addr; | 
 | 	status = myrb_exec_cmd(cb, cmd_blk); | 
 | 	if (status == MYRB_STATUS_SUCCESS) { | 
 | 		struct myrb_error_entry *table = cb->err_table; | 
 | 		struct myrb_error_entry *new, *old; | 
 | 		size_t err_table_offset; | 
 | 		struct scsi_device *sdev; | 
 |  | 
 | 		shost_for_each_device(sdev, cb->host) { | 
 | 			if (sdev->channel >= myrb_logical_channel(cb->host)) | 
 | 				continue; | 
 | 			err_table_offset = sdev->channel * MYRB_MAX_TARGETS | 
 | 				+ sdev->id; | 
 | 			new = table + err_table_offset; | 
 | 			old = &old_table[err_table_offset]; | 
 | 			if (new->parity_err == old->parity_err && | 
 | 			    new->soft_err == old->soft_err && | 
 | 			    new->hard_err == old->hard_err && | 
 | 			    new->misc_err == old->misc_err) | 
 | 				continue; | 
 | 			sdev_printk(KERN_CRIT, sdev, | 
 | 				    "Errors: Parity = %d, Soft = %d, Hard = %d, Misc = %d\n", | 
 | 				    new->parity_err, new->soft_err, | 
 | 				    new->hard_err, new->misc_err); | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | /* | 
 |  * myrb_get_ldev_info - retrieves the logical device table from the controller | 
 |  * | 
 |  * Executes a type 3 command and updates the logical device table. | 
 |  * | 
 |  * Return: command status | 
 |  */ | 
 | static unsigned short myrb_get_ldev_info(struct myrb_hba *cb) | 
 | { | 
 | 	unsigned short status; | 
 | 	int ldev_num, ldev_cnt = cb->enquiry->ldev_count; | 
 | 	struct Scsi_Host *shost = cb->host; | 
 |  | 
 | 	status = myrb_exec_type3(cb, MYRB_CMD_GET_LDEV_INFO, | 
 | 				 cb->ldev_info_addr); | 
 | 	if (status != MYRB_STATUS_SUCCESS) | 
 | 		return status; | 
 |  | 
 | 	for (ldev_num = 0; ldev_num < ldev_cnt; ldev_num++) { | 
 | 		struct myrb_ldev_info *old = NULL; | 
 | 		struct myrb_ldev_info *new = cb->ldev_info_buf + ldev_num; | 
 | 		struct scsi_device *sdev; | 
 |  | 
 | 		sdev = scsi_device_lookup(shost, myrb_logical_channel(shost), | 
 | 					  ldev_num, 0); | 
 | 		if (!sdev) { | 
 | 			if (new->state == MYRB_DEVICE_OFFLINE) | 
 | 				continue; | 
 | 			shost_printk(KERN_INFO, shost, | 
 | 				     "Adding Logical Drive %d in state %s\n", | 
 | 				     ldev_num, myrb_devstate_name(new->state)); | 
 | 			scsi_add_device(shost, myrb_logical_channel(shost), | 
 | 					ldev_num, 0); | 
 | 			continue; | 
 | 		} | 
 | 		old = sdev->hostdata; | 
 | 		if (new->state != old->state) | 
 | 			shost_printk(KERN_INFO, shost, | 
 | 				     "Logical Drive %d is now %s\n", | 
 | 				     ldev_num, myrb_devstate_name(new->state)); | 
 | 		if (new->wb_enabled != old->wb_enabled) | 
 | 			sdev_printk(KERN_INFO, sdev, | 
 | 				    "Logical Drive is now WRITE %s\n", | 
 | 				    (new->wb_enabled ? "BACK" : "THRU")); | 
 | 		memcpy(old, new, sizeof(*new)); | 
 | 		scsi_device_put(sdev); | 
 | 	} | 
 | 	return status; | 
 | } | 
 |  | 
 | /* | 
 |  * myrb_get_rbld_progress - get rebuild progress information | 
 |  * | 
 |  * Executes a type 3 command and returns the rebuild progress | 
 |  * information. | 
 |  * | 
 |  * Return: command status | 
 |  */ | 
 | static unsigned short myrb_get_rbld_progress(struct myrb_hba *cb, | 
 | 		struct myrb_rbld_progress *rbld) | 
 | { | 
 | 	struct myrb_cmdblk *cmd_blk = &cb->mcmd_blk; | 
 | 	union myrb_cmd_mbox *mbox = &cmd_blk->mbox; | 
 | 	struct myrb_rbld_progress *rbld_buf; | 
 | 	dma_addr_t rbld_addr; | 
 | 	unsigned short status; | 
 |  | 
 | 	rbld_buf = dma_alloc_coherent(&cb->pdev->dev, | 
 | 				      sizeof(struct myrb_rbld_progress), | 
 | 				      &rbld_addr, GFP_KERNEL); | 
 | 	if (!rbld_buf) | 
 | 		return MYRB_STATUS_RBLD_NOT_CHECKED; | 
 |  | 
 | 	myrb_reset_cmd(cmd_blk); | 
 | 	mbox->type3.id = MYRB_MCMD_TAG; | 
 | 	mbox->type3.opcode = MYRB_CMD_GET_REBUILD_PROGRESS; | 
 | 	mbox->type3.addr = rbld_addr; | 
 | 	status = myrb_exec_cmd(cb, cmd_blk); | 
 | 	if (rbld) | 
 | 		memcpy(rbld, rbld_buf, sizeof(struct myrb_rbld_progress)); | 
 | 	dma_free_coherent(&cb->pdev->dev, sizeof(struct myrb_rbld_progress), | 
 | 			  rbld_buf, rbld_addr); | 
 | 	return status; | 
 | } | 
 |  | 
 | /* | 
 |  * myrb_update_rbld_progress - updates the rebuild status | 
 |  * | 
 |  * Updates the rebuild status for the attached logical devices. | 
 |  */ | 
 | static void myrb_update_rbld_progress(struct myrb_hba *cb) | 
 | { | 
 | 	struct myrb_rbld_progress rbld_buf; | 
 | 	unsigned short status; | 
 |  | 
 | 	status = myrb_get_rbld_progress(cb, &rbld_buf); | 
 | 	if (status == MYRB_NO_STDBY_RBLD_OR_CHECK_IN_PROGRESS && | 
 | 	    cb->last_rbld_status == MYRB_STATUS_SUCCESS) | 
 | 		status = MYRB_STATUS_RBLD_SUCCESS; | 
 | 	if (status != MYRB_NO_STDBY_RBLD_OR_CHECK_IN_PROGRESS) { | 
 | 		unsigned int blocks_done = | 
 | 			rbld_buf.ldev_size - rbld_buf.blocks_left; | 
 | 		struct scsi_device *sdev; | 
 |  | 
 | 		sdev = scsi_device_lookup(cb->host, | 
 | 					  myrb_logical_channel(cb->host), | 
 | 					  rbld_buf.ldev_num, 0); | 
 | 		if (!sdev) | 
 | 			return; | 
 |  | 
 | 		switch (status) { | 
 | 		case MYRB_STATUS_SUCCESS: | 
 | 			sdev_printk(KERN_INFO, sdev, | 
 | 				    "Rebuild in Progress, %d%% completed\n", | 
 | 				    (100 * (blocks_done >> 7)) | 
 | 				    / (rbld_buf.ldev_size >> 7)); | 
 | 			break; | 
 | 		case MYRB_STATUS_RBLD_FAILED_LDEV_FAILURE: | 
 | 			sdev_printk(KERN_INFO, sdev, | 
 | 				    "Rebuild Failed due to Logical Drive Failure\n"); | 
 | 			break; | 
 | 		case MYRB_STATUS_RBLD_FAILED_BADBLOCKS: | 
 | 			sdev_printk(KERN_INFO, sdev, | 
 | 				    "Rebuild Failed due to Bad Blocks on Other Drives\n"); | 
 | 			break; | 
 | 		case MYRB_STATUS_RBLD_FAILED_NEW_DRIVE_FAILED: | 
 | 			sdev_printk(KERN_INFO, sdev, | 
 | 				    "Rebuild Failed due to Failure of Drive Being Rebuilt\n"); | 
 | 			break; | 
 | 		case MYRB_STATUS_RBLD_SUCCESS: | 
 | 			sdev_printk(KERN_INFO, sdev, | 
 | 				    "Rebuild Completed Successfully\n"); | 
 | 			break; | 
 | 		case MYRB_STATUS_RBLD_SUCCESS_TERMINATED: | 
 | 			sdev_printk(KERN_INFO, sdev, | 
 | 				     "Rebuild Successfully Terminated\n"); | 
 | 			break; | 
 | 		default: | 
 | 			break; | 
 | 		} | 
 | 		scsi_device_put(sdev); | 
 | 	} | 
 | 	cb->last_rbld_status = status; | 
 | } | 
 |  | 
 | /* | 
 |  * myrb_get_cc_progress - retrieve the rebuild status | 
 |  * | 
 |  * Execute a type 3 Command and fetch the rebuild / consistency check | 
 |  * status. | 
 |  */ | 
 | static void myrb_get_cc_progress(struct myrb_hba *cb) | 
 | { | 
 | 	struct myrb_cmdblk *cmd_blk = &cb->mcmd_blk; | 
 | 	union myrb_cmd_mbox *mbox = &cmd_blk->mbox; | 
 | 	struct myrb_rbld_progress *rbld_buf; | 
 | 	dma_addr_t rbld_addr; | 
 | 	unsigned short status; | 
 |  | 
 | 	rbld_buf = dma_alloc_coherent(&cb->pdev->dev, | 
 | 				      sizeof(struct myrb_rbld_progress), | 
 | 				      &rbld_addr, GFP_KERNEL); | 
 | 	if (!rbld_buf) { | 
 | 		cb->need_cc_status = true; | 
 | 		return; | 
 | 	} | 
 | 	myrb_reset_cmd(cmd_blk); | 
 | 	mbox->type3.id = MYRB_MCMD_TAG; | 
 | 	mbox->type3.opcode = MYRB_CMD_REBUILD_STAT; | 
 | 	mbox->type3.addr = rbld_addr; | 
 | 	status = myrb_exec_cmd(cb, cmd_blk); | 
 | 	if (status == MYRB_STATUS_SUCCESS) { | 
 | 		unsigned int ldev_num = rbld_buf->ldev_num; | 
 | 		unsigned int ldev_size = rbld_buf->ldev_size; | 
 | 		unsigned int blocks_done = | 
 | 			ldev_size - rbld_buf->blocks_left; | 
 | 		struct scsi_device *sdev; | 
 |  | 
 | 		sdev = scsi_device_lookup(cb->host, | 
 | 					  myrb_logical_channel(cb->host), | 
 | 					  ldev_num, 0); | 
 | 		if (sdev) { | 
 | 			sdev_printk(KERN_INFO, sdev, | 
 | 				    "Consistency Check in Progress: %d%% completed\n", | 
 | 				    (100 * (blocks_done >> 7)) | 
 | 				    / (ldev_size >> 7)); | 
 | 			scsi_device_put(sdev); | 
 | 		} | 
 | 	} | 
 | 	dma_free_coherent(&cb->pdev->dev, sizeof(struct myrb_rbld_progress), | 
 | 			  rbld_buf, rbld_addr); | 
 | } | 
 |  | 
 | /* | 
 |  * myrb_bgi_control - updates background initialisation status | 
 |  * | 
 |  * Executes a type 3B command and updates the background initialisation status | 
 |  */ | 
 | static void myrb_bgi_control(struct myrb_hba *cb) | 
 | { | 
 | 	struct myrb_cmdblk *cmd_blk = &cb->mcmd_blk; | 
 | 	union myrb_cmd_mbox *mbox = &cmd_blk->mbox; | 
 | 	struct myrb_bgi_status *bgi, *last_bgi; | 
 | 	dma_addr_t bgi_addr; | 
 | 	struct scsi_device *sdev = NULL; | 
 | 	unsigned short status; | 
 |  | 
 | 	bgi = dma_alloc_coherent(&cb->pdev->dev, sizeof(struct myrb_bgi_status), | 
 | 				 &bgi_addr, GFP_KERNEL); | 
 | 	if (!bgi) { | 
 | 		shost_printk(KERN_ERR, cb->host, | 
 | 			     "Failed to allocate bgi memory\n"); | 
 | 		return; | 
 | 	} | 
 | 	myrb_reset_cmd(cmd_blk); | 
 | 	mbox->type3B.id = MYRB_DCMD_TAG; | 
 | 	mbox->type3B.opcode = MYRB_CMD_BGI_CONTROL; | 
 | 	mbox->type3B.optype = 0x20; | 
 | 	mbox->type3B.addr = bgi_addr; | 
 | 	status = myrb_exec_cmd(cb, cmd_blk); | 
 | 	last_bgi = &cb->bgi_status; | 
 | 	sdev = scsi_device_lookup(cb->host, | 
 | 				  myrb_logical_channel(cb->host), | 
 | 				  bgi->ldev_num, 0); | 
 | 	switch (status) { | 
 | 	case MYRB_STATUS_SUCCESS: | 
 | 		switch (bgi->status) { | 
 | 		case MYRB_BGI_INVALID: | 
 | 			break; | 
 | 		case MYRB_BGI_STARTED: | 
 | 			if (!sdev) | 
 | 				break; | 
 | 			sdev_printk(KERN_INFO, sdev, | 
 | 				    "Background Initialization Started\n"); | 
 | 			break; | 
 | 		case MYRB_BGI_INPROGRESS: | 
 | 			if (!sdev) | 
 | 				break; | 
 | 			if (bgi->blocks_done == last_bgi->blocks_done && | 
 | 			    bgi->ldev_num == last_bgi->ldev_num) | 
 | 				break; | 
 | 			sdev_printk(KERN_INFO, sdev, | 
 | 				 "Background Initialization in Progress: %d%% completed\n", | 
 | 				 (100 * (bgi->blocks_done >> 7)) | 
 | 				 / (bgi->ldev_size >> 7)); | 
 | 			break; | 
 | 		case MYRB_BGI_SUSPENDED: | 
 | 			if (!sdev) | 
 | 				break; | 
 | 			sdev_printk(KERN_INFO, sdev, | 
 | 				    "Background Initialization Suspended\n"); | 
 | 			break; | 
 | 		case MYRB_BGI_CANCELLED: | 
 | 			if (!sdev) | 
 | 				break; | 
 | 			sdev_printk(KERN_INFO, sdev, | 
 | 				    "Background Initialization Cancelled\n"); | 
 | 			break; | 
 | 		} | 
 | 		memcpy(&cb->bgi_status, bgi, sizeof(struct myrb_bgi_status)); | 
 | 		break; | 
 | 	case MYRB_STATUS_BGI_SUCCESS: | 
 | 		if (sdev && cb->bgi_status.status == MYRB_BGI_INPROGRESS) | 
 | 			sdev_printk(KERN_INFO, sdev, | 
 | 				    "Background Initialization Completed Successfully\n"); | 
 | 		cb->bgi_status.status = MYRB_BGI_INVALID; | 
 | 		break; | 
 | 	case MYRB_STATUS_BGI_ABORTED: | 
 | 		if (sdev && cb->bgi_status.status == MYRB_BGI_INPROGRESS) | 
 | 			sdev_printk(KERN_INFO, sdev, | 
 | 				    "Background Initialization Aborted\n"); | 
 | 		fallthrough; | 
 | 	case MYRB_STATUS_NO_BGI_INPROGRESS: | 
 | 		cb->bgi_status.status = MYRB_BGI_INVALID; | 
 | 		break; | 
 | 	} | 
 | 	if (sdev) | 
 | 		scsi_device_put(sdev); | 
 | 	dma_free_coherent(&cb->pdev->dev, sizeof(struct myrb_bgi_status), | 
 | 			  bgi, bgi_addr); | 
 | } | 
 |  | 
 | /* | 
 |  * myrb_hba_enquiry - updates the controller status | 
 |  * | 
 |  * Executes a DAC_V1_Enquiry command and updates the controller status. | 
 |  * | 
 |  * Return: command status | 
 |  */ | 
 | static unsigned short myrb_hba_enquiry(struct myrb_hba *cb) | 
 | { | 
 | 	struct myrb_enquiry old, *new; | 
 | 	unsigned short status; | 
 |  | 
 | 	memcpy(&old, cb->enquiry, sizeof(struct myrb_enquiry)); | 
 |  | 
 | 	status = myrb_exec_type3(cb, MYRB_CMD_ENQUIRY, cb->enquiry_addr); | 
 | 	if (status != MYRB_STATUS_SUCCESS) | 
 | 		return status; | 
 |  | 
 | 	new = cb->enquiry; | 
 | 	if (new->ldev_count > old.ldev_count) { | 
 | 		int ldev_num = old.ldev_count - 1; | 
 |  | 
 | 		while (++ldev_num < new->ldev_count) | 
 | 			shost_printk(KERN_CRIT, cb->host, | 
 | 				     "Logical Drive %d Now Exists\n", | 
 | 				     ldev_num); | 
 | 	} | 
 | 	if (new->ldev_count < old.ldev_count) { | 
 | 		int ldev_num = new->ldev_count - 1; | 
 |  | 
 | 		while (++ldev_num < old.ldev_count) | 
 | 			shost_printk(KERN_CRIT, cb->host, | 
 | 				     "Logical Drive %d No Longer Exists\n", | 
 | 				     ldev_num); | 
 | 	} | 
 | 	if (new->status.deferred != old.status.deferred) | 
 | 		shost_printk(KERN_CRIT, cb->host, | 
 | 			     "Deferred Write Error Flag is now %s\n", | 
 | 			     (new->status.deferred ? "TRUE" : "FALSE")); | 
 | 	if (new->ev_seq != old.ev_seq) { | 
 | 		cb->new_ev_seq = new->ev_seq; | 
 | 		cb->need_err_info = true; | 
 | 		shost_printk(KERN_INFO, cb->host, | 
 | 			     "Event log %d/%d (%d/%d) available\n", | 
 | 			     cb->old_ev_seq, cb->new_ev_seq, | 
 | 			     old.ev_seq, new->ev_seq); | 
 | 	} | 
 | 	if ((new->ldev_critical > 0 && | 
 | 	     new->ldev_critical != old.ldev_critical) || | 
 | 	    (new->ldev_offline > 0 && | 
 | 	     new->ldev_offline != old.ldev_offline) || | 
 | 	    (new->ldev_count != old.ldev_count)) { | 
 | 		shost_printk(KERN_INFO, cb->host, | 
 | 			     "Logical drive count changed (%d/%d/%d)\n", | 
 | 			     new->ldev_critical, | 
 | 			     new->ldev_offline, | 
 | 			     new->ldev_count); | 
 | 		cb->need_ldev_info = true; | 
 | 	} | 
 | 	if (new->pdev_dead > 0 || | 
 | 	    new->pdev_dead != old.pdev_dead || | 
 | 	    time_after_eq(jiffies, cb->secondary_monitor_time | 
 | 			  + MYRB_SECONDARY_MONITOR_INTERVAL)) { | 
 | 		cb->need_bgi_status = cb->bgi_status_supported; | 
 | 		cb->secondary_monitor_time = jiffies; | 
 | 	} | 
 | 	if (new->rbld == MYRB_STDBY_RBLD_IN_PROGRESS || | 
 | 	    new->rbld == MYRB_BG_RBLD_IN_PROGRESS || | 
 | 	    old.rbld == MYRB_STDBY_RBLD_IN_PROGRESS || | 
 | 	    old.rbld == MYRB_BG_RBLD_IN_PROGRESS) { | 
 | 		cb->need_rbld = true; | 
 | 		cb->rbld_first = (new->ldev_critical < old.ldev_critical); | 
 | 	} | 
 | 	if (old.rbld == MYRB_BG_CHECK_IN_PROGRESS) | 
 | 		switch (new->rbld) { | 
 | 		case MYRB_NO_STDBY_RBLD_OR_CHECK_IN_PROGRESS: | 
 | 			shost_printk(KERN_INFO, cb->host, | 
 | 				     "Consistency Check Completed Successfully\n"); | 
 | 			break; | 
 | 		case MYRB_STDBY_RBLD_IN_PROGRESS: | 
 | 		case MYRB_BG_RBLD_IN_PROGRESS: | 
 | 			break; | 
 | 		case MYRB_BG_CHECK_IN_PROGRESS: | 
 | 			cb->need_cc_status = true; | 
 | 			break; | 
 | 		case MYRB_STDBY_RBLD_COMPLETED_WITH_ERROR: | 
 | 			shost_printk(KERN_INFO, cb->host, | 
 | 				     "Consistency Check Completed with Error\n"); | 
 | 			break; | 
 | 		case MYRB_BG_RBLD_OR_CHECK_FAILED_DRIVE_FAILED: | 
 | 			shost_printk(KERN_INFO, cb->host, | 
 | 				     "Consistency Check Failed - Physical Device Failed\n"); | 
 | 			break; | 
 | 		case MYRB_BG_RBLD_OR_CHECK_FAILED_LDEV_FAILED: | 
 | 			shost_printk(KERN_INFO, cb->host, | 
 | 				     "Consistency Check Failed - Logical Drive Failed\n"); | 
 | 			break; | 
 | 		case MYRB_BG_RBLD_OR_CHECK_FAILED_OTHER: | 
 | 			shost_printk(KERN_INFO, cb->host, | 
 | 				     "Consistency Check Failed - Other Causes\n"); | 
 | 			break; | 
 | 		case MYRB_BG_RBLD_OR_CHECK_SUCCESS_TERMINATED: | 
 | 			shost_printk(KERN_INFO, cb->host, | 
 | 				     "Consistency Check Successfully Terminated\n"); | 
 | 			break; | 
 | 		} | 
 | 	else if (new->rbld == MYRB_BG_CHECK_IN_PROGRESS) | 
 | 		cb->need_cc_status = true; | 
 |  | 
 | 	return MYRB_STATUS_SUCCESS; | 
 | } | 
 |  | 
 | /* | 
 |  * myrb_set_pdev_state - sets the device state for a physical device | 
 |  * | 
 |  * Return: command status | 
 |  */ | 
 | static unsigned short myrb_set_pdev_state(struct myrb_hba *cb, | 
 | 		struct scsi_device *sdev, enum myrb_devstate state) | 
 | { | 
 | 	struct myrb_cmdblk *cmd_blk = &cb->dcmd_blk; | 
 | 	union myrb_cmd_mbox *mbox = &cmd_blk->mbox; | 
 | 	unsigned short status; | 
 |  | 
 | 	mutex_lock(&cb->dcmd_mutex); | 
 | 	mbox->type3D.opcode = MYRB_CMD_START_DEVICE; | 
 | 	mbox->type3D.id = MYRB_DCMD_TAG; | 
 | 	mbox->type3D.channel = sdev->channel; | 
 | 	mbox->type3D.target = sdev->id; | 
 | 	mbox->type3D.state = state & 0x1F; | 
 | 	status = myrb_exec_cmd(cb, cmd_blk); | 
 | 	mutex_unlock(&cb->dcmd_mutex); | 
 |  | 
 | 	return status; | 
 | } | 
 |  | 
 | /* | 
 |  * myrb_enable_mmio - enables the Memory Mailbox Interface | 
 |  * | 
 |  * PD and P controller types have no memory mailbox, but still need the | 
 |  * other dma mapped memory. | 
 |  * | 
 |  * Return: true on success, false otherwise. | 
 |  */ | 
 | static bool myrb_enable_mmio(struct myrb_hba *cb, mbox_mmio_init_t mmio_init_fn) | 
 | { | 
 | 	void __iomem *base = cb->io_base; | 
 | 	struct pci_dev *pdev = cb->pdev; | 
 | 	size_t err_table_size; | 
 | 	size_t ldev_info_size; | 
 | 	union myrb_cmd_mbox *cmd_mbox_mem; | 
 | 	struct myrb_stat_mbox *stat_mbox_mem; | 
 | 	union myrb_cmd_mbox mbox; | 
 | 	unsigned short status; | 
 |  | 
 | 	memset(&mbox, 0, sizeof(union myrb_cmd_mbox)); | 
 |  | 
 | 	if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) { | 
 | 		dev_err(&pdev->dev, "DMA mask out of range\n"); | 
 | 		return false; | 
 | 	} | 
 |  | 
 | 	cb->enquiry = dma_alloc_coherent(&pdev->dev, | 
 | 					 sizeof(struct myrb_enquiry), | 
 | 					 &cb->enquiry_addr, GFP_KERNEL); | 
 | 	if (!cb->enquiry) | 
 | 		return false; | 
 |  | 
 | 	err_table_size = sizeof(struct myrb_error_entry) * | 
 | 		MYRB_MAX_CHANNELS * MYRB_MAX_TARGETS; | 
 | 	cb->err_table = dma_alloc_coherent(&pdev->dev, err_table_size, | 
 | 					   &cb->err_table_addr, GFP_KERNEL); | 
 | 	if (!cb->err_table) | 
 | 		return false; | 
 |  | 
 | 	ldev_info_size = sizeof(struct myrb_ldev_info) * MYRB_MAX_LDEVS; | 
 | 	cb->ldev_info_buf = dma_alloc_coherent(&pdev->dev, ldev_info_size, | 
 | 					       &cb->ldev_info_addr, GFP_KERNEL); | 
 | 	if (!cb->ldev_info_buf) | 
 | 		return false; | 
 |  | 
 | 	/* | 
 | 	 * Skip mailbox initialisation for PD and P Controllers | 
 | 	 */ | 
 | 	if (!mmio_init_fn) | 
 | 		return true; | 
 |  | 
 | 	/* These are the base addresses for the command memory mailbox array */ | 
 | 	cb->cmd_mbox_size =  MYRB_CMD_MBOX_COUNT * sizeof(union myrb_cmd_mbox); | 
 | 	cb->first_cmd_mbox = dma_alloc_coherent(&pdev->dev, | 
 | 						cb->cmd_mbox_size, | 
 | 						&cb->cmd_mbox_addr, | 
 | 						GFP_KERNEL); | 
 | 	if (!cb->first_cmd_mbox) | 
 | 		return false; | 
 |  | 
 | 	cmd_mbox_mem = cb->first_cmd_mbox; | 
 | 	cmd_mbox_mem += MYRB_CMD_MBOX_COUNT - 1; | 
 | 	cb->last_cmd_mbox = cmd_mbox_mem; | 
 | 	cb->next_cmd_mbox = cb->first_cmd_mbox; | 
 | 	cb->prev_cmd_mbox1 = cb->last_cmd_mbox; | 
 | 	cb->prev_cmd_mbox2 = cb->last_cmd_mbox - 1; | 
 |  | 
 | 	/* These are the base addresses for the status memory mailbox array */ | 
 | 	cb->stat_mbox_size = MYRB_STAT_MBOX_COUNT * | 
 | 	    sizeof(struct myrb_stat_mbox); | 
 | 	cb->first_stat_mbox = dma_alloc_coherent(&pdev->dev, | 
 | 						 cb->stat_mbox_size, | 
 | 						 &cb->stat_mbox_addr, | 
 | 						 GFP_KERNEL); | 
 | 	if (!cb->first_stat_mbox) | 
 | 		return false; | 
 |  | 
 | 	stat_mbox_mem = cb->first_stat_mbox; | 
 | 	stat_mbox_mem += MYRB_STAT_MBOX_COUNT - 1; | 
 | 	cb->last_stat_mbox = stat_mbox_mem; | 
 | 	cb->next_stat_mbox = cb->first_stat_mbox; | 
 |  | 
 | 	/* Enable the Memory Mailbox Interface. */ | 
 | 	cb->dual_mode_interface = true; | 
 | 	mbox.typeX.opcode = 0x2B; | 
 | 	mbox.typeX.id = 0; | 
 | 	mbox.typeX.opcode2 = 0x14; | 
 | 	mbox.typeX.cmd_mbox_addr = cb->cmd_mbox_addr; | 
 | 	mbox.typeX.stat_mbox_addr = cb->stat_mbox_addr; | 
 |  | 
 | 	status = mmio_init_fn(pdev, base, &mbox); | 
 | 	if (status != MYRB_STATUS_SUCCESS) { | 
 | 		cb->dual_mode_interface = false; | 
 | 		mbox.typeX.opcode2 = 0x10; | 
 | 		status = mmio_init_fn(pdev, base, &mbox); | 
 | 		if (status != MYRB_STATUS_SUCCESS) { | 
 | 			dev_err(&pdev->dev, | 
 | 				"Failed to enable mailbox, statux %02X\n", | 
 | 				status); | 
 | 			return false; | 
 | 		} | 
 | 	} | 
 | 	return true; | 
 | } | 
 |  | 
 | /* | 
 |  * myrb_get_hba_config - reads the configuration information | 
 |  * | 
 |  * Reads the configuration information from the controller and | 
 |  * initializes the controller structure. | 
 |  * | 
 |  * Return: 0 on success, errno otherwise | 
 |  */ | 
 | static int myrb_get_hba_config(struct myrb_hba *cb) | 
 | { | 
 | 	struct myrb_enquiry2 *enquiry2; | 
 | 	dma_addr_t enquiry2_addr; | 
 | 	struct myrb_config2 *config2; | 
 | 	dma_addr_t config2_addr; | 
 | 	struct Scsi_Host *shost = cb->host; | 
 | 	struct pci_dev *pdev = cb->pdev; | 
 | 	int pchan_max = 0, pchan_cur = 0; | 
 | 	unsigned short status; | 
 | 	int ret = -ENODEV, memsize = 0; | 
 |  | 
 | 	enquiry2 = dma_alloc_coherent(&pdev->dev, sizeof(struct myrb_enquiry2), | 
 | 				      &enquiry2_addr, GFP_KERNEL); | 
 | 	if (!enquiry2) { | 
 | 		shost_printk(KERN_ERR, cb->host, | 
 | 			     "Failed to allocate V1 enquiry2 memory\n"); | 
 | 		return -ENOMEM; | 
 | 	} | 
 | 	config2 = dma_alloc_coherent(&pdev->dev, sizeof(struct myrb_config2), | 
 | 				     &config2_addr, GFP_KERNEL); | 
 | 	if (!config2) { | 
 | 		shost_printk(KERN_ERR, cb->host, | 
 | 			     "Failed to allocate V1 config2 memory\n"); | 
 | 		dma_free_coherent(&pdev->dev, sizeof(struct myrb_enquiry2), | 
 | 				  enquiry2, enquiry2_addr); | 
 | 		return -ENOMEM; | 
 | 	} | 
 | 	mutex_lock(&cb->dma_mutex); | 
 | 	status = myrb_hba_enquiry(cb); | 
 | 	mutex_unlock(&cb->dma_mutex); | 
 | 	if (status != MYRB_STATUS_SUCCESS) { | 
 | 		shost_printk(KERN_WARNING, cb->host, | 
 | 			     "Failed it issue V1 Enquiry\n"); | 
 | 		goto out_free; | 
 | 	} | 
 |  | 
 | 	status = myrb_exec_type3(cb, MYRB_CMD_ENQUIRY2, enquiry2_addr); | 
 | 	if (status != MYRB_STATUS_SUCCESS) { | 
 | 		shost_printk(KERN_WARNING, cb->host, | 
 | 			     "Failed to issue V1 Enquiry2\n"); | 
 | 		goto out_free; | 
 | 	} | 
 |  | 
 | 	status = myrb_exec_type3(cb, MYRB_CMD_READ_CONFIG2, config2_addr); | 
 | 	if (status != MYRB_STATUS_SUCCESS) { | 
 | 		shost_printk(KERN_WARNING, cb->host, | 
 | 			     "Failed to issue ReadConfig2\n"); | 
 | 		goto out_free; | 
 | 	} | 
 |  | 
 | 	status = myrb_get_ldev_info(cb); | 
 | 	if (status != MYRB_STATUS_SUCCESS) { | 
 | 		shost_printk(KERN_WARNING, cb->host, | 
 | 			     "Failed to get logical drive information\n"); | 
 | 		goto out_free; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * Initialize the Controller Model Name and Full Model Name fields. | 
 | 	 */ | 
 | 	switch (enquiry2->hw.sub_model) { | 
 | 	case DAC960_V1_P_PD_PU: | 
 | 		if (enquiry2->scsi_cap.bus_speed == MYRB_SCSI_SPEED_ULTRA) | 
 | 			strcpy(cb->model_name, "DAC960PU"); | 
 | 		else | 
 | 			strcpy(cb->model_name, "DAC960PD"); | 
 | 		break; | 
 | 	case DAC960_V1_PL: | 
 | 		strcpy(cb->model_name, "DAC960PL"); | 
 | 		break; | 
 | 	case DAC960_V1_PG: | 
 | 		strcpy(cb->model_name, "DAC960PG"); | 
 | 		break; | 
 | 	case DAC960_V1_PJ: | 
 | 		strcpy(cb->model_name, "DAC960PJ"); | 
 | 		break; | 
 | 	case DAC960_V1_PR: | 
 | 		strcpy(cb->model_name, "DAC960PR"); | 
 | 		break; | 
 | 	case DAC960_V1_PT: | 
 | 		strcpy(cb->model_name, "DAC960PT"); | 
 | 		break; | 
 | 	case DAC960_V1_PTL0: | 
 | 		strcpy(cb->model_name, "DAC960PTL0"); | 
 | 		break; | 
 | 	case DAC960_V1_PRL: | 
 | 		strcpy(cb->model_name, "DAC960PRL"); | 
 | 		break; | 
 | 	case DAC960_V1_PTL1: | 
 | 		strcpy(cb->model_name, "DAC960PTL1"); | 
 | 		break; | 
 | 	case DAC960_V1_1164P: | 
 | 		strcpy(cb->model_name, "eXtremeRAID 1100"); | 
 | 		break; | 
 | 	default: | 
 | 		shost_printk(KERN_WARNING, cb->host, | 
 | 			     "Unknown Model %X\n", | 
 | 			     enquiry2->hw.sub_model); | 
 | 		goto out; | 
 | 	} | 
 | 	/* | 
 | 	 * Initialize the Controller Firmware Version field and verify that it | 
 | 	 * is a supported firmware version. | 
 | 	 * The supported firmware versions are: | 
 | 	 * | 
 | 	 * DAC1164P		    5.06 and above | 
 | 	 * DAC960PTL/PRL/PJ/PG	    4.06 and above | 
 | 	 * DAC960PU/PD/PL	    3.51 and above | 
 | 	 * DAC960PU/PD/PL/P	    2.73 and above | 
 | 	 */ | 
 | #if defined(CONFIG_ALPHA) | 
 | 	/* | 
 | 	 * DEC Alpha machines were often equipped with DAC960 cards that were | 
 | 	 * OEMed from Mylex, and had their own custom firmware. Version 2.70, | 
 | 	 * the last custom FW revision to be released by DEC for these older | 
 | 	 * controllers, appears to work quite well with this driver. | 
 | 	 * | 
 | 	 * Cards tested successfully were several versions each of the PD and | 
 | 	 * PU, called by DEC the KZPSC and KZPAC, respectively, and having | 
 | 	 * the Manufacturer Numbers (from Mylex), usually on a sticker on the | 
 | 	 * back of the board, of: | 
 | 	 * | 
 | 	 * KZPSC:  D040347 (1-channel) or D040348 (2-channel) | 
 | 	 *         or D040349 (3-channel) | 
 | 	 * KZPAC:  D040395 (1-channel) or D040396 (2-channel) | 
 | 	 *         or D040397 (3-channel) | 
 | 	 */ | 
 | # define FIRMWARE_27X	"2.70" | 
 | #else | 
 | # define FIRMWARE_27X	"2.73" | 
 | #endif | 
 |  | 
 | 	if (enquiry2->fw.major_version == 0) { | 
 | 		enquiry2->fw.major_version = cb->enquiry->fw_major_version; | 
 | 		enquiry2->fw.minor_version = cb->enquiry->fw_minor_version; | 
 | 		enquiry2->fw.firmware_type = '0'; | 
 | 		enquiry2->fw.turn_id = 0; | 
 | 	} | 
 | 	snprintf(cb->fw_version, sizeof(cb->fw_version), | 
 | 		"%u.%02u-%c-%02u", | 
 | 		enquiry2->fw.major_version, | 
 | 		enquiry2->fw.minor_version, | 
 | 		enquiry2->fw.firmware_type, | 
 | 		enquiry2->fw.turn_id); | 
 | 	if (!((enquiry2->fw.major_version == 5 && | 
 | 	       enquiry2->fw.minor_version >= 6) || | 
 | 	      (enquiry2->fw.major_version == 4 && | 
 | 	       enquiry2->fw.minor_version >= 6) || | 
 | 	      (enquiry2->fw.major_version == 3 && | 
 | 	       enquiry2->fw.minor_version >= 51) || | 
 | 	      (enquiry2->fw.major_version == 2 && | 
 | 	       strcmp(cb->fw_version, FIRMWARE_27X) >= 0))) { | 
 | 		shost_printk(KERN_WARNING, cb->host, | 
 | 			"Firmware Version '%s' unsupported\n", | 
 | 			cb->fw_version); | 
 | 		goto out; | 
 | 	} | 
 | 	/* | 
 | 	 * Initialize the Channels, Targets, Memory Size, and SAF-TE | 
 | 	 * Enclosure Management Enabled fields. | 
 | 	 */ | 
 | 	switch (enquiry2->hw.model) { | 
 | 	case MYRB_5_CHANNEL_BOARD: | 
 | 		pchan_max = 5; | 
 | 		break; | 
 | 	case MYRB_3_CHANNEL_BOARD: | 
 | 	case MYRB_3_CHANNEL_ASIC_DAC: | 
 | 		pchan_max = 3; | 
 | 		break; | 
 | 	case MYRB_2_CHANNEL_BOARD: | 
 | 		pchan_max = 2; | 
 | 		break; | 
 | 	default: | 
 | 		pchan_max = enquiry2->cfg_chan; | 
 | 		break; | 
 | 	} | 
 | 	pchan_cur = enquiry2->cur_chan; | 
 | 	if (enquiry2->scsi_cap.bus_width == MYRB_WIDTH_WIDE_32BIT) | 
 | 		cb->bus_width = 32; | 
 | 	else if (enquiry2->scsi_cap.bus_width == MYRB_WIDTH_WIDE_16BIT) | 
 | 		cb->bus_width = 16; | 
 | 	else | 
 | 		cb->bus_width = 8; | 
 | 	cb->ldev_block_size = enquiry2->ldev_block_size; | 
 | 	shost->max_channel = pchan_cur; | 
 | 	shost->max_id = enquiry2->max_targets; | 
 | 	memsize = enquiry2->mem_size >> 20; | 
 | 	cb->safte_enabled = (enquiry2->fault_mgmt == MYRB_FAULT_SAFTE); | 
 | 	/* | 
 | 	 * Initialize the Controller Queue Depth, Driver Queue Depth, | 
 | 	 * Logical Drive Count, Maximum Blocks per Command, Controller | 
 | 	 * Scatter/Gather Limit, and Driver Scatter/Gather Limit. | 
 | 	 * The Driver Queue Depth must be at most one less than the | 
 | 	 * Controller Queue Depth to allow for an automatic drive | 
 | 	 * rebuild operation. | 
 | 	 */ | 
 | 	shost->can_queue = cb->enquiry->max_tcq; | 
 | 	if (shost->can_queue < 3) | 
 | 		shost->can_queue = enquiry2->max_cmds; | 
 | 	if (shost->can_queue < 3) | 
 | 		/* Play safe and disable TCQ */ | 
 | 		shost->can_queue = 1; | 
 |  | 
 | 	if (shost->can_queue > MYRB_CMD_MBOX_COUNT - 2) | 
 | 		shost->can_queue = MYRB_CMD_MBOX_COUNT - 2; | 
 | 	shost->max_sectors = enquiry2->max_sectors; | 
 | 	shost->sg_tablesize = enquiry2->max_sge; | 
 | 	if (shost->sg_tablesize > MYRB_SCATTER_GATHER_LIMIT) | 
 | 		shost->sg_tablesize = MYRB_SCATTER_GATHER_LIMIT; | 
 | 	/* | 
 | 	 * Initialize the Stripe Size, Segment Size, and Geometry Translation. | 
 | 	 */ | 
 | 	cb->stripe_size = config2->blocks_per_stripe * config2->block_factor | 
 | 		>> (10 - MYRB_BLKSIZE_BITS); | 
 | 	cb->segment_size = config2->blocks_per_cacheline * config2->block_factor | 
 | 		>> (10 - MYRB_BLKSIZE_BITS); | 
 | 	/* Assume 255/63 translation */ | 
 | 	cb->ldev_geom_heads = 255; | 
 | 	cb->ldev_geom_sectors = 63; | 
 | 	if (config2->drive_geometry) { | 
 | 		cb->ldev_geom_heads = 128; | 
 | 		cb->ldev_geom_sectors = 32; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * Initialize the Background Initialization Status. | 
 | 	 */ | 
 | 	if ((cb->fw_version[0] == '4' && | 
 | 	     strcmp(cb->fw_version, "4.08") >= 0) || | 
 | 	    (cb->fw_version[0] == '5' && | 
 | 	     strcmp(cb->fw_version, "5.08") >= 0)) { | 
 | 		cb->bgi_status_supported = true; | 
 | 		myrb_bgi_control(cb); | 
 | 	} | 
 | 	cb->last_rbld_status = MYRB_NO_STDBY_RBLD_OR_CHECK_IN_PROGRESS; | 
 | 	ret = 0; | 
 |  | 
 | out: | 
 | 	shost_printk(KERN_INFO, cb->host, | 
 | 		"Configuring %s PCI RAID Controller\n", cb->model_name); | 
 | 	shost_printk(KERN_INFO, cb->host, | 
 | 		"  Firmware Version: %s, Memory Size: %dMB\n", | 
 | 		cb->fw_version, memsize); | 
 | 	if (cb->io_addr == 0) | 
 | 		shost_printk(KERN_INFO, cb->host, | 
 | 			"  I/O Address: n/a, PCI Address: 0x%lX, IRQ Channel: %d\n", | 
 | 			(unsigned long)cb->pci_addr, cb->irq); | 
 | 	else | 
 | 		shost_printk(KERN_INFO, cb->host, | 
 | 			"  I/O Address: 0x%lX, PCI Address: 0x%lX, IRQ Channel: %d\n", | 
 | 			(unsigned long)cb->io_addr, (unsigned long)cb->pci_addr, | 
 | 			cb->irq); | 
 | 	shost_printk(KERN_INFO, cb->host, | 
 | 		"  Controller Queue Depth: %d, Maximum Blocks per Command: %d\n", | 
 | 		cb->host->can_queue, cb->host->max_sectors); | 
 | 	shost_printk(KERN_INFO, cb->host, | 
 | 		     "  Driver Queue Depth: %d, Scatter/Gather Limit: %d of %d Segments\n", | 
 | 		     cb->host->can_queue, cb->host->sg_tablesize, | 
 | 		     MYRB_SCATTER_GATHER_LIMIT); | 
 | 	shost_printk(KERN_INFO, cb->host, | 
 | 		     "  Stripe Size: %dKB, Segment Size: %dKB, BIOS Geometry: %d/%d%s\n", | 
 | 		     cb->stripe_size, cb->segment_size, | 
 | 		     cb->ldev_geom_heads, cb->ldev_geom_sectors, | 
 | 		     cb->safte_enabled ? | 
 | 		     "  SAF-TE Enclosure Management Enabled" : ""); | 
 | 	shost_printk(KERN_INFO, cb->host, | 
 | 		     "  Physical: %d/%d channels %d/%d/%d devices\n", | 
 | 		     pchan_cur, pchan_max, 0, cb->enquiry->pdev_dead, | 
 | 		     cb->host->max_id); | 
 |  | 
 | 	shost_printk(KERN_INFO, cb->host, | 
 | 		     "  Logical: 1/1 channels, %d/%d disks\n", | 
 | 		     cb->enquiry->ldev_count, MYRB_MAX_LDEVS); | 
 |  | 
 | out_free: | 
 | 	dma_free_coherent(&pdev->dev, sizeof(struct myrb_enquiry2), | 
 | 			  enquiry2, enquiry2_addr); | 
 | 	dma_free_coherent(&pdev->dev, sizeof(struct myrb_config2), | 
 | 			  config2, config2_addr); | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | /* | 
 |  * myrb_unmap - unmaps controller structures | 
 |  */ | 
 | static void myrb_unmap(struct myrb_hba *cb) | 
 | { | 
 | 	if (cb->ldev_info_buf) { | 
 | 		size_t ldev_info_size = sizeof(struct myrb_ldev_info) * | 
 | 			MYRB_MAX_LDEVS; | 
 | 		dma_free_coherent(&cb->pdev->dev, ldev_info_size, | 
 | 				  cb->ldev_info_buf, cb->ldev_info_addr); | 
 | 		cb->ldev_info_buf = NULL; | 
 | 	} | 
 | 	if (cb->err_table) { | 
 | 		size_t err_table_size = sizeof(struct myrb_error_entry) * | 
 | 			MYRB_MAX_CHANNELS * MYRB_MAX_TARGETS; | 
 | 		dma_free_coherent(&cb->pdev->dev, err_table_size, | 
 | 				  cb->err_table, cb->err_table_addr); | 
 | 		cb->err_table = NULL; | 
 | 	} | 
 | 	if (cb->enquiry) { | 
 | 		dma_free_coherent(&cb->pdev->dev, sizeof(struct myrb_enquiry), | 
 | 				  cb->enquiry, cb->enquiry_addr); | 
 | 		cb->enquiry = NULL; | 
 | 	} | 
 | 	if (cb->first_stat_mbox) { | 
 | 		dma_free_coherent(&cb->pdev->dev, cb->stat_mbox_size, | 
 | 				  cb->first_stat_mbox, cb->stat_mbox_addr); | 
 | 		cb->first_stat_mbox = NULL; | 
 | 	} | 
 | 	if (cb->first_cmd_mbox) { | 
 | 		dma_free_coherent(&cb->pdev->dev, cb->cmd_mbox_size, | 
 | 				  cb->first_cmd_mbox, cb->cmd_mbox_addr); | 
 | 		cb->first_cmd_mbox = NULL; | 
 | 	} | 
 | } | 
 |  | 
 | /* | 
 |  * myrb_cleanup - cleanup controller structures | 
 |  */ | 
 | static void myrb_cleanup(struct myrb_hba *cb) | 
 | { | 
 | 	struct pci_dev *pdev = cb->pdev; | 
 |  | 
 | 	/* Free the memory mailbox, status, and related structures */ | 
 | 	myrb_unmap(cb); | 
 |  | 
 | 	if (cb->mmio_base) { | 
 | 		if (cb->disable_intr) | 
 | 			cb->disable_intr(cb->io_base); | 
 | 		iounmap(cb->mmio_base); | 
 | 	} | 
 | 	if (cb->irq) | 
 | 		free_irq(cb->irq, cb); | 
 | 	if (cb->io_addr) | 
 | 		release_region(cb->io_addr, 0x80); | 
 | 	pci_set_drvdata(pdev, NULL); | 
 | 	pci_disable_device(pdev); | 
 | 	scsi_host_put(cb->host); | 
 | } | 
 |  | 
 | static int myrb_host_reset(struct scsi_cmnd *scmd) | 
 | { | 
 | 	struct Scsi_Host *shost = scmd->device->host; | 
 | 	struct myrb_hba *cb = shost_priv(shost); | 
 |  | 
 | 	cb->reset(cb->io_base); | 
 | 	return SUCCESS; | 
 | } | 
 |  | 
 | static int myrb_pthru_queuecommand(struct Scsi_Host *shost, | 
 | 		struct scsi_cmnd *scmd) | 
 | { | 
 | 	struct request *rq = scsi_cmd_to_rq(scmd); | 
 | 	struct myrb_hba *cb = shost_priv(shost); | 
 | 	struct myrb_cmdblk *cmd_blk = scsi_cmd_priv(scmd); | 
 | 	union myrb_cmd_mbox *mbox = &cmd_blk->mbox; | 
 | 	struct myrb_dcdb *dcdb; | 
 | 	dma_addr_t dcdb_addr; | 
 | 	struct scsi_device *sdev = scmd->device; | 
 | 	struct scatterlist *sgl; | 
 | 	unsigned long flags; | 
 | 	int nsge; | 
 |  | 
 | 	myrb_reset_cmd(cmd_blk); | 
 | 	dcdb = dma_pool_alloc(cb->dcdb_pool, GFP_ATOMIC, &dcdb_addr); | 
 | 	if (!dcdb) | 
 | 		return SCSI_MLQUEUE_HOST_BUSY; | 
 | 	nsge = scsi_dma_map(scmd); | 
 | 	if (nsge > 1) { | 
 | 		dma_pool_free(cb->dcdb_pool, dcdb, dcdb_addr); | 
 | 		scmd->result = (DID_ERROR << 16); | 
 | 		scsi_done(scmd); | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	mbox->type3.opcode = MYRB_CMD_DCDB; | 
 | 	mbox->type3.id = rq->tag + 3; | 
 | 	mbox->type3.addr = dcdb_addr; | 
 | 	dcdb->channel = sdev->channel; | 
 | 	dcdb->target = sdev->id; | 
 | 	switch (scmd->sc_data_direction) { | 
 | 	case DMA_NONE: | 
 | 		dcdb->data_xfer = MYRB_DCDB_XFER_NONE; | 
 | 		break; | 
 | 	case DMA_TO_DEVICE: | 
 | 		dcdb->data_xfer = MYRB_DCDB_XFER_SYSTEM_TO_DEVICE; | 
 | 		break; | 
 | 	case DMA_FROM_DEVICE: | 
 | 		dcdb->data_xfer = MYRB_DCDB_XFER_DEVICE_TO_SYSTEM; | 
 | 		break; | 
 | 	default: | 
 | 		dcdb->data_xfer = MYRB_DCDB_XFER_ILLEGAL; | 
 | 		break; | 
 | 	} | 
 | 	dcdb->early_status = false; | 
 | 	if (rq->timeout <= 10) | 
 | 		dcdb->timeout = MYRB_DCDB_TMO_10_SECS; | 
 | 	else if (rq->timeout <= 60) | 
 | 		dcdb->timeout = MYRB_DCDB_TMO_60_SECS; | 
 | 	else if (rq->timeout <= 600) | 
 | 		dcdb->timeout = MYRB_DCDB_TMO_10_MINS; | 
 | 	else | 
 | 		dcdb->timeout = MYRB_DCDB_TMO_24_HRS; | 
 | 	dcdb->no_autosense = false; | 
 | 	dcdb->allow_disconnect = true; | 
 | 	sgl = scsi_sglist(scmd); | 
 | 	dcdb->dma_addr = sg_dma_address(sgl); | 
 | 	if (sg_dma_len(sgl) > USHRT_MAX) { | 
 | 		dcdb->xfer_len_lo = sg_dma_len(sgl) & 0xffff; | 
 | 		dcdb->xfer_len_hi4 = sg_dma_len(sgl) >> 16; | 
 | 	} else { | 
 | 		dcdb->xfer_len_lo = sg_dma_len(sgl); | 
 | 		dcdb->xfer_len_hi4 = 0; | 
 | 	} | 
 | 	dcdb->cdb_len = scmd->cmd_len; | 
 | 	dcdb->sense_len = sizeof(dcdb->sense); | 
 | 	memcpy(&dcdb->cdb, scmd->cmnd, scmd->cmd_len); | 
 |  | 
 | 	spin_lock_irqsave(&cb->queue_lock, flags); | 
 | 	cb->qcmd(cb, cmd_blk); | 
 | 	spin_unlock_irqrestore(&cb->queue_lock, flags); | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void myrb_inquiry(struct myrb_hba *cb, | 
 | 		struct scsi_cmnd *scmd) | 
 | { | 
 | 	unsigned char inq[36] = { | 
 | 		0x00, 0x00, 0x03, 0x02, 0x20, 0x00, 0x01, 0x00, | 
 | 		0x4d, 0x59, 0x4c, 0x45, 0x58, 0x20, 0x20, 0x20, | 
 | 		0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, | 
 | 		0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, | 
 | 		0x20, 0x20, 0x20, 0x20, | 
 | 	}; | 
 |  | 
 | 	if (cb->bus_width > 16) | 
 | 		inq[7] |= 1 << 6; | 
 | 	if (cb->bus_width > 8) | 
 | 		inq[7] |= 1 << 5; | 
 | 	memcpy(&inq[16], cb->model_name, 16); | 
 | 	memcpy(&inq[32], cb->fw_version, 1); | 
 | 	memcpy(&inq[33], &cb->fw_version[2], 2); | 
 | 	memcpy(&inq[35], &cb->fw_version[7], 1); | 
 |  | 
 | 	scsi_sg_copy_from_buffer(scmd, (void *)inq, 36); | 
 | } | 
 |  | 
 | static void | 
 | myrb_mode_sense(struct myrb_hba *cb, struct scsi_cmnd *scmd, | 
 | 		struct myrb_ldev_info *ldev_info) | 
 | { | 
 | 	unsigned char modes[32], *mode_pg; | 
 | 	bool dbd; | 
 | 	size_t mode_len; | 
 |  | 
 | 	dbd = (scmd->cmnd[1] & 0x08) == 0x08; | 
 | 	if (dbd) { | 
 | 		mode_len = 24; | 
 | 		mode_pg = &modes[4]; | 
 | 	} else { | 
 | 		mode_len = 32; | 
 | 		mode_pg = &modes[12]; | 
 | 	} | 
 | 	memset(modes, 0, sizeof(modes)); | 
 | 	modes[0] = mode_len - 1; | 
 | 	if (!dbd) { | 
 | 		unsigned char *block_desc = &modes[4]; | 
 |  | 
 | 		modes[3] = 8; | 
 | 		put_unaligned_be32(ldev_info->size, &block_desc[0]); | 
 | 		put_unaligned_be32(cb->ldev_block_size, &block_desc[5]); | 
 | 	} | 
 | 	mode_pg[0] = 0x08; | 
 | 	mode_pg[1] = 0x12; | 
 | 	if (ldev_info->wb_enabled) | 
 | 		mode_pg[2] |= 0x04; | 
 | 	if (cb->segment_size) { | 
 | 		mode_pg[2] |= 0x08; | 
 | 		put_unaligned_be16(cb->segment_size, &mode_pg[14]); | 
 | 	} | 
 |  | 
 | 	scsi_sg_copy_from_buffer(scmd, modes, mode_len); | 
 | } | 
 |  | 
 | static void myrb_request_sense(struct myrb_hba *cb, | 
 | 		struct scsi_cmnd *scmd) | 
 | { | 
 | 	scsi_build_sense(scmd, 0, NO_SENSE, 0, 0); | 
 | 	scsi_sg_copy_from_buffer(scmd, scmd->sense_buffer, | 
 | 				 SCSI_SENSE_BUFFERSIZE); | 
 | } | 
 |  | 
 | static void myrb_read_capacity(struct myrb_hba *cb, struct scsi_cmnd *scmd, | 
 | 		struct myrb_ldev_info *ldev_info) | 
 | { | 
 | 	unsigned char data[8]; | 
 |  | 
 | 	dev_dbg(&scmd->device->sdev_gendev, | 
 | 		"Capacity %u, blocksize %u\n", | 
 | 		ldev_info->size, cb->ldev_block_size); | 
 | 	put_unaligned_be32(ldev_info->size - 1, &data[0]); | 
 | 	put_unaligned_be32(cb->ldev_block_size, &data[4]); | 
 | 	scsi_sg_copy_from_buffer(scmd, data, 8); | 
 | } | 
 |  | 
 | static int myrb_ldev_queuecommand(struct Scsi_Host *shost, | 
 | 		struct scsi_cmnd *scmd) | 
 | { | 
 | 	struct myrb_hba *cb = shost_priv(shost); | 
 | 	struct myrb_cmdblk *cmd_blk = scsi_cmd_priv(scmd); | 
 | 	union myrb_cmd_mbox *mbox = &cmd_blk->mbox; | 
 | 	struct myrb_ldev_info *ldev_info; | 
 | 	struct scsi_device *sdev = scmd->device; | 
 | 	struct scatterlist *sgl; | 
 | 	unsigned long flags; | 
 | 	u64 lba; | 
 | 	u32 block_cnt; | 
 | 	int nsge; | 
 |  | 
 | 	ldev_info = sdev->hostdata; | 
 | 	if (ldev_info->state != MYRB_DEVICE_ONLINE && | 
 | 	    ldev_info->state != MYRB_DEVICE_WO) { | 
 | 		dev_dbg(&shost->shost_gendev, "ldev %u in state %x, skip\n", | 
 | 			sdev->id, ldev_info ? ldev_info->state : 0xff); | 
 | 		scmd->result = (DID_BAD_TARGET << 16); | 
 | 		scsi_done(scmd); | 
 | 		return 0; | 
 | 	} | 
 | 	switch (scmd->cmnd[0]) { | 
 | 	case TEST_UNIT_READY: | 
 | 		scmd->result = (DID_OK << 16); | 
 | 		scsi_done(scmd); | 
 | 		return 0; | 
 | 	case INQUIRY: | 
 | 		if (scmd->cmnd[1] & 1) { | 
 | 			/* Illegal request, invalid field in CDB */ | 
 | 			scsi_build_sense(scmd, 0, ILLEGAL_REQUEST, 0x24, 0); | 
 | 		} else { | 
 | 			myrb_inquiry(cb, scmd); | 
 | 			scmd->result = (DID_OK << 16); | 
 | 		} | 
 | 		scsi_done(scmd); | 
 | 		return 0; | 
 | 	case SYNCHRONIZE_CACHE: | 
 | 		scmd->result = (DID_OK << 16); | 
 | 		scsi_done(scmd); | 
 | 		return 0; | 
 | 	case MODE_SENSE: | 
 | 		if ((scmd->cmnd[2] & 0x3F) != 0x3F && | 
 | 		    (scmd->cmnd[2] & 0x3F) != 0x08) { | 
 | 			/* Illegal request, invalid field in CDB */ | 
 | 			scsi_build_sense(scmd, 0, ILLEGAL_REQUEST, 0x24, 0); | 
 | 		} else { | 
 | 			myrb_mode_sense(cb, scmd, ldev_info); | 
 | 			scmd->result = (DID_OK << 16); | 
 | 		} | 
 | 		scsi_done(scmd); | 
 | 		return 0; | 
 | 	case READ_CAPACITY: | 
 | 		if ((scmd->cmnd[1] & 1) || | 
 | 		    (scmd->cmnd[8] & 1)) { | 
 | 			/* Illegal request, invalid field in CDB */ | 
 | 			scsi_build_sense(scmd, 0, ILLEGAL_REQUEST, 0x24, 0); | 
 | 			scsi_done(scmd); | 
 | 			return 0; | 
 | 		} | 
 | 		lba = get_unaligned_be32(&scmd->cmnd[2]); | 
 | 		if (lba) { | 
 | 			/* Illegal request, invalid field in CDB */ | 
 | 			scsi_build_sense(scmd, 0, ILLEGAL_REQUEST, 0x24, 0); | 
 | 			scsi_done(scmd); | 
 | 			return 0; | 
 | 		} | 
 | 		myrb_read_capacity(cb, scmd, ldev_info); | 
 | 		scsi_done(scmd); | 
 | 		return 0; | 
 | 	case REQUEST_SENSE: | 
 | 		myrb_request_sense(cb, scmd); | 
 | 		scmd->result = (DID_OK << 16); | 
 | 		return 0; | 
 | 	case SEND_DIAGNOSTIC: | 
 | 		if (scmd->cmnd[1] != 0x04) { | 
 | 			/* Illegal request, invalid field in CDB */ | 
 | 			scsi_build_sense(scmd, 0, ILLEGAL_REQUEST, 0x24, 0); | 
 | 		} else { | 
 | 			/* Assume good status */ | 
 | 			scmd->result = (DID_OK << 16); | 
 | 		} | 
 | 		scsi_done(scmd); | 
 | 		return 0; | 
 | 	case READ_6: | 
 | 		if (ldev_info->state == MYRB_DEVICE_WO) { | 
 | 			/* Data protect, attempt to read invalid data */ | 
 | 			scsi_build_sense(scmd, 0, DATA_PROTECT, 0x21, 0x06); | 
 | 			scsi_done(scmd); | 
 | 			return 0; | 
 | 		} | 
 | 		fallthrough; | 
 | 	case WRITE_6: | 
 | 		lba = (((scmd->cmnd[1] & 0x1F) << 16) | | 
 | 		       (scmd->cmnd[2] << 8) | | 
 | 		       scmd->cmnd[3]); | 
 | 		block_cnt = scmd->cmnd[4]; | 
 | 		break; | 
 | 	case READ_10: | 
 | 		if (ldev_info->state == MYRB_DEVICE_WO) { | 
 | 			/* Data protect, attempt to read invalid data */ | 
 | 			scsi_build_sense(scmd, 0, DATA_PROTECT, 0x21, 0x06); | 
 | 			scsi_done(scmd); | 
 | 			return 0; | 
 | 		} | 
 | 		fallthrough; | 
 | 	case WRITE_10: | 
 | 	case VERIFY:		/* 0x2F */ | 
 | 	case WRITE_VERIFY:	/* 0x2E */ | 
 | 		lba = get_unaligned_be32(&scmd->cmnd[2]); | 
 | 		block_cnt = get_unaligned_be16(&scmd->cmnd[7]); | 
 | 		break; | 
 | 	case READ_12: | 
 | 		if (ldev_info->state == MYRB_DEVICE_WO) { | 
 | 			/* Data protect, attempt to read invalid data */ | 
 | 			scsi_build_sense(scmd, 0, DATA_PROTECT, 0x21, 0x06); | 
 | 			scsi_done(scmd); | 
 | 			return 0; | 
 | 		} | 
 | 		fallthrough; | 
 | 	case WRITE_12: | 
 | 	case VERIFY_12: /* 0xAF */ | 
 | 	case WRITE_VERIFY_12:	/* 0xAE */ | 
 | 		lba = get_unaligned_be32(&scmd->cmnd[2]); | 
 | 		block_cnt = get_unaligned_be32(&scmd->cmnd[6]); | 
 | 		break; | 
 | 	default: | 
 | 		/* Illegal request, invalid opcode */ | 
 | 		scsi_build_sense(scmd, 0, ILLEGAL_REQUEST, 0x20, 0); | 
 | 		scsi_done(scmd); | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	myrb_reset_cmd(cmd_blk); | 
 | 	mbox->type5.id = scsi_cmd_to_rq(scmd)->tag + 3; | 
 | 	if (scmd->sc_data_direction == DMA_NONE) | 
 | 		goto submit; | 
 | 	nsge = scsi_dma_map(scmd); | 
 | 	if (nsge == 1) { | 
 | 		sgl = scsi_sglist(scmd); | 
 | 		if (scmd->sc_data_direction == DMA_FROM_DEVICE) | 
 | 			mbox->type5.opcode = MYRB_CMD_READ; | 
 | 		else | 
 | 			mbox->type5.opcode = MYRB_CMD_WRITE; | 
 |  | 
 | 		mbox->type5.ld.xfer_len = block_cnt; | 
 | 		mbox->type5.ld.ldev_num = sdev->id; | 
 | 		mbox->type5.lba = lba; | 
 | 		mbox->type5.addr = (u32)sg_dma_address(sgl); | 
 | 	} else { | 
 | 		struct myrb_sge *hw_sgl; | 
 | 		dma_addr_t hw_sgl_addr; | 
 | 		int i; | 
 |  | 
 | 		hw_sgl = dma_pool_alloc(cb->sg_pool, GFP_ATOMIC, &hw_sgl_addr); | 
 | 		if (!hw_sgl) | 
 | 			return SCSI_MLQUEUE_HOST_BUSY; | 
 |  | 
 | 		cmd_blk->sgl = hw_sgl; | 
 | 		cmd_blk->sgl_addr = hw_sgl_addr; | 
 |  | 
 | 		if (scmd->sc_data_direction == DMA_FROM_DEVICE) | 
 | 			mbox->type5.opcode = MYRB_CMD_READ_SG; | 
 | 		else | 
 | 			mbox->type5.opcode = MYRB_CMD_WRITE_SG; | 
 |  | 
 | 		mbox->type5.ld.xfer_len = block_cnt; | 
 | 		mbox->type5.ld.ldev_num = sdev->id; | 
 | 		mbox->type5.lba = lba; | 
 | 		mbox->type5.addr = hw_sgl_addr; | 
 | 		mbox->type5.sg_count = nsge; | 
 |  | 
 | 		scsi_for_each_sg(scmd, sgl, nsge, i) { | 
 | 			hw_sgl->sge_addr = (u32)sg_dma_address(sgl); | 
 | 			hw_sgl->sge_count = (u32)sg_dma_len(sgl); | 
 | 			hw_sgl++; | 
 | 		} | 
 | 	} | 
 | submit: | 
 | 	spin_lock_irqsave(&cb->queue_lock, flags); | 
 | 	cb->qcmd(cb, cmd_blk); | 
 | 	spin_unlock_irqrestore(&cb->queue_lock, flags); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int myrb_queuecommand(struct Scsi_Host *shost, | 
 | 		struct scsi_cmnd *scmd) | 
 | { | 
 | 	struct scsi_device *sdev = scmd->device; | 
 |  | 
 | 	if (sdev->channel > myrb_logical_channel(shost)) { | 
 | 		scmd->result = (DID_BAD_TARGET << 16); | 
 | 		scsi_done(scmd); | 
 | 		return 0; | 
 | 	} | 
 | 	if (sdev->channel == myrb_logical_channel(shost)) | 
 | 		return myrb_ldev_queuecommand(shost, scmd); | 
 |  | 
 | 	return myrb_pthru_queuecommand(shost, scmd); | 
 | } | 
 |  | 
 | static int myrb_ldev_slave_alloc(struct scsi_device *sdev) | 
 | { | 
 | 	struct myrb_hba *cb = shost_priv(sdev->host); | 
 | 	struct myrb_ldev_info *ldev_info; | 
 | 	unsigned short ldev_num = sdev->id; | 
 | 	enum raid_level level; | 
 |  | 
 | 	ldev_info = cb->ldev_info_buf + ldev_num; | 
 | 	if (!ldev_info) | 
 | 		return -ENXIO; | 
 |  | 
 | 	sdev->hostdata = kzalloc(sizeof(*ldev_info), GFP_KERNEL); | 
 | 	if (!sdev->hostdata) | 
 | 		return -ENOMEM; | 
 | 	dev_dbg(&sdev->sdev_gendev, | 
 | 		"slave alloc ldev %d state %x\n", | 
 | 		ldev_num, ldev_info->state); | 
 | 	memcpy(sdev->hostdata, ldev_info, | 
 | 	       sizeof(*ldev_info)); | 
 | 	switch (ldev_info->raid_level) { | 
 | 	case MYRB_RAID_LEVEL0: | 
 | 		level = RAID_LEVEL_LINEAR; | 
 | 		break; | 
 | 	case MYRB_RAID_LEVEL1: | 
 | 		level = RAID_LEVEL_1; | 
 | 		break; | 
 | 	case MYRB_RAID_LEVEL3: | 
 | 		level = RAID_LEVEL_3; | 
 | 		break; | 
 | 	case MYRB_RAID_LEVEL5: | 
 | 		level = RAID_LEVEL_5; | 
 | 		break; | 
 | 	case MYRB_RAID_LEVEL6: | 
 | 		level = RAID_LEVEL_6; | 
 | 		break; | 
 | 	case MYRB_RAID_JBOD: | 
 | 		level = RAID_LEVEL_JBOD; | 
 | 		break; | 
 | 	default: | 
 | 		level = RAID_LEVEL_UNKNOWN; | 
 | 		break; | 
 | 	} | 
 | 	raid_set_level(myrb_raid_template, &sdev->sdev_gendev, level); | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int myrb_pdev_slave_alloc(struct scsi_device *sdev) | 
 | { | 
 | 	struct myrb_hba *cb = shost_priv(sdev->host); | 
 | 	struct myrb_pdev_state *pdev_info; | 
 | 	unsigned short status; | 
 |  | 
 | 	if (sdev->id > MYRB_MAX_TARGETS) | 
 | 		return -ENXIO; | 
 |  | 
 | 	pdev_info = kzalloc(sizeof(*pdev_info), GFP_KERNEL); | 
 | 	if (!pdev_info) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	status = myrb_exec_type3D(cb, MYRB_CMD_GET_DEVICE_STATE, | 
 | 				  sdev, pdev_info); | 
 | 	if (status != MYRB_STATUS_SUCCESS) { | 
 | 		dev_dbg(&sdev->sdev_gendev, | 
 | 			"Failed to get device state, status %x\n", | 
 | 			status); | 
 | 		kfree(pdev_info); | 
 | 		return -ENXIO; | 
 | 	} | 
 | 	if (!pdev_info->present) { | 
 | 		dev_dbg(&sdev->sdev_gendev, | 
 | 			"device not present, skip\n"); | 
 | 		kfree(pdev_info); | 
 | 		return -ENXIO; | 
 | 	} | 
 | 	dev_dbg(&sdev->sdev_gendev, | 
 | 		"slave alloc pdev %d:%d state %x\n", | 
 | 		sdev->channel, sdev->id, pdev_info->state); | 
 | 	sdev->hostdata = pdev_info; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int myrb_slave_alloc(struct scsi_device *sdev) | 
 | { | 
 | 	if (sdev->channel > myrb_logical_channel(sdev->host)) | 
 | 		return -ENXIO; | 
 |  | 
 | 	if (sdev->lun > 0) | 
 | 		return -ENXIO; | 
 |  | 
 | 	if (sdev->channel == myrb_logical_channel(sdev->host)) | 
 | 		return myrb_ldev_slave_alloc(sdev); | 
 |  | 
 | 	return myrb_pdev_slave_alloc(sdev); | 
 | } | 
 |  | 
 | static int myrb_slave_configure(struct scsi_device *sdev) | 
 | { | 
 | 	struct myrb_ldev_info *ldev_info; | 
 |  | 
 | 	if (sdev->channel > myrb_logical_channel(sdev->host)) | 
 | 		return -ENXIO; | 
 |  | 
 | 	if (sdev->channel < myrb_logical_channel(sdev->host)) { | 
 | 		sdev->no_uld_attach = 1; | 
 | 		return 0; | 
 | 	} | 
 | 	if (sdev->lun != 0) | 
 | 		return -ENXIO; | 
 |  | 
 | 	ldev_info = sdev->hostdata; | 
 | 	if (!ldev_info) | 
 | 		return -ENXIO; | 
 | 	if (ldev_info->state != MYRB_DEVICE_ONLINE) | 
 | 		sdev_printk(KERN_INFO, sdev, | 
 | 			    "Logical drive is %s\n", | 
 | 			    myrb_devstate_name(ldev_info->state)); | 
 |  | 
 | 	sdev->tagged_supported = 1; | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void myrb_slave_destroy(struct scsi_device *sdev) | 
 | { | 
 | 	kfree(sdev->hostdata); | 
 | } | 
 |  | 
 | static int myrb_biosparam(struct scsi_device *sdev, struct block_device *bdev, | 
 | 		sector_t capacity, int geom[]) | 
 | { | 
 | 	struct myrb_hba *cb = shost_priv(sdev->host); | 
 |  | 
 | 	geom[0] = cb->ldev_geom_heads; | 
 | 	geom[1] = cb->ldev_geom_sectors; | 
 | 	geom[2] = sector_div(capacity, geom[0] * geom[1]); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static ssize_t raid_state_show(struct device *dev, | 
 | 		struct device_attribute *attr, char *buf) | 
 | { | 
 | 	struct scsi_device *sdev = to_scsi_device(dev); | 
 | 	struct myrb_hba *cb = shost_priv(sdev->host); | 
 | 	int ret; | 
 |  | 
 | 	if (!sdev->hostdata) | 
 | 		return snprintf(buf, 16, "Unknown\n"); | 
 |  | 
 | 	if (sdev->channel == myrb_logical_channel(sdev->host)) { | 
 | 		struct myrb_ldev_info *ldev_info = sdev->hostdata; | 
 | 		const char *name; | 
 |  | 
 | 		name = myrb_devstate_name(ldev_info->state); | 
 | 		if (name) | 
 | 			ret = snprintf(buf, 32, "%s\n", name); | 
 | 		else | 
 | 			ret = snprintf(buf, 32, "Invalid (%02X)\n", | 
 | 				       ldev_info->state); | 
 | 	} else { | 
 | 		struct myrb_pdev_state *pdev_info = sdev->hostdata; | 
 | 		unsigned short status; | 
 | 		const char *name; | 
 |  | 
 | 		status = myrb_exec_type3D(cb, MYRB_CMD_GET_DEVICE_STATE, | 
 | 					  sdev, pdev_info); | 
 | 		if (status != MYRB_STATUS_SUCCESS) | 
 | 			sdev_printk(KERN_INFO, sdev, | 
 | 				    "Failed to get device state, status %x\n", | 
 | 				    status); | 
 |  | 
 | 		if (!pdev_info->present) | 
 | 			name = "Removed"; | 
 | 		else | 
 | 			name = myrb_devstate_name(pdev_info->state); | 
 | 		if (name) | 
 | 			ret = snprintf(buf, 32, "%s\n", name); | 
 | 		else | 
 | 			ret = snprintf(buf, 32, "Invalid (%02X)\n", | 
 | 				       pdev_info->state); | 
 | 	} | 
 | 	return ret; | 
 | } | 
 |  | 
 | static ssize_t raid_state_store(struct device *dev, | 
 | 		struct device_attribute *attr, const char *buf, size_t count) | 
 | { | 
 | 	struct scsi_device *sdev = to_scsi_device(dev); | 
 | 	struct myrb_hba *cb = shost_priv(sdev->host); | 
 | 	struct myrb_pdev_state *pdev_info; | 
 | 	enum myrb_devstate new_state; | 
 | 	unsigned short status; | 
 |  | 
 | 	if (!strncmp(buf, "kill", 4) || | 
 | 	    !strncmp(buf, "offline", 7)) | 
 | 		new_state = MYRB_DEVICE_DEAD; | 
 | 	else if (!strncmp(buf, "online", 6)) | 
 | 		new_state = MYRB_DEVICE_ONLINE; | 
 | 	else if (!strncmp(buf, "standby", 7)) | 
 | 		new_state = MYRB_DEVICE_STANDBY; | 
 | 	else | 
 | 		return -EINVAL; | 
 |  | 
 | 	pdev_info = sdev->hostdata; | 
 | 	if (!pdev_info) { | 
 | 		sdev_printk(KERN_INFO, sdev, | 
 | 			    "Failed - no physical device information\n"); | 
 | 		return -ENXIO; | 
 | 	} | 
 | 	if (!pdev_info->present) { | 
 | 		sdev_printk(KERN_INFO, sdev, | 
 | 			    "Failed - device not present\n"); | 
 | 		return -ENXIO; | 
 | 	} | 
 |  | 
 | 	if (pdev_info->state == new_state) | 
 | 		return count; | 
 |  | 
 | 	status = myrb_set_pdev_state(cb, sdev, new_state); | 
 | 	switch (status) { | 
 | 	case MYRB_STATUS_SUCCESS: | 
 | 		break; | 
 | 	case MYRB_STATUS_START_DEVICE_FAILED: | 
 | 		sdev_printk(KERN_INFO, sdev, | 
 | 			     "Failed - Unable to Start Device\n"); | 
 | 		count = -EAGAIN; | 
 | 		break; | 
 | 	case MYRB_STATUS_NO_DEVICE: | 
 | 		sdev_printk(KERN_INFO, sdev, | 
 | 			    "Failed - No Device at Address\n"); | 
 | 		count = -ENODEV; | 
 | 		break; | 
 | 	case MYRB_STATUS_INVALID_CHANNEL_OR_TARGET: | 
 | 		sdev_printk(KERN_INFO, sdev, | 
 | 			 "Failed - Invalid Channel or Target or Modifier\n"); | 
 | 		count = -EINVAL; | 
 | 		break; | 
 | 	case MYRB_STATUS_CHANNEL_BUSY: | 
 | 		sdev_printk(KERN_INFO, sdev, | 
 | 			 "Failed - Channel Busy\n"); | 
 | 		count = -EBUSY; | 
 | 		break; | 
 | 	default: | 
 | 		sdev_printk(KERN_INFO, sdev, | 
 | 			 "Failed - Unexpected Status %04X\n", status); | 
 | 		count = -EIO; | 
 | 		break; | 
 | 	} | 
 | 	return count; | 
 | } | 
 | static DEVICE_ATTR_RW(raid_state); | 
 |  | 
 | static ssize_t raid_level_show(struct device *dev, | 
 | 		struct device_attribute *attr, char *buf) | 
 | { | 
 | 	struct scsi_device *sdev = to_scsi_device(dev); | 
 |  | 
 | 	if (sdev->channel == myrb_logical_channel(sdev->host)) { | 
 | 		struct myrb_ldev_info *ldev_info = sdev->hostdata; | 
 | 		const char *name; | 
 |  | 
 | 		if (!ldev_info) | 
 | 			return -ENXIO; | 
 |  | 
 | 		name = myrb_raidlevel_name(ldev_info->raid_level); | 
 | 		if (!name) | 
 | 			return snprintf(buf, 32, "Invalid (%02X)\n", | 
 | 					ldev_info->state); | 
 | 		return snprintf(buf, 32, "%s\n", name); | 
 | 	} | 
 | 	return snprintf(buf, 32, "Physical Drive\n"); | 
 | } | 
 | static DEVICE_ATTR_RO(raid_level); | 
 |  | 
 | static ssize_t rebuild_show(struct device *dev, | 
 | 		struct device_attribute *attr, char *buf) | 
 | { | 
 | 	struct scsi_device *sdev = to_scsi_device(dev); | 
 | 	struct myrb_hba *cb = shost_priv(sdev->host); | 
 | 	struct myrb_rbld_progress rbld_buf; | 
 | 	unsigned char status; | 
 |  | 
 | 	if (sdev->channel < myrb_logical_channel(sdev->host)) | 
 | 		return snprintf(buf, 32, "physical device - not rebuilding\n"); | 
 |  | 
 | 	status = myrb_get_rbld_progress(cb, &rbld_buf); | 
 |  | 
 | 	if (rbld_buf.ldev_num != sdev->id || | 
 | 	    status != MYRB_STATUS_SUCCESS) | 
 | 		return snprintf(buf, 32, "not rebuilding\n"); | 
 |  | 
 | 	return snprintf(buf, 32, "rebuilding block %u of %u\n", | 
 | 			rbld_buf.ldev_size - rbld_buf.blocks_left, | 
 | 			rbld_buf.ldev_size); | 
 | } | 
 |  | 
 | static ssize_t rebuild_store(struct device *dev, | 
 | 		struct device_attribute *attr, const char *buf, size_t count) | 
 | { | 
 | 	struct scsi_device *sdev = to_scsi_device(dev); | 
 | 	struct myrb_hba *cb = shost_priv(sdev->host); | 
 | 	struct myrb_cmdblk *cmd_blk; | 
 | 	union myrb_cmd_mbox *mbox; | 
 | 	unsigned short status; | 
 | 	int rc, start; | 
 | 	const char *msg; | 
 |  | 
 | 	rc = kstrtoint(buf, 0, &start); | 
 | 	if (rc) | 
 | 		return rc; | 
 |  | 
 | 	if (sdev->channel >= myrb_logical_channel(sdev->host)) | 
 | 		return -ENXIO; | 
 |  | 
 | 	status = myrb_get_rbld_progress(cb, NULL); | 
 | 	if (start) { | 
 | 		if (status == MYRB_STATUS_SUCCESS) { | 
 | 			sdev_printk(KERN_INFO, sdev, | 
 | 				    "Rebuild Not Initiated; already in progress\n"); | 
 | 			return -EALREADY; | 
 | 		} | 
 | 		mutex_lock(&cb->dcmd_mutex); | 
 | 		cmd_blk = &cb->dcmd_blk; | 
 | 		myrb_reset_cmd(cmd_blk); | 
 | 		mbox = &cmd_blk->mbox; | 
 | 		mbox->type3D.opcode = MYRB_CMD_REBUILD_ASYNC; | 
 | 		mbox->type3D.id = MYRB_DCMD_TAG; | 
 | 		mbox->type3D.channel = sdev->channel; | 
 | 		mbox->type3D.target = sdev->id; | 
 | 		status = myrb_exec_cmd(cb, cmd_blk); | 
 | 		mutex_unlock(&cb->dcmd_mutex); | 
 | 	} else { | 
 | 		struct pci_dev *pdev = cb->pdev; | 
 | 		unsigned char *rate; | 
 | 		dma_addr_t rate_addr; | 
 |  | 
 | 		if (status != MYRB_STATUS_SUCCESS) { | 
 | 			sdev_printk(KERN_INFO, sdev, | 
 | 				    "Rebuild Not Cancelled; not in progress\n"); | 
 | 			return 0; | 
 | 		} | 
 |  | 
 | 		rate = dma_alloc_coherent(&pdev->dev, sizeof(char), | 
 | 					  &rate_addr, GFP_KERNEL); | 
 | 		if (rate == NULL) { | 
 | 			sdev_printk(KERN_INFO, sdev, | 
 | 				    "Cancellation of Rebuild Failed - Out of Memory\n"); | 
 | 			return -ENOMEM; | 
 | 		} | 
 | 		mutex_lock(&cb->dcmd_mutex); | 
 | 		cmd_blk = &cb->dcmd_blk; | 
 | 		myrb_reset_cmd(cmd_blk); | 
 | 		mbox = &cmd_blk->mbox; | 
 | 		mbox->type3R.opcode = MYRB_CMD_REBUILD_CONTROL; | 
 | 		mbox->type3R.id = MYRB_DCMD_TAG; | 
 | 		mbox->type3R.rbld_rate = 0xFF; | 
 | 		mbox->type3R.addr = rate_addr; | 
 | 		status = myrb_exec_cmd(cb, cmd_blk); | 
 | 		dma_free_coherent(&pdev->dev, sizeof(char), rate, rate_addr); | 
 | 		mutex_unlock(&cb->dcmd_mutex); | 
 | 	} | 
 | 	if (status == MYRB_STATUS_SUCCESS) { | 
 | 		sdev_printk(KERN_INFO, sdev, "Rebuild %s\n", | 
 | 			    start ? "Initiated" : "Cancelled"); | 
 | 		return count; | 
 | 	} | 
 | 	if (!start) { | 
 | 		sdev_printk(KERN_INFO, sdev, | 
 | 			    "Rebuild Not Cancelled, status 0x%x\n", | 
 | 			    status); | 
 | 		return -EIO; | 
 | 	} | 
 |  | 
 | 	switch (status) { | 
 | 	case MYRB_STATUS_ATTEMPT_TO_RBLD_ONLINE_DRIVE: | 
 | 		msg = "Attempt to Rebuild Online or Unresponsive Drive"; | 
 | 		break; | 
 | 	case MYRB_STATUS_RBLD_NEW_DISK_FAILED: | 
 | 		msg = "New Disk Failed During Rebuild"; | 
 | 		break; | 
 | 	case MYRB_STATUS_INVALID_ADDRESS: | 
 | 		msg = "Invalid Device Address"; | 
 | 		break; | 
 | 	case MYRB_STATUS_RBLD_OR_CHECK_INPROGRESS: | 
 | 		msg = "Already in Progress"; | 
 | 		break; | 
 | 	default: | 
 | 		msg = NULL; | 
 | 		break; | 
 | 	} | 
 | 	if (msg) | 
 | 		sdev_printk(KERN_INFO, sdev, | 
 | 			    "Rebuild Failed - %s\n", msg); | 
 | 	else | 
 | 		sdev_printk(KERN_INFO, sdev, | 
 | 			    "Rebuild Failed, status 0x%x\n", status); | 
 |  | 
 | 	return -EIO; | 
 | } | 
 | static DEVICE_ATTR_RW(rebuild); | 
 |  | 
 | static ssize_t consistency_check_store(struct device *dev, | 
 | 		struct device_attribute *attr, const char *buf, size_t count) | 
 | { | 
 | 	struct scsi_device *sdev = to_scsi_device(dev); | 
 | 	struct myrb_hba *cb = shost_priv(sdev->host); | 
 | 	struct myrb_rbld_progress rbld_buf; | 
 | 	struct myrb_cmdblk *cmd_blk; | 
 | 	union myrb_cmd_mbox *mbox; | 
 | 	unsigned short ldev_num = 0xFFFF; | 
 | 	unsigned short status; | 
 | 	int rc, start; | 
 | 	const char *msg; | 
 |  | 
 | 	rc = kstrtoint(buf, 0, &start); | 
 | 	if (rc) | 
 | 		return rc; | 
 |  | 
 | 	if (sdev->channel < myrb_logical_channel(sdev->host)) | 
 | 		return -ENXIO; | 
 |  | 
 | 	status = myrb_get_rbld_progress(cb, &rbld_buf); | 
 | 	if (start) { | 
 | 		if (status == MYRB_STATUS_SUCCESS) { | 
 | 			sdev_printk(KERN_INFO, sdev, | 
 | 				    "Check Consistency Not Initiated; already in progress\n"); | 
 | 			return -EALREADY; | 
 | 		} | 
 | 		mutex_lock(&cb->dcmd_mutex); | 
 | 		cmd_blk = &cb->dcmd_blk; | 
 | 		myrb_reset_cmd(cmd_blk); | 
 | 		mbox = &cmd_blk->mbox; | 
 | 		mbox->type3C.opcode = MYRB_CMD_CHECK_CONSISTENCY_ASYNC; | 
 | 		mbox->type3C.id = MYRB_DCMD_TAG; | 
 | 		mbox->type3C.ldev_num = sdev->id; | 
 | 		mbox->type3C.auto_restore = true; | 
 |  | 
 | 		status = myrb_exec_cmd(cb, cmd_blk); | 
 | 		mutex_unlock(&cb->dcmd_mutex); | 
 | 	} else { | 
 | 		struct pci_dev *pdev = cb->pdev; | 
 | 		unsigned char *rate; | 
 | 		dma_addr_t rate_addr; | 
 |  | 
 | 		if (ldev_num != sdev->id) { | 
 | 			sdev_printk(KERN_INFO, sdev, | 
 | 				    "Check Consistency Not Cancelled; not in progress\n"); | 
 | 			return 0; | 
 | 		} | 
 | 		rate = dma_alloc_coherent(&pdev->dev, sizeof(char), | 
 | 					  &rate_addr, GFP_KERNEL); | 
 | 		if (rate == NULL) { | 
 | 			sdev_printk(KERN_INFO, sdev, | 
 | 				    "Cancellation of Check Consistency Failed - Out of Memory\n"); | 
 | 			return -ENOMEM; | 
 | 		} | 
 | 		mutex_lock(&cb->dcmd_mutex); | 
 | 		cmd_blk = &cb->dcmd_blk; | 
 | 		myrb_reset_cmd(cmd_blk); | 
 | 		mbox = &cmd_blk->mbox; | 
 | 		mbox->type3R.opcode = MYRB_CMD_REBUILD_CONTROL; | 
 | 		mbox->type3R.id = MYRB_DCMD_TAG; | 
 | 		mbox->type3R.rbld_rate = 0xFF; | 
 | 		mbox->type3R.addr = rate_addr; | 
 | 		status = myrb_exec_cmd(cb, cmd_blk); | 
 | 		dma_free_coherent(&pdev->dev, sizeof(char), rate, rate_addr); | 
 | 		mutex_unlock(&cb->dcmd_mutex); | 
 | 	} | 
 | 	if (status == MYRB_STATUS_SUCCESS) { | 
 | 		sdev_printk(KERN_INFO, sdev, "Check Consistency %s\n", | 
 | 			    start ? "Initiated" : "Cancelled"); | 
 | 		return count; | 
 | 	} | 
 | 	if (!start) { | 
 | 		sdev_printk(KERN_INFO, sdev, | 
 | 			    "Check Consistency Not Cancelled, status 0x%x\n", | 
 | 			    status); | 
 | 		return -EIO; | 
 | 	} | 
 |  | 
 | 	switch (status) { | 
 | 	case MYRB_STATUS_ATTEMPT_TO_RBLD_ONLINE_DRIVE: | 
 | 		msg = "Dependent Physical Device is DEAD"; | 
 | 		break; | 
 | 	case MYRB_STATUS_RBLD_NEW_DISK_FAILED: | 
 | 		msg = "New Disk Failed During Rebuild"; | 
 | 		break; | 
 | 	case MYRB_STATUS_INVALID_ADDRESS: | 
 | 		msg = "Invalid or Nonredundant Logical Drive"; | 
 | 		break; | 
 | 	case MYRB_STATUS_RBLD_OR_CHECK_INPROGRESS: | 
 | 		msg = "Already in Progress"; | 
 | 		break; | 
 | 	default: | 
 | 		msg = NULL; | 
 | 		break; | 
 | 	} | 
 | 	if (msg) | 
 | 		sdev_printk(KERN_INFO, sdev, | 
 | 			    "Check Consistency Failed - %s\n", msg); | 
 | 	else | 
 | 		sdev_printk(KERN_INFO, sdev, | 
 | 			    "Check Consistency Failed, status 0x%x\n", status); | 
 |  | 
 | 	return -EIO; | 
 | } | 
 |  | 
 | static ssize_t consistency_check_show(struct device *dev, | 
 | 		struct device_attribute *attr, char *buf) | 
 | { | 
 | 	return rebuild_show(dev, attr, buf); | 
 | } | 
 | static DEVICE_ATTR_RW(consistency_check); | 
 |  | 
 | static ssize_t ctlr_num_show(struct device *dev, | 
 | 		struct device_attribute *attr, char *buf) | 
 | { | 
 | 	struct Scsi_Host *shost = class_to_shost(dev); | 
 | 	struct myrb_hba *cb = shost_priv(shost); | 
 |  | 
 | 	return snprintf(buf, 20, "%u\n", cb->ctlr_num); | 
 | } | 
 | static DEVICE_ATTR_RO(ctlr_num); | 
 |  | 
 | static ssize_t firmware_show(struct device *dev, | 
 | 		struct device_attribute *attr, char *buf) | 
 | { | 
 | 	struct Scsi_Host *shost = class_to_shost(dev); | 
 | 	struct myrb_hba *cb = shost_priv(shost); | 
 |  | 
 | 	return snprintf(buf, 16, "%s\n", cb->fw_version); | 
 | } | 
 | static DEVICE_ATTR_RO(firmware); | 
 |  | 
 | static ssize_t model_show(struct device *dev, | 
 | 		struct device_attribute *attr, char *buf) | 
 | { | 
 | 	struct Scsi_Host *shost = class_to_shost(dev); | 
 | 	struct myrb_hba *cb = shost_priv(shost); | 
 |  | 
 | 	return snprintf(buf, 16, "%s\n", cb->model_name); | 
 | } | 
 | static DEVICE_ATTR_RO(model); | 
 |  | 
 | static ssize_t flush_cache_store(struct device *dev, | 
 | 		struct device_attribute *attr, const char *buf, size_t count) | 
 | { | 
 | 	struct Scsi_Host *shost = class_to_shost(dev); | 
 | 	struct myrb_hba *cb = shost_priv(shost); | 
 | 	unsigned short status; | 
 |  | 
 | 	status = myrb_exec_type3(cb, MYRB_CMD_FLUSH, 0); | 
 | 	if (status == MYRB_STATUS_SUCCESS) { | 
 | 		shost_printk(KERN_INFO, shost, | 
 | 			     "Cache Flush Completed\n"); | 
 | 		return count; | 
 | 	} | 
 | 	shost_printk(KERN_INFO, shost, | 
 | 		     "Cache Flush Failed, status %x\n", status); | 
 | 	return -EIO; | 
 | } | 
 | static DEVICE_ATTR_WO(flush_cache); | 
 |  | 
 | static struct attribute *myrb_sdev_attrs[] = { | 
 | 	&dev_attr_rebuild.attr, | 
 | 	&dev_attr_consistency_check.attr, | 
 | 	&dev_attr_raid_state.attr, | 
 | 	&dev_attr_raid_level.attr, | 
 | 	NULL, | 
 | }; | 
 |  | 
 | ATTRIBUTE_GROUPS(myrb_sdev); | 
 |  | 
 | static struct attribute *myrb_shost_attrs[] = { | 
 | 	&dev_attr_ctlr_num.attr, | 
 | 	&dev_attr_model.attr, | 
 | 	&dev_attr_firmware.attr, | 
 | 	&dev_attr_flush_cache.attr, | 
 | 	NULL, | 
 | }; | 
 |  | 
 | ATTRIBUTE_GROUPS(myrb_shost); | 
 |  | 
 | static struct scsi_host_template myrb_template = { | 
 | 	.module			= THIS_MODULE, | 
 | 	.name			= "DAC960", | 
 | 	.proc_name		= "myrb", | 
 | 	.queuecommand		= myrb_queuecommand, | 
 | 	.eh_host_reset_handler	= myrb_host_reset, | 
 | 	.slave_alloc		= myrb_slave_alloc, | 
 | 	.slave_configure	= myrb_slave_configure, | 
 | 	.slave_destroy		= myrb_slave_destroy, | 
 | 	.bios_param		= myrb_biosparam, | 
 | 	.cmd_size		= sizeof(struct myrb_cmdblk), | 
 | 	.shost_groups		= myrb_shost_groups, | 
 | 	.sdev_groups		= myrb_sdev_groups, | 
 | 	.this_id		= -1, | 
 | }; | 
 |  | 
 | /** | 
 |  * myrb_is_raid - return boolean indicating device is raid volume | 
 |  * @dev: the device struct object | 
 |  */ | 
 | static int myrb_is_raid(struct device *dev) | 
 | { | 
 | 	struct scsi_device *sdev = to_scsi_device(dev); | 
 |  | 
 | 	return sdev->channel == myrb_logical_channel(sdev->host); | 
 | } | 
 |  | 
 | /** | 
 |  * myrb_get_resync - get raid volume resync percent complete | 
 |  * @dev: the device struct object | 
 |  */ | 
 | static void myrb_get_resync(struct device *dev) | 
 | { | 
 | 	struct scsi_device *sdev = to_scsi_device(dev); | 
 | 	struct myrb_hba *cb = shost_priv(sdev->host); | 
 | 	struct myrb_rbld_progress rbld_buf; | 
 | 	unsigned int percent_complete = 0; | 
 | 	unsigned short status; | 
 | 	unsigned int ldev_size = 0, remaining = 0; | 
 |  | 
 | 	if (sdev->channel < myrb_logical_channel(sdev->host)) | 
 | 		return; | 
 | 	status = myrb_get_rbld_progress(cb, &rbld_buf); | 
 | 	if (status == MYRB_STATUS_SUCCESS) { | 
 | 		if (rbld_buf.ldev_num == sdev->id) { | 
 | 			ldev_size = rbld_buf.ldev_size; | 
 | 			remaining = rbld_buf.blocks_left; | 
 | 		} | 
 | 	} | 
 | 	if (remaining && ldev_size) | 
 | 		percent_complete = (ldev_size - remaining) * 100 / ldev_size; | 
 | 	raid_set_resync(myrb_raid_template, dev, percent_complete); | 
 | } | 
 |  | 
 | /** | 
 |  * myrb_get_state - get raid volume status | 
 |  * @dev: the device struct object | 
 |  */ | 
 | static void myrb_get_state(struct device *dev) | 
 | { | 
 | 	struct scsi_device *sdev = to_scsi_device(dev); | 
 | 	struct myrb_hba *cb = shost_priv(sdev->host); | 
 | 	struct myrb_ldev_info *ldev_info = sdev->hostdata; | 
 | 	enum raid_state state = RAID_STATE_UNKNOWN; | 
 | 	unsigned short status; | 
 |  | 
 | 	if (sdev->channel < myrb_logical_channel(sdev->host) || !ldev_info) | 
 | 		state = RAID_STATE_UNKNOWN; | 
 | 	else { | 
 | 		status = myrb_get_rbld_progress(cb, NULL); | 
 | 		if (status == MYRB_STATUS_SUCCESS) | 
 | 			state = RAID_STATE_RESYNCING; | 
 | 		else { | 
 | 			switch (ldev_info->state) { | 
 | 			case MYRB_DEVICE_ONLINE: | 
 | 				state = RAID_STATE_ACTIVE; | 
 | 				break; | 
 | 			case MYRB_DEVICE_WO: | 
 | 			case MYRB_DEVICE_CRITICAL: | 
 | 				state = RAID_STATE_DEGRADED; | 
 | 				break; | 
 | 			default: | 
 | 				state = RAID_STATE_OFFLINE; | 
 | 			} | 
 | 		} | 
 | 	} | 
 | 	raid_set_state(myrb_raid_template, dev, state); | 
 | } | 
 |  | 
 | static struct raid_function_template myrb_raid_functions = { | 
 | 	.cookie		= &myrb_template, | 
 | 	.is_raid	= myrb_is_raid, | 
 | 	.get_resync	= myrb_get_resync, | 
 | 	.get_state	= myrb_get_state, | 
 | }; | 
 |  | 
 | static void myrb_handle_scsi(struct myrb_hba *cb, struct myrb_cmdblk *cmd_blk, | 
 | 		struct scsi_cmnd *scmd) | 
 | { | 
 | 	unsigned short status; | 
 |  | 
 | 	if (!cmd_blk) | 
 | 		return; | 
 |  | 
 | 	scsi_dma_unmap(scmd); | 
 |  | 
 | 	if (cmd_blk->dcdb) { | 
 | 		memcpy(scmd->sense_buffer, &cmd_blk->dcdb->sense, 64); | 
 | 		dma_pool_free(cb->dcdb_pool, cmd_blk->dcdb, | 
 | 			      cmd_blk->dcdb_addr); | 
 | 		cmd_blk->dcdb = NULL; | 
 | 	} | 
 | 	if (cmd_blk->sgl) { | 
 | 		dma_pool_free(cb->sg_pool, cmd_blk->sgl, cmd_blk->sgl_addr); | 
 | 		cmd_blk->sgl = NULL; | 
 | 		cmd_blk->sgl_addr = 0; | 
 | 	} | 
 | 	status = cmd_blk->status; | 
 | 	switch (status) { | 
 | 	case MYRB_STATUS_SUCCESS: | 
 | 	case MYRB_STATUS_DEVICE_BUSY: | 
 | 		scmd->result = (DID_OK << 16) | status; | 
 | 		break; | 
 | 	case MYRB_STATUS_BAD_DATA: | 
 | 		dev_dbg(&scmd->device->sdev_gendev, | 
 | 			"Bad Data Encountered\n"); | 
 | 		if (scmd->sc_data_direction == DMA_FROM_DEVICE) | 
 | 			/* Unrecovered read error */ | 
 | 			scsi_build_sense(scmd, 0, MEDIUM_ERROR, 0x11, 0); | 
 | 		else | 
 | 			/* Write error */ | 
 | 			scsi_build_sense(scmd, 0, MEDIUM_ERROR, 0x0C, 0); | 
 | 		break; | 
 | 	case MYRB_STATUS_IRRECOVERABLE_DATA_ERROR: | 
 | 		scmd_printk(KERN_ERR, scmd, "Irrecoverable Data Error\n"); | 
 | 		if (scmd->sc_data_direction == DMA_FROM_DEVICE) | 
 | 			/* Unrecovered read error, auto-reallocation failed */ | 
 | 			scsi_build_sense(scmd, 0, MEDIUM_ERROR, 0x11, 0x04); | 
 | 		else | 
 | 			/* Write error, auto-reallocation failed */ | 
 | 			scsi_build_sense(scmd, 0, MEDIUM_ERROR, 0x0C, 0x02); | 
 | 		break; | 
 | 	case MYRB_STATUS_LDRV_NONEXISTENT_OR_OFFLINE: | 
 | 		dev_dbg(&scmd->device->sdev_gendev, | 
 | 			    "Logical Drive Nonexistent or Offline"); | 
 | 		scmd->result = (DID_BAD_TARGET << 16); | 
 | 		break; | 
 | 	case MYRB_STATUS_ACCESS_BEYOND_END_OF_LDRV: | 
 | 		dev_dbg(&scmd->device->sdev_gendev, | 
 | 			    "Attempt to Access Beyond End of Logical Drive"); | 
 | 		/* Logical block address out of range */ | 
 | 		scsi_build_sense(scmd, 0, NOT_READY, 0x21, 0); | 
 | 		break; | 
 | 	case MYRB_STATUS_DEVICE_NONRESPONSIVE: | 
 | 		dev_dbg(&scmd->device->sdev_gendev, "Device nonresponsive\n"); | 
 | 		scmd->result = (DID_BAD_TARGET << 16); | 
 | 		break; | 
 | 	default: | 
 | 		scmd_printk(KERN_ERR, scmd, | 
 | 			    "Unexpected Error Status %04X", status); | 
 | 		scmd->result = (DID_ERROR << 16); | 
 | 		break; | 
 | 	} | 
 | 	scsi_done(scmd); | 
 | } | 
 |  | 
 | static void myrb_handle_cmdblk(struct myrb_hba *cb, struct myrb_cmdblk *cmd_blk) | 
 | { | 
 | 	if (!cmd_blk) | 
 | 		return; | 
 |  | 
 | 	if (cmd_blk->completion) { | 
 | 		complete(cmd_blk->completion); | 
 | 		cmd_blk->completion = NULL; | 
 | 	} | 
 | } | 
 |  | 
 | static void myrb_monitor(struct work_struct *work) | 
 | { | 
 | 	struct myrb_hba *cb = container_of(work, | 
 | 			struct myrb_hba, monitor_work.work); | 
 | 	struct Scsi_Host *shost = cb->host; | 
 | 	unsigned long interval = MYRB_PRIMARY_MONITOR_INTERVAL; | 
 |  | 
 | 	dev_dbg(&shost->shost_gendev, "monitor tick\n"); | 
 |  | 
 | 	if (cb->new_ev_seq > cb->old_ev_seq) { | 
 | 		int event = cb->old_ev_seq; | 
 |  | 
 | 		dev_dbg(&shost->shost_gendev, | 
 | 			"get event log no %d/%d\n", | 
 | 			cb->new_ev_seq, event); | 
 | 		myrb_get_event(cb, event); | 
 | 		cb->old_ev_seq = event + 1; | 
 | 		interval = 10; | 
 | 	} else if (cb->need_err_info) { | 
 | 		cb->need_err_info = false; | 
 | 		dev_dbg(&shost->shost_gendev, "get error table\n"); | 
 | 		myrb_get_errtable(cb); | 
 | 		interval = 10; | 
 | 	} else if (cb->need_rbld && cb->rbld_first) { | 
 | 		cb->need_rbld = false; | 
 | 		dev_dbg(&shost->shost_gendev, | 
 | 			"get rebuild progress\n"); | 
 | 		myrb_update_rbld_progress(cb); | 
 | 		interval = 10; | 
 | 	} else if (cb->need_ldev_info) { | 
 | 		cb->need_ldev_info = false; | 
 | 		dev_dbg(&shost->shost_gendev, | 
 | 			"get logical drive info\n"); | 
 | 		myrb_get_ldev_info(cb); | 
 | 		interval = 10; | 
 | 	} else if (cb->need_rbld) { | 
 | 		cb->need_rbld = false; | 
 | 		dev_dbg(&shost->shost_gendev, | 
 | 			"get rebuild progress\n"); | 
 | 		myrb_update_rbld_progress(cb); | 
 | 		interval = 10; | 
 | 	} else if (cb->need_cc_status) { | 
 | 		cb->need_cc_status = false; | 
 | 		dev_dbg(&shost->shost_gendev, | 
 | 			"get consistency check progress\n"); | 
 | 		myrb_get_cc_progress(cb); | 
 | 		interval = 10; | 
 | 	} else if (cb->need_bgi_status) { | 
 | 		cb->need_bgi_status = false; | 
 | 		dev_dbg(&shost->shost_gendev, "get background init status\n"); | 
 | 		myrb_bgi_control(cb); | 
 | 		interval = 10; | 
 | 	} else { | 
 | 		dev_dbg(&shost->shost_gendev, "new enquiry\n"); | 
 | 		mutex_lock(&cb->dma_mutex); | 
 | 		myrb_hba_enquiry(cb); | 
 | 		mutex_unlock(&cb->dma_mutex); | 
 | 		if ((cb->new_ev_seq - cb->old_ev_seq > 0) || | 
 | 		    cb->need_err_info || cb->need_rbld || | 
 | 		    cb->need_ldev_info || cb->need_cc_status || | 
 | 		    cb->need_bgi_status) { | 
 | 			dev_dbg(&shost->shost_gendev, | 
 | 				"reschedule monitor\n"); | 
 | 			interval = 0; | 
 | 		} | 
 | 	} | 
 | 	if (interval > 1) | 
 | 		cb->primary_monitor_time = jiffies; | 
 | 	queue_delayed_work(cb->work_q, &cb->monitor_work, interval); | 
 | } | 
 |  | 
 | /* | 
 |  * myrb_err_status - reports controller BIOS messages | 
 |  * | 
 |  * Controller BIOS messages are passed through the Error Status Register | 
 |  * when the driver performs the BIOS handshaking. | 
 |  * | 
 |  * Return: true for fatal errors and false otherwise. | 
 |  */ | 
 | static bool myrb_err_status(struct myrb_hba *cb, unsigned char error, | 
 | 		unsigned char parm0, unsigned char parm1) | 
 | { | 
 | 	struct pci_dev *pdev = cb->pdev; | 
 |  | 
 | 	switch (error) { | 
 | 	case 0x00: | 
 | 		dev_info(&pdev->dev, | 
 | 			 "Physical Device %d:%d Not Responding\n", | 
 | 			 parm1, parm0); | 
 | 		break; | 
 | 	case 0x08: | 
 | 		dev_notice(&pdev->dev, "Spinning Up Drives\n"); | 
 | 		break; | 
 | 	case 0x30: | 
 | 		dev_notice(&pdev->dev, "Configuration Checksum Error\n"); | 
 | 		break; | 
 | 	case 0x60: | 
 | 		dev_notice(&pdev->dev, "Mirror Race Recovery Failed\n"); | 
 | 		break; | 
 | 	case 0x70: | 
 | 		dev_notice(&pdev->dev, "Mirror Race Recovery In Progress\n"); | 
 | 		break; | 
 | 	case 0x90: | 
 | 		dev_notice(&pdev->dev, "Physical Device %d:%d COD Mismatch\n", | 
 | 			   parm1, parm0); | 
 | 		break; | 
 | 	case 0xA0: | 
 | 		dev_notice(&pdev->dev, "Logical Drive Installation Aborted\n"); | 
 | 		break; | 
 | 	case 0xB0: | 
 | 		dev_notice(&pdev->dev, "Mirror Race On A Critical Logical Drive\n"); | 
 | 		break; | 
 | 	case 0xD0: | 
 | 		dev_notice(&pdev->dev, "New Controller Configuration Found\n"); | 
 | 		break; | 
 | 	case 0xF0: | 
 | 		dev_err(&pdev->dev, "Fatal Memory Parity Error\n"); | 
 | 		return true; | 
 | 	default: | 
 | 		dev_err(&pdev->dev, "Unknown Initialization Error %02X\n", | 
 | 			error); | 
 | 		return true; | 
 | 	} | 
 | 	return false; | 
 | } | 
 |  | 
 | /* | 
 |  * Hardware-specific functions | 
 |  */ | 
 |  | 
 | /* | 
 |  * DAC960 LA Series Controllers | 
 |  */ | 
 |  | 
 | static inline void DAC960_LA_hw_mbox_new_cmd(void __iomem *base) | 
 | { | 
 | 	writeb(DAC960_LA_IDB_HWMBOX_NEW_CMD, base + DAC960_LA_IDB_OFFSET); | 
 | } | 
 |  | 
 | static inline void DAC960_LA_ack_hw_mbox_status(void __iomem *base) | 
 | { | 
 | 	writeb(DAC960_LA_IDB_HWMBOX_ACK_STS, base + DAC960_LA_IDB_OFFSET); | 
 | } | 
 |  | 
 | static inline void DAC960_LA_reset_ctrl(void __iomem *base) | 
 | { | 
 | 	writeb(DAC960_LA_IDB_CTRL_RESET, base + DAC960_LA_IDB_OFFSET); | 
 | } | 
 |  | 
 | static inline void DAC960_LA_mem_mbox_new_cmd(void __iomem *base) | 
 | { | 
 | 	writeb(DAC960_LA_IDB_MMBOX_NEW_CMD, base + DAC960_LA_IDB_OFFSET); | 
 | } | 
 |  | 
 | static inline bool DAC960_LA_hw_mbox_is_full(void __iomem *base) | 
 | { | 
 | 	unsigned char idb = readb(base + DAC960_LA_IDB_OFFSET); | 
 |  | 
 | 	return !(idb & DAC960_LA_IDB_HWMBOX_EMPTY); | 
 | } | 
 |  | 
 | static inline bool DAC960_LA_init_in_progress(void __iomem *base) | 
 | { | 
 | 	unsigned char idb = readb(base + DAC960_LA_IDB_OFFSET); | 
 |  | 
 | 	return !(idb & DAC960_LA_IDB_INIT_DONE); | 
 | } | 
 |  | 
 | static inline void DAC960_LA_ack_hw_mbox_intr(void __iomem *base) | 
 | { | 
 | 	writeb(DAC960_LA_ODB_HWMBOX_ACK_IRQ, base + DAC960_LA_ODB_OFFSET); | 
 | } | 
 |  | 
 | static inline void DAC960_LA_ack_intr(void __iomem *base) | 
 | { | 
 | 	writeb(DAC960_LA_ODB_HWMBOX_ACK_IRQ | DAC960_LA_ODB_MMBOX_ACK_IRQ, | 
 | 	       base + DAC960_LA_ODB_OFFSET); | 
 | } | 
 |  | 
 | static inline bool DAC960_LA_hw_mbox_status_available(void __iomem *base) | 
 | { | 
 | 	unsigned char odb = readb(base + DAC960_LA_ODB_OFFSET); | 
 |  | 
 | 	return odb & DAC960_LA_ODB_HWMBOX_STS_AVAIL; | 
 | } | 
 |  | 
 | static inline void DAC960_LA_enable_intr(void __iomem *base) | 
 | { | 
 | 	unsigned char odb = 0xFF; | 
 |  | 
 | 	odb &= ~DAC960_LA_IRQMASK_DISABLE_IRQ; | 
 | 	writeb(odb, base + DAC960_LA_IRQMASK_OFFSET); | 
 | } | 
 |  | 
 | static inline void DAC960_LA_disable_intr(void __iomem *base) | 
 | { | 
 | 	unsigned char odb = 0xFF; | 
 |  | 
 | 	odb |= DAC960_LA_IRQMASK_DISABLE_IRQ; | 
 | 	writeb(odb, base + DAC960_LA_IRQMASK_OFFSET); | 
 | } | 
 |  | 
 | static inline void DAC960_LA_write_cmd_mbox(union myrb_cmd_mbox *mem_mbox, | 
 | 		union myrb_cmd_mbox *mbox) | 
 | { | 
 | 	mem_mbox->words[1] = mbox->words[1]; | 
 | 	mem_mbox->words[2] = mbox->words[2]; | 
 | 	mem_mbox->words[3] = mbox->words[3]; | 
 | 	/* Memory barrier to prevent reordering */ | 
 | 	wmb(); | 
 | 	mem_mbox->words[0] = mbox->words[0]; | 
 | 	/* Memory barrier to force PCI access */ | 
 | 	mb(); | 
 | } | 
 |  | 
 | static inline void DAC960_LA_write_hw_mbox(void __iomem *base, | 
 | 		union myrb_cmd_mbox *mbox) | 
 | { | 
 | 	writel(mbox->words[0], base + DAC960_LA_CMDOP_OFFSET); | 
 | 	writel(mbox->words[1], base + DAC960_LA_MBOX4_OFFSET); | 
 | 	writel(mbox->words[2], base + DAC960_LA_MBOX8_OFFSET); | 
 | 	writeb(mbox->bytes[12], base + DAC960_LA_MBOX12_OFFSET); | 
 | } | 
 |  | 
 | static inline unsigned short DAC960_LA_read_status(void __iomem *base) | 
 | { | 
 | 	return readw(base + DAC960_LA_STS_OFFSET); | 
 | } | 
 |  | 
 | static inline bool | 
 | DAC960_LA_read_error_status(void __iomem *base, unsigned char *error, | 
 | 		unsigned char *param0, unsigned char *param1) | 
 | { | 
 | 	unsigned char errsts = readb(base + DAC960_LA_ERRSTS_OFFSET); | 
 |  | 
 | 	if (!(errsts & DAC960_LA_ERRSTS_PENDING)) | 
 | 		return false; | 
 | 	errsts &= ~DAC960_LA_ERRSTS_PENDING; | 
 |  | 
 | 	*error = errsts; | 
 | 	*param0 = readb(base + DAC960_LA_CMDOP_OFFSET); | 
 | 	*param1 = readb(base + DAC960_LA_CMDID_OFFSET); | 
 | 	writeb(0xFF, base + DAC960_LA_ERRSTS_OFFSET); | 
 | 	return true; | 
 | } | 
 |  | 
 | static inline unsigned short | 
 | DAC960_LA_mbox_init(struct pci_dev *pdev, void __iomem *base, | 
 | 		union myrb_cmd_mbox *mbox) | 
 | { | 
 | 	unsigned short status; | 
 | 	int timeout = 0; | 
 |  | 
 | 	while (timeout < MYRB_MAILBOX_TIMEOUT) { | 
 | 		if (!DAC960_LA_hw_mbox_is_full(base)) | 
 | 			break; | 
 | 		udelay(10); | 
 | 		timeout++; | 
 | 	} | 
 | 	if (DAC960_LA_hw_mbox_is_full(base)) { | 
 | 		dev_err(&pdev->dev, | 
 | 			"Timeout waiting for empty mailbox\n"); | 
 | 		return MYRB_STATUS_SUBSYS_TIMEOUT; | 
 | 	} | 
 | 	DAC960_LA_write_hw_mbox(base, mbox); | 
 | 	DAC960_LA_hw_mbox_new_cmd(base); | 
 | 	timeout = 0; | 
 | 	while (timeout < MYRB_MAILBOX_TIMEOUT) { | 
 | 		if (DAC960_LA_hw_mbox_status_available(base)) | 
 | 			break; | 
 | 		udelay(10); | 
 | 		timeout++; | 
 | 	} | 
 | 	if (!DAC960_LA_hw_mbox_status_available(base)) { | 
 | 		dev_err(&pdev->dev, "Timeout waiting for mailbox status\n"); | 
 | 		return MYRB_STATUS_SUBSYS_TIMEOUT; | 
 | 	} | 
 | 	status = DAC960_LA_read_status(base); | 
 | 	DAC960_LA_ack_hw_mbox_intr(base); | 
 | 	DAC960_LA_ack_hw_mbox_status(base); | 
 |  | 
 | 	return status; | 
 | } | 
 |  | 
 | static int DAC960_LA_hw_init(struct pci_dev *pdev, | 
 | 		struct myrb_hba *cb, void __iomem *base) | 
 | { | 
 | 	int timeout = 0; | 
 | 	unsigned char error, parm0, parm1; | 
 |  | 
 | 	DAC960_LA_disable_intr(base); | 
 | 	DAC960_LA_ack_hw_mbox_status(base); | 
 | 	udelay(1000); | 
 | 	while (DAC960_LA_init_in_progress(base) && | 
 | 	       timeout < MYRB_MAILBOX_TIMEOUT) { | 
 | 		if (DAC960_LA_read_error_status(base, &error, | 
 | 					      &parm0, &parm1) && | 
 | 		    myrb_err_status(cb, error, parm0, parm1)) | 
 | 			return -ENODEV; | 
 | 		udelay(10); | 
 | 		timeout++; | 
 | 	} | 
 | 	if (timeout == MYRB_MAILBOX_TIMEOUT) { | 
 | 		dev_err(&pdev->dev, | 
 | 			"Timeout waiting for Controller Initialisation\n"); | 
 | 		return -ETIMEDOUT; | 
 | 	} | 
 | 	if (!myrb_enable_mmio(cb, DAC960_LA_mbox_init)) { | 
 | 		dev_err(&pdev->dev, | 
 | 			"Unable to Enable Memory Mailbox Interface\n"); | 
 | 		DAC960_LA_reset_ctrl(base); | 
 | 		return -ENODEV; | 
 | 	} | 
 | 	DAC960_LA_enable_intr(base); | 
 | 	cb->qcmd = myrb_qcmd; | 
 | 	cb->write_cmd_mbox = DAC960_LA_write_cmd_mbox; | 
 | 	if (cb->dual_mode_interface) | 
 | 		cb->get_cmd_mbox = DAC960_LA_mem_mbox_new_cmd; | 
 | 	else | 
 | 		cb->get_cmd_mbox = DAC960_LA_hw_mbox_new_cmd; | 
 | 	cb->disable_intr = DAC960_LA_disable_intr; | 
 | 	cb->reset = DAC960_LA_reset_ctrl; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static irqreturn_t DAC960_LA_intr_handler(int irq, void *arg) | 
 | { | 
 | 	struct myrb_hba *cb = arg; | 
 | 	void __iomem *base = cb->io_base; | 
 | 	struct myrb_stat_mbox *next_stat_mbox; | 
 | 	unsigned long flags; | 
 |  | 
 | 	spin_lock_irqsave(&cb->queue_lock, flags); | 
 | 	DAC960_LA_ack_intr(base); | 
 | 	next_stat_mbox = cb->next_stat_mbox; | 
 | 	while (next_stat_mbox->valid) { | 
 | 		unsigned char id = next_stat_mbox->id; | 
 | 		struct scsi_cmnd *scmd = NULL; | 
 | 		struct myrb_cmdblk *cmd_blk = NULL; | 
 |  | 
 | 		if (id == MYRB_DCMD_TAG) | 
 | 			cmd_blk = &cb->dcmd_blk; | 
 | 		else if (id == MYRB_MCMD_TAG) | 
 | 			cmd_blk = &cb->mcmd_blk; | 
 | 		else { | 
 | 			scmd = scsi_host_find_tag(cb->host, id - 3); | 
 | 			if (scmd) | 
 | 				cmd_blk = scsi_cmd_priv(scmd); | 
 | 		} | 
 | 		if (cmd_blk) | 
 | 			cmd_blk->status = next_stat_mbox->status; | 
 | 		else | 
 | 			dev_err(&cb->pdev->dev, | 
 | 				"Unhandled command completion %d\n", id); | 
 |  | 
 | 		memset(next_stat_mbox, 0, sizeof(struct myrb_stat_mbox)); | 
 | 		if (++next_stat_mbox > cb->last_stat_mbox) | 
 | 			next_stat_mbox = cb->first_stat_mbox; | 
 |  | 
 | 		if (cmd_blk) { | 
 | 			if (id < 3) | 
 | 				myrb_handle_cmdblk(cb, cmd_blk); | 
 | 			else | 
 | 				myrb_handle_scsi(cb, cmd_blk, scmd); | 
 | 		} | 
 | 	} | 
 | 	cb->next_stat_mbox = next_stat_mbox; | 
 | 	spin_unlock_irqrestore(&cb->queue_lock, flags); | 
 | 	return IRQ_HANDLED; | 
 | } | 
 |  | 
 | static struct myrb_privdata DAC960_LA_privdata = { | 
 | 	.hw_init =	DAC960_LA_hw_init, | 
 | 	.irq_handler =	DAC960_LA_intr_handler, | 
 | 	.mmio_size =	DAC960_LA_mmio_size, | 
 | }; | 
 |  | 
 | /* | 
 |  * DAC960 PG Series Controllers | 
 |  */ | 
 | static inline void DAC960_PG_hw_mbox_new_cmd(void __iomem *base) | 
 | { | 
 | 	writel(DAC960_PG_IDB_HWMBOX_NEW_CMD, base + DAC960_PG_IDB_OFFSET); | 
 | } | 
 |  | 
 | static inline void DAC960_PG_ack_hw_mbox_status(void __iomem *base) | 
 | { | 
 | 	writel(DAC960_PG_IDB_HWMBOX_ACK_STS, base + DAC960_PG_IDB_OFFSET); | 
 | } | 
 |  | 
 | static inline void DAC960_PG_reset_ctrl(void __iomem *base) | 
 | { | 
 | 	writel(DAC960_PG_IDB_CTRL_RESET, base + DAC960_PG_IDB_OFFSET); | 
 | } | 
 |  | 
 | static inline void DAC960_PG_mem_mbox_new_cmd(void __iomem *base) | 
 | { | 
 | 	writel(DAC960_PG_IDB_MMBOX_NEW_CMD, base + DAC960_PG_IDB_OFFSET); | 
 | } | 
 |  | 
 | static inline bool DAC960_PG_hw_mbox_is_full(void __iomem *base) | 
 | { | 
 | 	unsigned char idb = readl(base + DAC960_PG_IDB_OFFSET); | 
 |  | 
 | 	return idb & DAC960_PG_IDB_HWMBOX_FULL; | 
 | } | 
 |  | 
 | static inline bool DAC960_PG_init_in_progress(void __iomem *base) | 
 | { | 
 | 	unsigned char idb = readl(base + DAC960_PG_IDB_OFFSET); | 
 |  | 
 | 	return idb & DAC960_PG_IDB_INIT_IN_PROGRESS; | 
 | } | 
 |  | 
 | static inline void DAC960_PG_ack_hw_mbox_intr(void __iomem *base) | 
 | { | 
 | 	writel(DAC960_PG_ODB_HWMBOX_ACK_IRQ, base + DAC960_PG_ODB_OFFSET); | 
 | } | 
 |  | 
 | static inline void DAC960_PG_ack_intr(void __iomem *base) | 
 | { | 
 | 	writel(DAC960_PG_ODB_HWMBOX_ACK_IRQ | DAC960_PG_ODB_MMBOX_ACK_IRQ, | 
 | 	       base + DAC960_PG_ODB_OFFSET); | 
 | } | 
 |  | 
 | static inline bool DAC960_PG_hw_mbox_status_available(void __iomem *base) | 
 | { | 
 | 	unsigned char odb = readl(base + DAC960_PG_ODB_OFFSET); | 
 |  | 
 | 	return odb & DAC960_PG_ODB_HWMBOX_STS_AVAIL; | 
 | } | 
 |  | 
 | static inline void DAC960_PG_enable_intr(void __iomem *base) | 
 | { | 
 | 	unsigned int imask = (unsigned int)-1; | 
 |  | 
 | 	imask &= ~DAC960_PG_IRQMASK_DISABLE_IRQ; | 
 | 	writel(imask, base + DAC960_PG_IRQMASK_OFFSET); | 
 | } | 
 |  | 
 | static inline void DAC960_PG_disable_intr(void __iomem *base) | 
 | { | 
 | 	unsigned int imask = (unsigned int)-1; | 
 |  | 
 | 	writel(imask, base + DAC960_PG_IRQMASK_OFFSET); | 
 | } | 
 |  | 
 | static inline void DAC960_PG_write_cmd_mbox(union myrb_cmd_mbox *mem_mbox, | 
 | 		union myrb_cmd_mbox *mbox) | 
 | { | 
 | 	mem_mbox->words[1] = mbox->words[1]; | 
 | 	mem_mbox->words[2] = mbox->words[2]; | 
 | 	mem_mbox->words[3] = mbox->words[3]; | 
 | 	/* Memory barrier to prevent reordering */ | 
 | 	wmb(); | 
 | 	mem_mbox->words[0] = mbox->words[0]; | 
 | 	/* Memory barrier to force PCI access */ | 
 | 	mb(); | 
 | } | 
 |  | 
 | static inline void DAC960_PG_write_hw_mbox(void __iomem *base, | 
 | 		union myrb_cmd_mbox *mbox) | 
 | { | 
 | 	writel(mbox->words[0], base + DAC960_PG_CMDOP_OFFSET); | 
 | 	writel(mbox->words[1], base + DAC960_PG_MBOX4_OFFSET); | 
 | 	writel(mbox->words[2], base + DAC960_PG_MBOX8_OFFSET); | 
 | 	writeb(mbox->bytes[12], base + DAC960_PG_MBOX12_OFFSET); | 
 | } | 
 |  | 
 | static inline unsigned short | 
 | DAC960_PG_read_status(void __iomem *base) | 
 | { | 
 | 	return readw(base + DAC960_PG_STS_OFFSET); | 
 | } | 
 |  | 
 | static inline bool | 
 | DAC960_PG_read_error_status(void __iomem *base, unsigned char *error, | 
 | 		unsigned char *param0, unsigned char *param1) | 
 | { | 
 | 	unsigned char errsts = readb(base + DAC960_PG_ERRSTS_OFFSET); | 
 |  | 
 | 	if (!(errsts & DAC960_PG_ERRSTS_PENDING)) | 
 | 		return false; | 
 | 	errsts &= ~DAC960_PG_ERRSTS_PENDING; | 
 | 	*error = errsts; | 
 | 	*param0 = readb(base + DAC960_PG_CMDOP_OFFSET); | 
 | 	*param1 = readb(base + DAC960_PG_CMDID_OFFSET); | 
 | 	writeb(0, base + DAC960_PG_ERRSTS_OFFSET); | 
 | 	return true; | 
 | } | 
 |  | 
 | static inline unsigned short | 
 | DAC960_PG_mbox_init(struct pci_dev *pdev, void __iomem *base, | 
 | 		union myrb_cmd_mbox *mbox) | 
 | { | 
 | 	unsigned short status; | 
 | 	int timeout = 0; | 
 |  | 
 | 	while (timeout < MYRB_MAILBOX_TIMEOUT) { | 
 | 		if (!DAC960_PG_hw_mbox_is_full(base)) | 
 | 			break; | 
 | 		udelay(10); | 
 | 		timeout++; | 
 | 	} | 
 | 	if (DAC960_PG_hw_mbox_is_full(base)) { | 
 | 		dev_err(&pdev->dev, | 
 | 			"Timeout waiting for empty mailbox\n"); | 
 | 		return MYRB_STATUS_SUBSYS_TIMEOUT; | 
 | 	} | 
 | 	DAC960_PG_write_hw_mbox(base, mbox); | 
 | 	DAC960_PG_hw_mbox_new_cmd(base); | 
 |  | 
 | 	timeout = 0; | 
 | 	while (timeout < MYRB_MAILBOX_TIMEOUT) { | 
 | 		if (DAC960_PG_hw_mbox_status_available(base)) | 
 | 			break; | 
 | 		udelay(10); | 
 | 		timeout++; | 
 | 	} | 
 | 	if (!DAC960_PG_hw_mbox_status_available(base)) { | 
 | 		dev_err(&pdev->dev, | 
 | 			"Timeout waiting for mailbox status\n"); | 
 | 		return MYRB_STATUS_SUBSYS_TIMEOUT; | 
 | 	} | 
 | 	status = DAC960_PG_read_status(base); | 
 | 	DAC960_PG_ack_hw_mbox_intr(base); | 
 | 	DAC960_PG_ack_hw_mbox_status(base); | 
 |  | 
 | 	return status; | 
 | } | 
 |  | 
 | static int DAC960_PG_hw_init(struct pci_dev *pdev, | 
 | 		struct myrb_hba *cb, void __iomem *base) | 
 | { | 
 | 	int timeout = 0; | 
 | 	unsigned char error, parm0, parm1; | 
 |  | 
 | 	DAC960_PG_disable_intr(base); | 
 | 	DAC960_PG_ack_hw_mbox_status(base); | 
 | 	udelay(1000); | 
 | 	while (DAC960_PG_init_in_progress(base) && | 
 | 	       timeout < MYRB_MAILBOX_TIMEOUT) { | 
 | 		if (DAC960_PG_read_error_status(base, &error, | 
 | 						&parm0, &parm1) && | 
 | 		    myrb_err_status(cb, error, parm0, parm1)) | 
 | 			return -EIO; | 
 | 		udelay(10); | 
 | 		timeout++; | 
 | 	} | 
 | 	if (timeout == MYRB_MAILBOX_TIMEOUT) { | 
 | 		dev_err(&pdev->dev, | 
 | 			"Timeout waiting for Controller Initialisation\n"); | 
 | 		return -ETIMEDOUT; | 
 | 	} | 
 | 	if (!myrb_enable_mmio(cb, DAC960_PG_mbox_init)) { | 
 | 		dev_err(&pdev->dev, | 
 | 			"Unable to Enable Memory Mailbox Interface\n"); | 
 | 		DAC960_PG_reset_ctrl(base); | 
 | 		return -ENODEV; | 
 | 	} | 
 | 	DAC960_PG_enable_intr(base); | 
 | 	cb->qcmd = myrb_qcmd; | 
 | 	cb->write_cmd_mbox = DAC960_PG_write_cmd_mbox; | 
 | 	if (cb->dual_mode_interface) | 
 | 		cb->get_cmd_mbox = DAC960_PG_mem_mbox_new_cmd; | 
 | 	else | 
 | 		cb->get_cmd_mbox = DAC960_PG_hw_mbox_new_cmd; | 
 | 	cb->disable_intr = DAC960_PG_disable_intr; | 
 | 	cb->reset = DAC960_PG_reset_ctrl; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static irqreturn_t DAC960_PG_intr_handler(int irq, void *arg) | 
 | { | 
 | 	struct myrb_hba *cb = arg; | 
 | 	void __iomem *base = cb->io_base; | 
 | 	struct myrb_stat_mbox *next_stat_mbox; | 
 | 	unsigned long flags; | 
 |  | 
 | 	spin_lock_irqsave(&cb->queue_lock, flags); | 
 | 	DAC960_PG_ack_intr(base); | 
 | 	next_stat_mbox = cb->next_stat_mbox; | 
 | 	while (next_stat_mbox->valid) { | 
 | 		unsigned char id = next_stat_mbox->id; | 
 | 		struct scsi_cmnd *scmd = NULL; | 
 | 		struct myrb_cmdblk *cmd_blk = NULL; | 
 |  | 
 | 		if (id == MYRB_DCMD_TAG) | 
 | 			cmd_blk = &cb->dcmd_blk; | 
 | 		else if (id == MYRB_MCMD_TAG) | 
 | 			cmd_blk = &cb->mcmd_blk; | 
 | 		else { | 
 | 			scmd = scsi_host_find_tag(cb->host, id - 3); | 
 | 			if (scmd) | 
 | 				cmd_blk = scsi_cmd_priv(scmd); | 
 | 		} | 
 | 		if (cmd_blk) | 
 | 			cmd_blk->status = next_stat_mbox->status; | 
 | 		else | 
 | 			dev_err(&cb->pdev->dev, | 
 | 				"Unhandled command completion %d\n", id); | 
 |  | 
 | 		memset(next_stat_mbox, 0, sizeof(struct myrb_stat_mbox)); | 
 | 		if (++next_stat_mbox > cb->last_stat_mbox) | 
 | 			next_stat_mbox = cb->first_stat_mbox; | 
 |  | 
 | 		if (id < 3) | 
 | 			myrb_handle_cmdblk(cb, cmd_blk); | 
 | 		else | 
 | 			myrb_handle_scsi(cb, cmd_blk, scmd); | 
 | 	} | 
 | 	cb->next_stat_mbox = next_stat_mbox; | 
 | 	spin_unlock_irqrestore(&cb->queue_lock, flags); | 
 | 	return IRQ_HANDLED; | 
 | } | 
 |  | 
 | static struct myrb_privdata DAC960_PG_privdata = { | 
 | 	.hw_init =	DAC960_PG_hw_init, | 
 | 	.irq_handler =	DAC960_PG_intr_handler, | 
 | 	.mmio_size =	DAC960_PG_mmio_size, | 
 | }; | 
 |  | 
 |  | 
 | /* | 
 |  * DAC960 PD Series Controllers | 
 |  */ | 
 |  | 
 | static inline void DAC960_PD_hw_mbox_new_cmd(void __iomem *base) | 
 | { | 
 | 	writeb(DAC960_PD_IDB_HWMBOX_NEW_CMD, base + DAC960_PD_IDB_OFFSET); | 
 | } | 
 |  | 
 | static inline void DAC960_PD_ack_hw_mbox_status(void __iomem *base) | 
 | { | 
 | 	writeb(DAC960_PD_IDB_HWMBOX_ACK_STS, base + DAC960_PD_IDB_OFFSET); | 
 | } | 
 |  | 
 | static inline void DAC960_PD_reset_ctrl(void __iomem *base) | 
 | { | 
 | 	writeb(DAC960_PD_IDB_CTRL_RESET, base + DAC960_PD_IDB_OFFSET); | 
 | } | 
 |  | 
 | static inline bool DAC960_PD_hw_mbox_is_full(void __iomem *base) | 
 | { | 
 | 	unsigned char idb = readb(base + DAC960_PD_IDB_OFFSET); | 
 |  | 
 | 	return idb & DAC960_PD_IDB_HWMBOX_FULL; | 
 | } | 
 |  | 
 | static inline bool DAC960_PD_init_in_progress(void __iomem *base) | 
 | { | 
 | 	unsigned char idb = readb(base + DAC960_PD_IDB_OFFSET); | 
 |  | 
 | 	return idb & DAC960_PD_IDB_INIT_IN_PROGRESS; | 
 | } | 
 |  | 
 | static inline void DAC960_PD_ack_intr(void __iomem *base) | 
 | { | 
 | 	writeb(DAC960_PD_ODB_HWMBOX_ACK_IRQ, base + DAC960_PD_ODB_OFFSET); | 
 | } | 
 |  | 
 | static inline bool DAC960_PD_hw_mbox_status_available(void __iomem *base) | 
 | { | 
 | 	unsigned char odb = readb(base + DAC960_PD_ODB_OFFSET); | 
 |  | 
 | 	return odb & DAC960_PD_ODB_HWMBOX_STS_AVAIL; | 
 | } | 
 |  | 
 | static inline void DAC960_PD_enable_intr(void __iomem *base) | 
 | { | 
 | 	writeb(DAC960_PD_IRQMASK_ENABLE_IRQ, base + DAC960_PD_IRQEN_OFFSET); | 
 | } | 
 |  | 
 | static inline void DAC960_PD_disable_intr(void __iomem *base) | 
 | { | 
 | 	writeb(0, base + DAC960_PD_IRQEN_OFFSET); | 
 | } | 
 |  | 
 | static inline void DAC960_PD_write_cmd_mbox(void __iomem *base, | 
 | 		union myrb_cmd_mbox *mbox) | 
 | { | 
 | 	writel(mbox->words[0], base + DAC960_PD_CMDOP_OFFSET); | 
 | 	writel(mbox->words[1], base + DAC960_PD_MBOX4_OFFSET); | 
 | 	writel(mbox->words[2], base + DAC960_PD_MBOX8_OFFSET); | 
 | 	writeb(mbox->bytes[12], base + DAC960_PD_MBOX12_OFFSET); | 
 | } | 
 |  | 
 | static inline unsigned char | 
 | DAC960_PD_read_status_cmd_ident(void __iomem *base) | 
 | { | 
 | 	return readb(base + DAC960_PD_STSID_OFFSET); | 
 | } | 
 |  | 
 | static inline unsigned short | 
 | DAC960_PD_read_status(void __iomem *base) | 
 | { | 
 | 	return readw(base + DAC960_PD_STS_OFFSET); | 
 | } | 
 |  | 
 | static inline bool | 
 | DAC960_PD_read_error_status(void __iomem *base, unsigned char *error, | 
 | 		unsigned char *param0, unsigned char *param1) | 
 | { | 
 | 	unsigned char errsts = readb(base + DAC960_PD_ERRSTS_OFFSET); | 
 |  | 
 | 	if (!(errsts & DAC960_PD_ERRSTS_PENDING)) | 
 | 		return false; | 
 | 	errsts &= ~DAC960_PD_ERRSTS_PENDING; | 
 | 	*error = errsts; | 
 | 	*param0 = readb(base + DAC960_PD_CMDOP_OFFSET); | 
 | 	*param1 = readb(base + DAC960_PD_CMDID_OFFSET); | 
 | 	writeb(0, base + DAC960_PD_ERRSTS_OFFSET); | 
 | 	return true; | 
 | } | 
 |  | 
 | static void DAC960_PD_qcmd(struct myrb_hba *cb, struct myrb_cmdblk *cmd_blk) | 
 | { | 
 | 	void __iomem *base = cb->io_base; | 
 | 	union myrb_cmd_mbox *mbox = &cmd_blk->mbox; | 
 |  | 
 | 	while (DAC960_PD_hw_mbox_is_full(base)) | 
 | 		udelay(1); | 
 | 	DAC960_PD_write_cmd_mbox(base, mbox); | 
 | 	DAC960_PD_hw_mbox_new_cmd(base); | 
 | } | 
 |  | 
 | static int DAC960_PD_hw_init(struct pci_dev *pdev, | 
 | 		struct myrb_hba *cb, void __iomem *base) | 
 | { | 
 | 	int timeout = 0; | 
 | 	unsigned char error, parm0, parm1; | 
 |  | 
 | 	if (!request_region(cb->io_addr, 0x80, "myrb")) { | 
 | 		dev_err(&pdev->dev, "IO port 0x%lx busy\n", | 
 | 			(unsigned long)cb->io_addr); | 
 | 		return -EBUSY; | 
 | 	} | 
 | 	DAC960_PD_disable_intr(base); | 
 | 	DAC960_PD_ack_hw_mbox_status(base); | 
 | 	udelay(1000); | 
 | 	while (DAC960_PD_init_in_progress(base) && | 
 | 	       timeout < MYRB_MAILBOX_TIMEOUT) { | 
 | 		if (DAC960_PD_read_error_status(base, &error, | 
 | 					      &parm0, &parm1) && | 
 | 		    myrb_err_status(cb, error, parm0, parm1)) | 
 | 			return -EIO; | 
 | 		udelay(10); | 
 | 		timeout++; | 
 | 	} | 
 | 	if (timeout == MYRB_MAILBOX_TIMEOUT) { | 
 | 		dev_err(&pdev->dev, | 
 | 			"Timeout waiting for Controller Initialisation\n"); | 
 | 		return -ETIMEDOUT; | 
 | 	} | 
 | 	if (!myrb_enable_mmio(cb, NULL)) { | 
 | 		dev_err(&pdev->dev, | 
 | 			"Unable to Enable Memory Mailbox Interface\n"); | 
 | 		DAC960_PD_reset_ctrl(base); | 
 | 		return -ENODEV; | 
 | 	} | 
 | 	DAC960_PD_enable_intr(base); | 
 | 	cb->qcmd = DAC960_PD_qcmd; | 
 | 	cb->disable_intr = DAC960_PD_disable_intr; | 
 | 	cb->reset = DAC960_PD_reset_ctrl; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static irqreturn_t DAC960_PD_intr_handler(int irq, void *arg) | 
 | { | 
 | 	struct myrb_hba *cb = arg; | 
 | 	void __iomem *base = cb->io_base; | 
 | 	unsigned long flags; | 
 |  | 
 | 	spin_lock_irqsave(&cb->queue_lock, flags); | 
 | 	while (DAC960_PD_hw_mbox_status_available(base)) { | 
 | 		unsigned char id = DAC960_PD_read_status_cmd_ident(base); | 
 | 		struct scsi_cmnd *scmd = NULL; | 
 | 		struct myrb_cmdblk *cmd_blk = NULL; | 
 |  | 
 | 		if (id == MYRB_DCMD_TAG) | 
 | 			cmd_blk = &cb->dcmd_blk; | 
 | 		else if (id == MYRB_MCMD_TAG) | 
 | 			cmd_blk = &cb->mcmd_blk; | 
 | 		else { | 
 | 			scmd = scsi_host_find_tag(cb->host, id - 3); | 
 | 			if (scmd) | 
 | 				cmd_blk = scsi_cmd_priv(scmd); | 
 | 		} | 
 | 		if (cmd_blk) | 
 | 			cmd_blk->status = DAC960_PD_read_status(base); | 
 | 		else | 
 | 			dev_err(&cb->pdev->dev, | 
 | 				"Unhandled command completion %d\n", id); | 
 |  | 
 | 		DAC960_PD_ack_intr(base); | 
 | 		DAC960_PD_ack_hw_mbox_status(base); | 
 |  | 
 | 		if (id < 3) | 
 | 			myrb_handle_cmdblk(cb, cmd_blk); | 
 | 		else | 
 | 			myrb_handle_scsi(cb, cmd_blk, scmd); | 
 | 	} | 
 | 	spin_unlock_irqrestore(&cb->queue_lock, flags); | 
 | 	return IRQ_HANDLED; | 
 | } | 
 |  | 
 | static struct myrb_privdata DAC960_PD_privdata = { | 
 | 	.hw_init =	DAC960_PD_hw_init, | 
 | 	.irq_handler =	DAC960_PD_intr_handler, | 
 | 	.mmio_size =	DAC960_PD_mmio_size, | 
 | }; | 
 |  | 
 |  | 
 | /* | 
 |  * DAC960 P Series Controllers | 
 |  * | 
 |  * Similar to the DAC960 PD Series Controllers, but some commands have | 
 |  * to be translated. | 
 |  */ | 
 |  | 
 | static inline void myrb_translate_enquiry(void *enq) | 
 | { | 
 | 	memcpy(enq + 132, enq + 36, 64); | 
 | 	memset(enq + 36, 0, 96); | 
 | } | 
 |  | 
 | static inline void myrb_translate_devstate(void *state) | 
 | { | 
 | 	memcpy(state + 2, state + 3, 1); | 
 | 	memmove(state + 4, state + 5, 2); | 
 | 	memmove(state + 6, state + 8, 4); | 
 | } | 
 |  | 
 | static inline void myrb_translate_to_rw_command(struct myrb_cmdblk *cmd_blk) | 
 | { | 
 | 	union myrb_cmd_mbox *mbox = &cmd_blk->mbox; | 
 | 	int ldev_num = mbox->type5.ld.ldev_num; | 
 |  | 
 | 	mbox->bytes[3] &= 0x7; | 
 | 	mbox->bytes[3] |= mbox->bytes[7] << 6; | 
 | 	mbox->bytes[7] = ldev_num; | 
 | } | 
 |  | 
 | static inline void myrb_translate_from_rw_command(struct myrb_cmdblk *cmd_blk) | 
 | { | 
 | 	union myrb_cmd_mbox *mbox = &cmd_blk->mbox; | 
 | 	int ldev_num = mbox->bytes[7]; | 
 |  | 
 | 	mbox->bytes[7] = mbox->bytes[3] >> 6; | 
 | 	mbox->bytes[3] &= 0x7; | 
 | 	mbox->bytes[3] |= ldev_num << 3; | 
 | } | 
 |  | 
 | static void DAC960_P_qcmd(struct myrb_hba *cb, struct myrb_cmdblk *cmd_blk) | 
 | { | 
 | 	void __iomem *base = cb->io_base; | 
 | 	union myrb_cmd_mbox *mbox = &cmd_blk->mbox; | 
 |  | 
 | 	switch (mbox->common.opcode) { | 
 | 	case MYRB_CMD_ENQUIRY: | 
 | 		mbox->common.opcode = MYRB_CMD_ENQUIRY_OLD; | 
 | 		break; | 
 | 	case MYRB_CMD_GET_DEVICE_STATE: | 
 | 		mbox->common.opcode = MYRB_CMD_GET_DEVICE_STATE_OLD; | 
 | 		break; | 
 | 	case MYRB_CMD_READ: | 
 | 		mbox->common.opcode = MYRB_CMD_READ_OLD; | 
 | 		myrb_translate_to_rw_command(cmd_blk); | 
 | 		break; | 
 | 	case MYRB_CMD_WRITE: | 
 | 		mbox->common.opcode = MYRB_CMD_WRITE_OLD; | 
 | 		myrb_translate_to_rw_command(cmd_blk); | 
 | 		break; | 
 | 	case MYRB_CMD_READ_SG: | 
 | 		mbox->common.opcode = MYRB_CMD_READ_SG_OLD; | 
 | 		myrb_translate_to_rw_command(cmd_blk); | 
 | 		break; | 
 | 	case MYRB_CMD_WRITE_SG: | 
 | 		mbox->common.opcode = MYRB_CMD_WRITE_SG_OLD; | 
 | 		myrb_translate_to_rw_command(cmd_blk); | 
 | 		break; | 
 | 	default: | 
 | 		break; | 
 | 	} | 
 | 	while (DAC960_PD_hw_mbox_is_full(base)) | 
 | 		udelay(1); | 
 | 	DAC960_PD_write_cmd_mbox(base, mbox); | 
 | 	DAC960_PD_hw_mbox_new_cmd(base); | 
 | } | 
 |  | 
 |  | 
 | static int DAC960_P_hw_init(struct pci_dev *pdev, | 
 | 		struct myrb_hba *cb, void __iomem *base) | 
 | { | 
 | 	int timeout = 0; | 
 | 	unsigned char error, parm0, parm1; | 
 |  | 
 | 	if (!request_region(cb->io_addr, 0x80, "myrb")) { | 
 | 		dev_err(&pdev->dev, "IO port 0x%lx busy\n", | 
 | 			(unsigned long)cb->io_addr); | 
 | 		return -EBUSY; | 
 | 	} | 
 | 	DAC960_PD_disable_intr(base); | 
 | 	DAC960_PD_ack_hw_mbox_status(base); | 
 | 	udelay(1000); | 
 | 	while (DAC960_PD_init_in_progress(base) && | 
 | 	       timeout < MYRB_MAILBOX_TIMEOUT) { | 
 | 		if (DAC960_PD_read_error_status(base, &error, | 
 | 						&parm0, &parm1) && | 
 | 		    myrb_err_status(cb, error, parm0, parm1)) | 
 | 			return -EAGAIN; | 
 | 		udelay(10); | 
 | 		timeout++; | 
 | 	} | 
 | 	if (timeout == MYRB_MAILBOX_TIMEOUT) { | 
 | 		dev_err(&pdev->dev, | 
 | 			"Timeout waiting for Controller Initialisation\n"); | 
 | 		return -ETIMEDOUT; | 
 | 	} | 
 | 	if (!myrb_enable_mmio(cb, NULL)) { | 
 | 		dev_err(&pdev->dev, | 
 | 			"Unable to allocate DMA mapped memory\n"); | 
 | 		DAC960_PD_reset_ctrl(base); | 
 | 		return -ETIMEDOUT; | 
 | 	} | 
 | 	DAC960_PD_enable_intr(base); | 
 | 	cb->qcmd = DAC960_P_qcmd; | 
 | 	cb->disable_intr = DAC960_PD_disable_intr; | 
 | 	cb->reset = DAC960_PD_reset_ctrl; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static irqreturn_t DAC960_P_intr_handler(int irq, void *arg) | 
 | { | 
 | 	struct myrb_hba *cb = arg; | 
 | 	void __iomem *base = cb->io_base; | 
 | 	unsigned long flags; | 
 |  | 
 | 	spin_lock_irqsave(&cb->queue_lock, flags); | 
 | 	while (DAC960_PD_hw_mbox_status_available(base)) { | 
 | 		unsigned char id = DAC960_PD_read_status_cmd_ident(base); | 
 | 		struct scsi_cmnd *scmd = NULL; | 
 | 		struct myrb_cmdblk *cmd_blk = NULL; | 
 | 		union myrb_cmd_mbox *mbox; | 
 | 		enum myrb_cmd_opcode op; | 
 |  | 
 |  | 
 | 		if (id == MYRB_DCMD_TAG) | 
 | 			cmd_blk = &cb->dcmd_blk; | 
 | 		else if (id == MYRB_MCMD_TAG) | 
 | 			cmd_blk = &cb->mcmd_blk; | 
 | 		else { | 
 | 			scmd = scsi_host_find_tag(cb->host, id - 3); | 
 | 			if (scmd) | 
 | 				cmd_blk = scsi_cmd_priv(scmd); | 
 | 		} | 
 | 		if (cmd_blk) | 
 | 			cmd_blk->status = DAC960_PD_read_status(base); | 
 | 		else | 
 | 			dev_err(&cb->pdev->dev, | 
 | 				"Unhandled command completion %d\n", id); | 
 |  | 
 | 		DAC960_PD_ack_intr(base); | 
 | 		DAC960_PD_ack_hw_mbox_status(base); | 
 |  | 
 | 		if (!cmd_blk) | 
 | 			continue; | 
 |  | 
 | 		mbox = &cmd_blk->mbox; | 
 | 		op = mbox->common.opcode; | 
 | 		switch (op) { | 
 | 		case MYRB_CMD_ENQUIRY_OLD: | 
 | 			mbox->common.opcode = MYRB_CMD_ENQUIRY; | 
 | 			myrb_translate_enquiry(cb->enquiry); | 
 | 			break; | 
 | 		case MYRB_CMD_READ_OLD: | 
 | 			mbox->common.opcode = MYRB_CMD_READ; | 
 | 			myrb_translate_from_rw_command(cmd_blk); | 
 | 			break; | 
 | 		case MYRB_CMD_WRITE_OLD: | 
 | 			mbox->common.opcode = MYRB_CMD_WRITE; | 
 | 			myrb_translate_from_rw_command(cmd_blk); | 
 | 			break; | 
 | 		case MYRB_CMD_READ_SG_OLD: | 
 | 			mbox->common.opcode = MYRB_CMD_READ_SG; | 
 | 			myrb_translate_from_rw_command(cmd_blk); | 
 | 			break; | 
 | 		case MYRB_CMD_WRITE_SG_OLD: | 
 | 			mbox->common.opcode = MYRB_CMD_WRITE_SG; | 
 | 			myrb_translate_from_rw_command(cmd_blk); | 
 | 			break; | 
 | 		default: | 
 | 			break; | 
 | 		} | 
 | 		if (id < 3) | 
 | 			myrb_handle_cmdblk(cb, cmd_blk); | 
 | 		else | 
 | 			myrb_handle_scsi(cb, cmd_blk, scmd); | 
 | 	} | 
 | 	spin_unlock_irqrestore(&cb->queue_lock, flags); | 
 | 	return IRQ_HANDLED; | 
 | } | 
 |  | 
 | static struct myrb_privdata DAC960_P_privdata = { | 
 | 	.hw_init =	DAC960_P_hw_init, | 
 | 	.irq_handler =	DAC960_P_intr_handler, | 
 | 	.mmio_size =	DAC960_PD_mmio_size, | 
 | }; | 
 |  | 
 | static struct myrb_hba *myrb_detect(struct pci_dev *pdev, | 
 | 		const struct pci_device_id *entry) | 
 | { | 
 | 	struct myrb_privdata *privdata = | 
 | 		(struct myrb_privdata *)entry->driver_data; | 
 | 	irq_handler_t irq_handler = privdata->irq_handler; | 
 | 	unsigned int mmio_size = privdata->mmio_size; | 
 | 	struct Scsi_Host *shost; | 
 | 	struct myrb_hba *cb = NULL; | 
 |  | 
 | 	shost = scsi_host_alloc(&myrb_template, sizeof(struct myrb_hba)); | 
 | 	if (!shost) { | 
 | 		dev_err(&pdev->dev, "Unable to allocate Controller\n"); | 
 | 		return NULL; | 
 | 	} | 
 | 	shost->max_cmd_len = 12; | 
 | 	shost->max_lun = 256; | 
 | 	cb = shost_priv(shost); | 
 | 	mutex_init(&cb->dcmd_mutex); | 
 | 	mutex_init(&cb->dma_mutex); | 
 | 	cb->pdev = pdev; | 
 | 	cb->host = shost; | 
 |  | 
 | 	if (pci_enable_device(pdev)) { | 
 | 		dev_err(&pdev->dev, "Failed to enable PCI device\n"); | 
 | 		scsi_host_put(shost); | 
 | 		return NULL; | 
 | 	} | 
 |  | 
 | 	if (privdata->hw_init == DAC960_PD_hw_init || | 
 | 	    privdata->hw_init == DAC960_P_hw_init) { | 
 | 		cb->io_addr = pci_resource_start(pdev, 0); | 
 | 		cb->pci_addr = pci_resource_start(pdev, 1); | 
 | 	} else | 
 | 		cb->pci_addr = pci_resource_start(pdev, 0); | 
 |  | 
 | 	pci_set_drvdata(pdev, cb); | 
 | 	spin_lock_init(&cb->queue_lock); | 
 | 	if (mmio_size < PAGE_SIZE) | 
 | 		mmio_size = PAGE_SIZE; | 
 | 	cb->mmio_base = ioremap(cb->pci_addr & PAGE_MASK, mmio_size); | 
 | 	if (cb->mmio_base == NULL) { | 
 | 		dev_err(&pdev->dev, | 
 | 			"Unable to map Controller Register Window\n"); | 
 | 		goto failure; | 
 | 	} | 
 |  | 
 | 	cb->io_base = cb->mmio_base + (cb->pci_addr & ~PAGE_MASK); | 
 | 	if (privdata->hw_init(pdev, cb, cb->io_base)) | 
 | 		goto failure; | 
 |  | 
 | 	if (request_irq(pdev->irq, irq_handler, IRQF_SHARED, "myrb", cb) < 0) { | 
 | 		dev_err(&pdev->dev, | 
 | 			"Unable to acquire IRQ Channel %d\n", pdev->irq); | 
 | 		goto failure; | 
 | 	} | 
 | 	cb->irq = pdev->irq; | 
 | 	return cb; | 
 |  | 
 | failure: | 
 | 	dev_err(&pdev->dev, | 
 | 		"Failed to initialize Controller\n"); | 
 | 	myrb_cleanup(cb); | 
 | 	return NULL; | 
 | } | 
 |  | 
 | static int myrb_probe(struct pci_dev *dev, const struct pci_device_id *entry) | 
 | { | 
 | 	struct myrb_hba *cb; | 
 | 	int ret; | 
 |  | 
 | 	cb = myrb_detect(dev, entry); | 
 | 	if (!cb) | 
 | 		return -ENODEV; | 
 |  | 
 | 	ret = myrb_get_hba_config(cb); | 
 | 	if (ret < 0) { | 
 | 		myrb_cleanup(cb); | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	if (!myrb_create_mempools(dev, cb)) { | 
 | 		ret = -ENOMEM; | 
 | 		goto failed; | 
 | 	} | 
 |  | 
 | 	ret = scsi_add_host(cb->host, &dev->dev); | 
 | 	if (ret) { | 
 | 		dev_err(&dev->dev, "scsi_add_host failed with %d\n", ret); | 
 | 		myrb_destroy_mempools(cb); | 
 | 		goto failed; | 
 | 	} | 
 | 	scsi_scan_host(cb->host); | 
 | 	return 0; | 
 | failed: | 
 | 	myrb_cleanup(cb); | 
 | 	return ret; | 
 | } | 
 |  | 
 |  | 
 | static void myrb_remove(struct pci_dev *pdev) | 
 | { | 
 | 	struct myrb_hba *cb = pci_get_drvdata(pdev); | 
 |  | 
 | 	shost_printk(KERN_NOTICE, cb->host, "Flushing Cache..."); | 
 | 	myrb_exec_type3(cb, MYRB_CMD_FLUSH, 0); | 
 | 	myrb_cleanup(cb); | 
 | 	myrb_destroy_mempools(cb); | 
 | } | 
 |  | 
 |  | 
 | static const struct pci_device_id myrb_id_table[] = { | 
 | 	{ | 
 | 		PCI_DEVICE_SUB(PCI_VENDOR_ID_DEC, | 
 | 			       PCI_DEVICE_ID_DEC_21285, | 
 | 			       PCI_VENDOR_ID_MYLEX, | 
 | 			       PCI_DEVICE_ID_MYLEX_DAC960_LA), | 
 | 		.driver_data	= (unsigned long) &DAC960_LA_privdata, | 
 | 	}, | 
 | 	{ | 
 | 		PCI_DEVICE_DATA(MYLEX, DAC960_PG, &DAC960_PG_privdata), | 
 | 	}, | 
 | 	{ | 
 | 		PCI_DEVICE_DATA(MYLEX, DAC960_PD, &DAC960_PD_privdata), | 
 | 	}, | 
 | 	{ | 
 | 		PCI_DEVICE_DATA(MYLEX, DAC960_P, &DAC960_P_privdata), | 
 | 	}, | 
 | 	{0, }, | 
 | }; | 
 |  | 
 | MODULE_DEVICE_TABLE(pci, myrb_id_table); | 
 |  | 
 | static struct pci_driver myrb_pci_driver = { | 
 | 	.name		= "myrb", | 
 | 	.id_table	= myrb_id_table, | 
 | 	.probe		= myrb_probe, | 
 | 	.remove		= myrb_remove, | 
 | }; | 
 |  | 
 | static int __init myrb_init_module(void) | 
 | { | 
 | 	int ret; | 
 |  | 
 | 	myrb_raid_template = raid_class_attach(&myrb_raid_functions); | 
 | 	if (!myrb_raid_template) | 
 | 		return -ENODEV; | 
 |  | 
 | 	ret = pci_register_driver(&myrb_pci_driver); | 
 | 	if (ret) | 
 | 		raid_class_release(myrb_raid_template); | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | static void __exit myrb_cleanup_module(void) | 
 | { | 
 | 	pci_unregister_driver(&myrb_pci_driver); | 
 | 	raid_class_release(myrb_raid_template); | 
 | } | 
 |  | 
 | module_init(myrb_init_module); | 
 | module_exit(myrb_cleanup_module); | 
 |  | 
 | MODULE_DESCRIPTION("Mylex DAC960/AcceleRAID/eXtremeRAID driver (Block interface)"); | 
 | MODULE_AUTHOR("Hannes Reinecke <hare@suse.com>"); | 
 | MODULE_LICENSE("GPL"); |